From 87623e0fa99b45007a509797fcc7bad8920bf94a Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Wed, 7 Oct 2026 17:04:53 -0700 Subject: [PATCH 01/14] fix(chat): send entitlements and agent mode from the public chat API `/api/v2/chat` (what `sim chat` uses) stopped sending `entitlements` and `mode` in #8208, so CLI and API chats got no entitlement-gated tools and every CLI service refused them with "CLI services require agent mode". The route now computes entitlements per turn like the workspace chat and sends `mode: 'agent'`. Its test still mocked the old entitlements function and listed `mode` as a forbidden legacy field; both are updated. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EHGBgHrtePpi7KMrEfy41R --- apps/sim/app/api/v2/chat/route.test.ts | 5 +++-- apps/sim/app/api/v2/chat/route.ts | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/sim/app/api/v2/chat/route.test.ts b/apps/sim/app/api/v2/chat/route.test.ts index 66d3084f8f8..1112daa00d0 100644 --- a/apps/sim/app/api/v2/chat/route.test.ts +++ b/apps/sim/app/api/v2/chat/route.test.ts @@ -85,7 +85,7 @@ vi.mock('@/lib/mothership/chat/messages-store', () => mothershipChatMessagesMock vi.mock('@/lib/mothership/chat/payload', () => mothershipChatPayloadMock) vi.mock('@/lib/mothership/entitlements', () => ({ - computeWorkspaceEntitlements: vi.fn().mockResolvedValue([]), + computeEntitlements: vi.fn().mockResolvedValue([]), })) vi.mock('@/lib/mothership/request/lifecycle/headless', () => ({ @@ -418,8 +418,9 @@ describe('POST /api/v2/chat', () => { workspaceId: 'workspace-1', chatId: SERVER_ISSUED_CHAT_ID, integrationCatalog: { mcpServerIds: [] }, + mode: 'agent', }) - for (const legacy of ['messages', 'mode', 'isHosted', 'workspaceContext', 'userPermission']) { + for (const legacy of ['messages', 'isHosted', 'workspaceContext', 'userPermission']) { expect(payload).not.toHaveProperty(legacy) } expect(options).toMatchObject({ diff --git a/apps/sim/app/api/v2/chat/route.ts b/apps/sim/app/api/v2/chat/route.ts index 46b9beaa742..29b9297ba93 100644 --- a/apps/sim/app/api/v2/chat/route.ts +++ b/apps/sim/app/api/v2/chat/route.ts @@ -36,6 +36,7 @@ import { buildPersistedUserMessage, } from '@/lib/mothership/chat/persisted-message' import { MOTHERSHIP_CHAT_DEFAULT_MODEL } from '@/lib/mothership/constants' +import { computeEntitlements } from '@/lib/mothership/entitlements' import { type CopilotEnvironmentContext, createCopilotEnvironmentContext, @@ -343,6 +344,8 @@ export const POST = withRouteHandler( chatId, messageId, integrationCatalog: { mcpServerIds: [] }, + mode: 'agent', + entitlements: await computeEntitlements({ principal, workspaceId }), ...(effort ? { effort } : {}), } From c86b716724fb39e385322a7be8887330485307c0 Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Wed, 7 Oct 2026 17:05:20 -0700 Subject: [PATCH 02/14] feat(tests): workflow tests as a workspace resource Workflow tests are a workspace resource whose source is a plain vitest file, `tests/.test.js`, owned by the test (workspace_files.context = 'test'). Sim creates a test's metadata with the tests tool and writes its cases with the file tools; every write is collected in the sandbox and refused if the file does not load. - Runner: test files run in the isolated-vm sandbox against draft or deployed workflows. `runWorkflow` executes real runs; `mockBlock`, `mockTool` (Agent tool calls) and `spyOnBlock` reach blocks in the tested workflow and in every child workflow it runs, matched by name as each workflow starts. `.mockSampleOutput()` builds outputs shaped like the real block or tool. `toMatchRubric` asks a model judge for pass or fail. - Runs record live per-case progress, the source hash, and the deployment of every workflow they ran, so results show as out of date once the test or a workflow changes. - UI: Tests page and test page (Edit / Split / Preview over the file, the preview a dashboard of the selected run), a test resource type in chat, and a Tests sidebar entry behind the `workflow-tests` flag. - Owned files never open as file tabs in chat: only workspace files and chat uploads do. - Migration 0400 adds workflow_test and workflow_test_run. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EHGBgHrtePpi7KMrEfy41R --- .../api/workspaces/[id]/tests/[name]/route.ts | 23 + .../[id]/tests/[name]/runs/[runId]/route.ts | 23 + .../app/api/workspaces/[id]/tests/route.ts | 19 + .../api/workspaces/[id]/tests/run/route.ts | 19 + .../components/composer/composer.test.tsx | 3 + apps/sim/app/o/[organizationId]/layout.tsx | 35 +- .../components/file-viewer/file-viewer.tsx | 14 +- .../components/file-viewer/text-editor.tsx | 43 +- .../file-viewer/use-editable-file-content.ts | 6 + .../available-resources.ts | 19 + .../components/add-resource-dropdown/index.ts | 2 +- .../organization-resource-inventory.tsx | 6 +- .../resource-content/resource-content.tsx | 72 + .../resource-invalidation.ts | 5 + .../resource-registry/resource-registry.tsx | 11 + .../mothership-view/mothership-view.tsx | 4 +- .../user-input/components/constants.ts | 2 + .../plus-menu-dropdown/plus-menu-dropdown.tsx | 2 + .../app/workspace/[workspaceId]/layout.tsx | 4 + .../providers/feature-flags-provider.tsx | 1 + .../[workspaceId]/tests/[name]/loading.tsx | 14 + .../[workspaceId]/tests/[name]/page.tsx | 13 + .../[workspaceId]/tests/components/index.ts | 2 + .../tests/components/test-dashboard.tsx | 470 + .../tests/components/test-detail.tsx | 156 + .../tests/components/test-status-badge.tsx | 28 + .../tests/components/tests-feature-gate.tsx | 19 + .../components/workflow-version-preview.tsx | 50 + .../workspace/[workspaceId]/tests/layout.tsx | 10 + .../workspace/[workspaceId]/tests/loading.tsx | 8 + .../workspace/[workspaceId]/tests/page.tsx | 19 + .../[workspaceId]/tests/search-params.ts | 18 + .../workspace/[workspaceId]/tests/tests.tsx | 163 + .../w/components/sidebar/sidebar.tsx | 11 + apps/sim/executor/execution/block-executor.ts | 51 + apps/sim/executor/execution/executor.ts | 10 + .../sim/executor/execution/test-hooks.test.ts | 229 + apps/sim/executor/execution/types.ts | 50 + .../handlers/workflow/workflow-handler.ts | 2 + apps/sim/executor/types.ts | 3 + apps/sim/hooks/queries/workflow-tests.ts | 102 + .../contracts/mothership-management-tools.ts | 9 + .../sim/lib/api/contracts/mothership-tests.ts | 60 + apps/sim/lib/api/contracts/workflow-tests.ts | 119 + apps/sim/lib/api/contracts/workspace-files.ts | 2 +- apps/sim/lib/billing/usage-sources.ts | 4 +- apps/sim/lib/core/config/env.ts | 1 + apps/sim/lib/core/config/feature-flags.ts | 5 + apps/sim/lib/execution/isolated-vm-worker.cjs | 62 +- apps/sim/lib/execution/isolated-vm.ts | 2 + .../lib/execution/sandbox/bundles/build.ts | 60 + .../sandbox/bundles/event-polyfills.ts | 38 + .../execution/sandbox/bundles/verify.test.ts | 17 + .../sandbox/bundles/vitest-expect.cjs | 90 + apps/sim/lib/execution/sandbox/run-task.ts | 12 +- apps/sim/lib/execution/sandbox/types.ts | 9 +- apps/sim/lib/logs/get-trigger-options.ts | 1 + .../mothership/agent-cli/resource-effects.ts | 14 +- apps/sim/lib/mothership/agent-cli/services.ts | 3 +- .../execute-workflow-test-use-case.ts | 13 + apps/sim/lib/mothership/chat/post.ts | 1 + apps/sim/lib/mothership/entitlements.ts | 5 + .../sim/lib/mothership/generated/agent-cli.ts | 1 + apps/sim/lib/mothership/generated/protocol.ts | 2 +- .../sim/lib/mothership/generated/resources.ts | 1 + .../request/go/file-preview-adapter.ts | 31 +- .../lib/mothership/request/tools/resources.ts | 9 +- .../sim/lib/mothership/resources/file-tabs.ts | 25 + apps/sim/lib/mothership/resources/types.ts | 2 + .../tools/server/files/workspace-file.ts | 7 + .../sim/lib/mothership/tools/server/router.ts | 2 + apps/sim/lib/mothership/tools/server/tests.ts | 171 + apps/sim/lib/mothership/vfs/path-utils.ts | 6 +- apps/sim/lib/uploads/config.ts | 3 + .../workspace/workspace-file-manager.ts | 151 +- .../workspace-file-secret-provenance.ts | 2 +- apps/sim/lib/uploads/shared/types.ts | 1 + .../application/availability.ts | 28 + .../workflow-tests/application/operations.ts | 47 + .../workflow-tests/application/run-tests.ts | 139 + .../lib/workflow-tests/application/tests.ts | 383 + apps/sim/lib/workflow-tests/brokers.ts | 80 + apps/sim/lib/workflow-tests/collect.ts | 16 + apps/sim/lib/workflow-tests/definition.ts | 68 + apps/sim/lib/workflow-tests/feature-flag.ts | 13 + apps/sim/lib/workflow-tests/judge.ts | 104 + apps/sim/lib/workflow-tests/mock-channel.ts | 120 + apps/sim/lib/workflow-tests/paths.ts | 31 + apps/sim/lib/workflow-tests/protocol.ts | 131 + apps/sim/lib/workflow-tests/repository.ts | 328 + apps/sim/lib/workflow-tests/run-test-file.ts | 55 + .../lib/workflow-tests/session-registry.ts | 19 + apps/sim/lib/workflow-tests/session.ts | 505 + apps/sim/lib/workflow-tests/source-write.ts | 38 + .../application/run-workflow-for-test.ts | 103 + .../application/run-workflow-from-copilot.ts | 21 +- .../workflows/executor/execute-workflow.ts | 4 + .../lib/workflows/executor/execution-core.ts | 4 + apps/sim/lib/workflows/queries.ts | 25 + .../edit-workspace-file-content.ts | 17 +- .../export-workspace-file-snapshot.test.ts | 1 + .../export-workspace-file-snapshot.ts | 15 +- .../application/file-versions.ts | 47 +- .../application/owned-file-content.ts | 19 + .../read-workspace-file-artifact.ts | 6 +- ...read-workspace-file-content-by-key.test.ts | 6 +- .../read-workspace-file-content-by-key.ts | 17 +- .../read-workspace-file-content.test.ts | 2 + .../read-workspace-file-content.ts | 9 +- .../read-workspace-file-metadata.ts | 15 +- .../read-workspace-file-record.test.ts | 2 + .../application/read-workspace-file-record.ts | 16 +- .../read-workspace-file-secret-provenance.ts | 15 +- .../application/read-workspace-file-text.ts | 5 +- .../resolve-workspace-file-reference.test.ts | 20 +- .../resolve-workspace-file-reference.ts | 28 +- .../update-workspace-file-content.ts | 25 +- .../application/workspace-file-context.ts | 36 + apps/sim/lib/workspace-files/query-scope.ts | 6 + apps/sim/providers/runtime-context.ts | 56 + apps/sim/sandbox-tasks/index.ts | 2 + apps/sim/sandbox-tasks/registry.ts | 3 + apps/sim/sandbox-tasks/workflow-test.ts | 476 + apps/sim/stores/logs/filters/types.ts | 1 + .../db/migrations/0400_workflow_tests.sql | 48 + .../db/migrations/meta/0400_snapshot.json | 30555 ++++++++++++++++ packages/db/migrations/meta/_journal.json | 7 + packages/db/schema.ts | 92 + .../src/mocks/schema-tables.generated.ts | 34 + ...check-tool-registry-boundary.baseline.json | 175 +- scripts/check-unused-exports.baseline.json | 1 - 131 files changed, 36503 insertions(+), 198 deletions(-) create mode 100644 apps/sim/app/api/workspaces/[id]/tests/[name]/route.ts create mode 100644 apps/sim/app/api/workspaces/[id]/tests/[name]/runs/[runId]/route.ts create mode 100644 apps/sim/app/api/workspaces/[id]/tests/route.ts create mode 100644 apps/sim/app/api/workspaces/[id]/tests/run/route.ts create mode 100644 apps/sim/app/workspace/[workspaceId]/tests/[name]/loading.tsx create mode 100644 apps/sim/app/workspace/[workspaceId]/tests/[name]/page.tsx create mode 100644 apps/sim/app/workspace/[workspaceId]/tests/components/index.ts create mode 100644 apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx create mode 100644 apps/sim/app/workspace/[workspaceId]/tests/components/test-detail.tsx create mode 100644 apps/sim/app/workspace/[workspaceId]/tests/components/test-status-badge.tsx create mode 100644 apps/sim/app/workspace/[workspaceId]/tests/components/tests-feature-gate.tsx create mode 100644 apps/sim/app/workspace/[workspaceId]/tests/components/workflow-version-preview.tsx create mode 100644 apps/sim/app/workspace/[workspaceId]/tests/layout.tsx create mode 100644 apps/sim/app/workspace/[workspaceId]/tests/loading.tsx create mode 100644 apps/sim/app/workspace/[workspaceId]/tests/page.tsx create mode 100644 apps/sim/app/workspace/[workspaceId]/tests/search-params.ts create mode 100644 apps/sim/app/workspace/[workspaceId]/tests/tests.tsx create mode 100644 apps/sim/executor/execution/test-hooks.test.ts create mode 100644 apps/sim/hooks/queries/workflow-tests.ts create mode 100644 apps/sim/lib/api/contracts/mothership-tests.ts create mode 100644 apps/sim/lib/api/contracts/workflow-tests.ts create mode 100644 apps/sim/lib/execution/sandbox/bundles/event-polyfills.ts create mode 100644 apps/sim/lib/execution/sandbox/bundles/vitest-expect.cjs create mode 100644 apps/sim/lib/mothership/application/execute-workflow-test-use-case.ts create mode 100644 apps/sim/lib/mothership/resources/file-tabs.ts create mode 100644 apps/sim/lib/mothership/tools/server/tests.ts create mode 100644 apps/sim/lib/workflow-tests/application/availability.ts create mode 100644 apps/sim/lib/workflow-tests/application/operations.ts create mode 100644 apps/sim/lib/workflow-tests/application/run-tests.ts create mode 100644 apps/sim/lib/workflow-tests/application/tests.ts create mode 100644 apps/sim/lib/workflow-tests/brokers.ts create mode 100644 apps/sim/lib/workflow-tests/collect.ts create mode 100644 apps/sim/lib/workflow-tests/definition.ts create mode 100644 apps/sim/lib/workflow-tests/feature-flag.ts create mode 100644 apps/sim/lib/workflow-tests/judge.ts create mode 100644 apps/sim/lib/workflow-tests/mock-channel.ts create mode 100644 apps/sim/lib/workflow-tests/paths.ts create mode 100644 apps/sim/lib/workflow-tests/protocol.ts create mode 100644 apps/sim/lib/workflow-tests/repository.ts create mode 100644 apps/sim/lib/workflow-tests/run-test-file.ts create mode 100644 apps/sim/lib/workflow-tests/session-registry.ts create mode 100644 apps/sim/lib/workflow-tests/session.ts create mode 100644 apps/sim/lib/workflow-tests/source-write.ts create mode 100644 apps/sim/lib/workflows/application/run-workflow-for-test.ts create mode 100644 apps/sim/lib/workspace-files/application/owned-file-content.ts create mode 100644 apps/sim/sandbox-tasks/workflow-test.ts create mode 100644 packages/db/migrations/0400_workflow_tests.sql create mode 100644 packages/db/migrations/meta/0400_snapshot.json diff --git a/apps/sim/app/api/workspaces/[id]/tests/[name]/route.ts b/apps/sim/app/api/workspaces/[id]/tests/[name]/route.ts new file mode 100644 index 00000000000..75e5217aa41 --- /dev/null +++ b/apps/sim/app/api/workspaces/[id]/tests/[name]/route.ts @@ -0,0 +1,23 @@ +import { getWorkflowTestContract } from '@/lib/api/contracts/workflow-tests' +import { + defineInternalJsonRoute, + internalOrchestrationErrorPolicy, + internalRateLimits, + internalSessionAuth, +} from '@/lib/api/server/routes' +import { workflowTestOperations } from '@/lib/workflow-tests/application/operations' +import { getWorkflowTestDetail } from '@/lib/workflow-tests/application/tests' + +export const GET = defineInternalJsonRoute({ + contract: getWorkflowTestContract, + auth: internalSessionAuth, + operation: workflowTestOperations.read, + rateLimit: internalRateLimits.user({ bucketName: 'workflow-tests' }), + errorPolicy: internalOrchestrationErrorPolicy, + mapInput: ({ params, query }) => ({ + workspaceId: params.id, + name: params.name, + version: query.version, + }), + useCase: getWorkflowTestDetail, +}) diff --git a/apps/sim/app/api/workspaces/[id]/tests/[name]/runs/[runId]/route.ts b/apps/sim/app/api/workspaces/[id]/tests/[name]/runs/[runId]/route.ts new file mode 100644 index 00000000000..bf72a3df245 --- /dev/null +++ b/apps/sim/app/api/workspaces/[id]/tests/[name]/runs/[runId]/route.ts @@ -0,0 +1,23 @@ +import { getWorkflowTestRunContract } from '@/lib/api/contracts/workflow-tests' +import { + defineInternalJsonRoute, + internalOrchestrationErrorPolicy, + internalRateLimits, + internalSessionAuth, +} from '@/lib/api/server/routes' +import { workflowTestOperations } from '@/lib/workflow-tests/application/operations' +import { getWorkflowTestRunDetail } from '@/lib/workflow-tests/application/tests' + +export const GET = defineInternalJsonRoute({ + contract: getWorkflowTestRunContract, + auth: internalSessionAuth, + operation: workflowTestOperations.read, + rateLimit: internalRateLimits.user({ bucketName: 'workflow-tests' }), + errorPolicy: internalOrchestrationErrorPolicy, + mapInput: ({ params }) => ({ + workspaceId: params.id, + name: params.name, + runId: params.runId, + }), + useCase: getWorkflowTestRunDetail, +}) diff --git a/apps/sim/app/api/workspaces/[id]/tests/route.ts b/apps/sim/app/api/workspaces/[id]/tests/route.ts new file mode 100644 index 00000000000..0b4ff17fde7 --- /dev/null +++ b/apps/sim/app/api/workspaces/[id]/tests/route.ts @@ -0,0 +1,19 @@ +import { listWorkflowTestsContract } from '@/lib/api/contracts/workflow-tests' +import { + defineInternalJsonRoute, + internalOrchestrationErrorPolicy, + internalRateLimits, + internalSessionAuth, +} from '@/lib/api/server/routes' +import { workflowTestOperations } from '@/lib/workflow-tests/application/operations' +import { listWorkflowTests } from '@/lib/workflow-tests/application/tests' + +export const GET = defineInternalJsonRoute({ + contract: listWorkflowTestsContract, + auth: internalSessionAuth, + operation: workflowTestOperations.read, + rateLimit: internalRateLimits.user({ bucketName: 'workflow-tests' }), + errorPolicy: internalOrchestrationErrorPolicy, + mapInput: ({ params, query }) => ({ workspaceId: params.id, version: query.version }), + useCase: listWorkflowTests, +}) diff --git a/apps/sim/app/api/workspaces/[id]/tests/run/route.ts b/apps/sim/app/api/workspaces/[id]/tests/run/route.ts new file mode 100644 index 00000000000..b53971f293a --- /dev/null +++ b/apps/sim/app/api/workspaces/[id]/tests/run/route.ts @@ -0,0 +1,19 @@ +import { runWorkflowTestsContract } from '@/lib/api/contracts/workflow-tests' +import { + defineInternalJsonRoute, + internalOrchestrationErrorPolicy, + internalRateLimits, + internalSessionAuth, +} from '@/lib/api/server/routes' +import { workflowTestOperations } from '@/lib/workflow-tests/application/operations' +import { startWorkflowTestRuns } from '@/lib/workflow-tests/application/run-tests' + +export const POST = defineInternalJsonRoute({ + contract: runWorkflowTestsContract, + auth: internalSessionAuth, + operation: workflowTestOperations.run, + rateLimit: internalRateLimits.user({ bucketName: 'workflow-tests' }), + errorPolicy: internalOrchestrationErrorPolicy, + mapInput: ({ params, body }) => ({ workspaceId: params.id, ...body }), + useCase: startWorkflowTestRuns, +}) diff --git a/apps/sim/app/o/[organizationId]/home/components/composer/composer.test.tsx b/apps/sim/app/o/[organizationId]/home/components/composer/composer.test.tsx index aa1466b9499..4a3616e56db 100644 --- a/apps/sim/app/o/[organizationId]/home/components/composer/composer.test.tsx +++ b/apps/sim/app/o/[organizationId]/home/components/composer/composer.test.tsx @@ -232,6 +232,7 @@ async function render( { Promise ) => void + /** Receives why a save was refused; text files only. */ + onSaveError?: (error: unknown) => void + /** Replaces the file's own preview pane, for an owner with a richer view of the file. */ + preview?: ReactNode + /** Which side of a split the source takes; `end` puts it after the preview. */ + sourceSide?: 'start' | 'end' saveRef?: React.MutableRefObject<(() => Promise) | null> downloadSourceRef?: React.MutableRefObject discardRef?: React.MutableRefObject<(() => void) | null> @@ -164,6 +170,9 @@ function FileViewerContent({ autoFocus, onDirtyChange, onSaveStatusChange, + onSaveError, + preview, + sourceSide, saveRef, downloadSourceRef, discardRef, @@ -244,6 +253,9 @@ function FileViewerContent({ autoFocus={autoFocus} onDirtyChange={onDirtyChange} onSaveStatusChange={onSaveStatusChange} + onSaveError={onSaveError} + preview={preview} + sourceSide={sourceSide} saveRef={saveRef} discardRef={discardRef} streamingContent={streamingContent} diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/text-editor.tsx b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/text-editor.tsx index 5411c931030..e8c8d93ef6b 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/text-editor.tsx +++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/text-editor.tsx @@ -4,6 +4,7 @@ import { memo, type ClipboardEvent as ReactClipboardEvent, type KeyboardEvent as ReactKeyboardEvent, + type ReactNode, useCallback, useEffect, useId, @@ -405,6 +406,9 @@ interface TextEditorProps { status: 'idle' | 'saving' | 'saved' | 'error', retry?: () => Promise ) => void + onSaveError?: (error: unknown) => void + preview?: ReactNode + sourceSide?: 'start' | 'end' saveRef?: React.MutableRefObject<(() => Promise) | null> downloadSourceRef?: React.MutableRefObject discardRef?: React.MutableRefObject<(() => void) | null> @@ -422,6 +426,9 @@ export const TextEditor = memo(function TextEditor({ autoFocus, onDirtyChange, onSaveStatusChange, + onSaveError, + preview, + sourceSide = 'start', saveRef, downloadSourceRef, discardRef, @@ -502,6 +509,7 @@ export const TextEditor = memo(function TextEditor({ isAgentEditing, onDirtyChange, onSaveStatusChange, + onSaveError, saveRef, discardRef, }) @@ -607,7 +615,8 @@ export const TextEditor = memo(function TextEditor({ if (!container) return const rect = container.getBoundingClientRect() const isRtl = getComputedStyle(container).direction === 'rtl' - const sourceWidth = isRtl ? rect.right - e.clientX : e.clientX - rect.left + const fromRight = isRtl !== (sourceSide === 'end') + const sourceWidth = fromRight ? rect.right - e.clientX : e.clientX - rect.left const pct = (sourceWidth / rect.width) * 100 setSplitPct(Math.min(SPLIT_MAX_PCT, Math.max(SPLIT_MIN_PCT, pct))) } @@ -625,7 +634,7 @@ export const TextEditor = memo(function TextEditor({ document.body.style.cursor = '' document.body.style.userSelect = '' } - }, [isResizing]) + }, [isResizing, sourceSide]) const handleSplitKeyDown = (event: ReactKeyboardEvent) => { const key = readSeparatorKey(event) @@ -638,7 +647,8 @@ export const TextEditor = memo(function TextEditor({ } const container = containerRef.current const isRtl = container !== null && getComputedStyle(container).direction === 'rtl' - const delta = (key === 'left' ? -1 : 1) * (isRtl ? -1 : 1) * SPLIT_KEYBOARD_STEP_PCT + const fromRight = isRtl !== (sourceSide === 'end') + const delta = (key === 'left' ? -1 : 1) * (fromRight ? -1 : 1) * SPLIT_KEYBOARD_STEP_PCT setSplitPct((current) => Math.min(SPLIT_MAX_PCT, Math.max(SPLIT_MIN_PCT, current + delta))) } @@ -786,7 +796,10 @@ export const TextEditor = memo(function TextEditor({
{showEditor && ( @@ -844,16 +857,18 @@ export const TextEditor = memo(function TextEditor({ isResizing && 'pointer-events-none' )} > - + {preview ?? ( + + )}
)} diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/use-editable-file-content.ts b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/use-editable-file-content.ts index 7e57f6834c5..a3c13f5a39b 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/use-editable-file-content.ts +++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/use-editable-file-content.ts @@ -54,6 +54,8 @@ interface UseEditableFileContentOptions { onDirtyChange?: (isDirty: boolean) => void /** `retry` is this instance's own `saveImmediately`, passed alongside an `'error'` status so a caller-side retry never depends on a shared, remount-able ref. */ onSaveStatusChange?: (status: SaveStatus, retry?: () => Promise) => void + /** Receives why a save was refused, for callers whose server checks the content itself. */ + onSaveError?: (error: unknown) => void saveRef?: React.MutableRefObject<(() => Promise) | null> /** Bridges an imperative "discard the current draft" command up to the caller, mirroring `saveRef`. */ discardRef?: React.MutableRefObject<(() => void) | null> @@ -164,6 +166,7 @@ export function useEditableFileContent({ isAgentEditing, onDirtyChange, onSaveStatusChange, + onSaveError, saveRef, discardRef, normalizeBaseline, @@ -171,6 +174,7 @@ export function useEditableFileContent({ }: UseEditableFileContentOptions): EditableFileContent { const onDirtyChangeRef = useRef(onDirtyChange) const onSaveStatusChangeRef = useRef(onSaveStatusChange) + const onSaveErrorRef = useRef(onSaveError) /** * Mirrors the reducer's `reconciling` phase (assigned below the reducer hook; read here through a @@ -292,6 +296,7 @@ export function useEditableFileContent({ everStreamedRef.current = everStreamed onDirtyChangeRef.current = onDirtyChange onSaveStatusChangeRef.current = onSaveStatusChange + onSaveErrorRef.current = onSaveError updateContentRef.current = updateContent streamActiveRef.current = isAgentStreamActive contentRef.current = content @@ -339,6 +344,7 @@ export function useEditableFileContent({ conflictRef.current = true markConflict() } + onSaveErrorRef.current?.(error) throw error } }, diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/available-resources.ts b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/available-resources.ts index 70bf34e89cc..404f50cf8c5 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/available-resources.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/available-resources.ts @@ -17,6 +17,7 @@ import { useKnowledgeBasesQuery } from '@/hooks/queries/kb/knowledge' import { useLogsList } from '@/hooks/queries/logs' import { useMothershipChats } from '@/hooks/queries/mothership-chats' import { useTablesList } from '@/hooks/queries/tables' +import { useWorkflowTests } from '@/hooks/queries/workflow-tests' import { useWorkflows } from '@/hooks/queries/workflows' import { useWorkspaceFileFolders } from '@/hooks/queries/workspace-file-folders' import { useWorkspaceFiles } from '@/hooks/queries/workspace-files' @@ -30,6 +31,12 @@ export const BROWSER_LAUNCHER_ID = 'browser' /** Placeholder id for the Terminal launcher row; the shell the desktop app opens becomes the tab. */ export const TERMINAL_LAUNCHER_ID = 'terminal' +/** + * Families the chat composer cannot attach: they open as resource tabs but have no chat + * context. Sim reads a test through its file, `tests/.test.js`. + */ +export const COMPOSER_EXCLUDED_TYPES: readonly MothershipResourceType[] = ['test'] + export interface AvailableItemsByType { type: MothershipResourceType items: AvailableItem[] @@ -97,6 +104,7 @@ export function useAvailableResources( options?: UseAvailableResourcesOptions ): AvailableResources { const dashboardsEnabled = useFeatureFlag('dashboards') + const testsEnabled = useFeatureFlag('workflow-tests') const enabled = options?.enabled ?? true const excludeTypes = options?.excludeTypes const browserAvailable = useSyncExternalStore( @@ -121,6 +129,9 @@ export function useAvailableResources( const { data: dashboardData, isPending: dashboardsPending } = useWorkspaceDashboard(workspaceId, { enabled: enabled && dashboardsEnabled && !excludeTypes?.includes('dashboard'), }) + const { data: tests, isPending: testsPending } = useWorkflowTests(workspaceId, { + enabled: enabled && testsEnabled && !excludeTypes?.includes('test'), + }) const { data: files, isPending: filesPending } = useWorkspaceFiles(workspaceId, 'active', { enabled: enabled && Boolean(workspaceId), }) @@ -172,6 +183,7 @@ export function useAvailableResources( tablesPending || filesPending || (dashboardsEnabled && !excludeTypes?.includes('dashboard') && dashboardsPending) || + (testsEnabled && !excludeTypes?.includes('test') && testsPending) || knowledgeBasesPending || foldersPending || (options?.includeFolderMentions && @@ -185,6 +197,7 @@ export function useAvailableResources( if (!enabled) return NO_RESOURCE_GROUPS const excluded = new Set(excludeTypes ?? []) if (!dashboardsEnabled) excluded.add('dashboard') + if (!testsEnabled) excluded.add('test') const groups: AvailableItemsByType[] = [ { type: 'workflow' as const, @@ -218,6 +231,10 @@ export function useAvailableResources( ? [{ id: dashboardData.dashboard.id, name: dashboardData.dashboard.name, folderId: null }] : [], }, + { + type: 'test' as const, + items: (tests ?? []).map((test) => ({ id: test.name, name: test.title, folderId: null })), + }, { type: 'file' as const, items: (files ?? []).map((f) => ({ id: f.id, name: f.name, folderId: f.folderId ?? null })), @@ -311,11 +328,13 @@ export function useAvailableResources( tables, files, dashboardData, + tests, knowledgeBases, tasks, logs, excludeTypes, dashboardsEnabled, + testsEnabled, ]) /** diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/index.ts b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/index.ts index 0e9f610be57..5534c34dd3f 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/index.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/index.ts @@ -6,5 +6,5 @@ export { useResourceTreeSections, WorkspaceResourceSubmenu, } from './add-resource-dropdown' -export { useAvailableResources } from './available-resources' +export { COMPOSER_EXCLUDED_TYPES, useAvailableResources } from './available-resources' export { resourceFromItem } from './resource-from-item' diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/organization-resource-inventory.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/organization-resource-inventory.tsx index 1dbf07425e5..8973edf0fcf 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/organization-resource-inventory.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/organization-resource-inventory.tsx @@ -1,6 +1,7 @@ import { useEffect } from 'react' import { type AvailableResources, + COMPOSER_EXCLUDED_TYPES, useAvailableResources, } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/available-resources' @@ -12,7 +13,10 @@ export function OrganizationResourceInventory({ workspaceId: string onChange: (workspaceId: string, inventory: AvailableResources) => void }) { - const inventory = useAvailableResources(workspaceId, { includeFolderMentions: true }) + const inventory = useAvailableResources(workspaceId, { + includeFolderMentions: true, + excludeTypes: COMPOSER_EXCLUDED_TYPES, + }) useEffect(() => { onChange(workspaceId, inventory) }, [workspaceId, inventory, onChange]) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx index 2329d717c9a..7099708d0bc 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx @@ -55,6 +55,11 @@ import { useWorkspacePermissionsContext, } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' import { Table } from '@/app/workspace/[workspaceId]/tables/[tableId]/table' +import { + TestsFeatureGate, + TestView, + useTestRunAction, +} from '@/app/workspace/[workspaceId]/tests/components' import { useUsageLimits } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/hooks' import { useWorkflowExecution } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution' import { useFolders } from '@/hooks/queries/folders' @@ -63,6 +68,7 @@ import { exportTable } from '@/hooks/queries/tables' import { fetchWorkflowEnvelope } from '@/hooks/queries/utils/fetch-workflow-envelope' import { workflowKeys } from '@/hooks/queries/utils/workflow-keys' import { mapWorkflow } from '@/hooks/queries/utils/workflow-list-query' +import { useWorkflowTest } from '@/hooks/queries/workflow-tests' import { useWorkflows, WORKFLOW_STATE_STALE_TIME } from '@/hooks/queries/workflows' import { useAddressedWorkspaceFileRecord, useWorkspaceFiles } from '@/hooks/queries/workspace-files' import { createWorkspaceFileContentSource } from '@/hooks/use-file-content-source' @@ -296,6 +302,19 @@ export const ResourceContent = memo(function ResourceContent({ case 'dashboard': return + case 'test': + return ( + +
+ +
+
+ ) case 'file': return ( + case 'test': + return case 'table': return case 'log': @@ -543,6 +564,57 @@ function EmbeddedDashboardActions({ workspaceId }: EmbeddedDashboardActionsProps ) } +interface EmbeddedTestActionsProps { + workspaceId: string + name: string +} + +/** Runs the test the tab shows, and opens its page. */ +function EmbeddedTestActions({ workspaceId, name }: EmbeddedTestActionsProps) { + const router = useRouter() + const testsEnabled = useFeatureFlag('workflow-tests') + const detail = useWorkflowTest(workspaceId, name) + const run = useTestRunAction({ + workspaceId, + name, + running: detail.data?.latestRun?.status === 'running', + }) + if (!testsEnabled) return null + return ( + <> + + + + + + + +

{run.text}

+
+
+ + + router.push(`/workspace/${workspaceId}/tests/${name}`)} + aria-label='Open test' + > + + + + +

Open test

+
+
+ + ) +} + interface EmbeddedKnowledgeBaseActionsProps { workspaceId: string knowledgeBaseId: string diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-invalidation.ts b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-invalidation.ts index 6962cdab5f8..66fdac714ee 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-invalidation.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-invalidation.ts @@ -9,6 +9,7 @@ import { invalidateWorkflowLists } from '@/hooks/queries/utils/invalidate-workfl import { knowledgeKeys } from '@/hooks/queries/utils/knowledge-keys' import { tableKeys } from '@/hooks/queries/utils/table-keys' import { workflowKeys } from '@/hooks/queries/utils/workflow-keys' +import { workflowTestKeys } from '@/hooks/queries/workflow-tests' import { workspaceFileFolderKeys } from '@/hooks/queries/workspace-file-folders' import { workspaceFilesKeys } from '@/hooks/queries/workspace-files' @@ -28,6 +29,10 @@ const RESOURCE_INVALIDATORS: Record< invalidate(qc, id ? tableKeys.views(id) : tableKeys.viewsRoot()) }, dashboard: (qc, wId) => invalidate(qc, dashboardKeys.workspace(wId)), + test: (qc) => { + invalidate(qc, workflowTestKeys.lists()) + invalidate(qc, workflowTestKeys.details()) + }, file: (qc, wId, id) => { invalidate(qc, workspaceFilesKeys.lists()) invalidate(qc, id ? workspaceFilesKeys.record(wId, id) : workspaceFilesKeys.records()) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-registry.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-registry.tsx index 1a9de189f0d..ae635b51111 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-registry.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-registry.tsx @@ -11,6 +11,7 @@ import { Globe, Library, Search, + ShieldCheck, Table as TableIcon, Task, TerminalWindow, @@ -193,6 +194,15 @@ export const RESOURCE_REGISTRY: Record , }, + test: { + type: 'test', + label: 'Tests', + icon: ShieldCheck, + renderTabIcon: (_resource, className) => ( + + ), + renderDropdownItem: (props) => , + }, file: { type: 'file', label: 'Files', @@ -299,6 +309,7 @@ export const RESOURCE_MENU_ORDER: readonly MothershipResourceType[] = [ 'integration', 'task', 'dashboard', + 'test', 'table', 'file', 'filefolder', diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx index 9fb538d0330..c5c61207c10 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx @@ -203,7 +203,7 @@ export const MothershipView = memo( const activeFile = active?.type === 'file' ? files?.find((f) => f.id === active.id) : undefined const isActiveCsv = active?.type === 'file' && getFileExtension(active.title) === 'csv' - const isActivePreviewable = + const isActiveFilePreviewable = canEdit && active?.type === 'file' && // Dashboards store extensionless names, so the record's type decides once it loads. @@ -221,6 +221,8 @@ export const MothershipView = memo( // A Sim page is locked to its rendered view (the pdf model — the raw // source is not a mode this surface offers), so no toggle either. activeFile?.type !== SIM_PAGE_CONTENT_TYPE + // A test shows its source, its results, or both, like an HTML file. + const isActivePreviewable = isActiveFilePreviewable || (canEdit && active?.type === 'test') return ( ({ kind: 'dashboard', dashboardId: r.id, label: r.title }), + /** Sim reads a test through its file, `tests/.test.js`, not a chat context. */ + test: () => null, file: (r) => ({ kind: 'file', fileId: r.id, label: r.title }), folder: (r) => ({ kind: 'folder', folderId: r.id, label: r.title }), filefolder: (r) => ({ kind: 'filefolder', fileFolderId: r.id, label: r.title }), diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/plus-menu-dropdown/plus-menu-dropdown.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/plus-menu-dropdown/plus-menu-dropdown.tsx index 3759f6e3d81..5227b97da5b 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/plus-menu-dropdown/plus-menu-dropdown.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/plus-menu-dropdown/plus-menu-dropdown.tsx @@ -15,6 +15,7 @@ import { import { IdentityTile } from '@/components/identity-tile/identity-tile' import { getWorkspaceInitial } from '@/lib/workspaces/initials' import { + COMPOSER_EXCLUDED_TYPES, ResourceMenuSections, resourceFromItem, useAvailableResources, @@ -154,6 +155,7 @@ export const PlusMenuDropdown = React.memo( const workspaceInventory = useAvailableResources(organizationId ? '' : workspaceId, { enabled: inventoryEnabled, includeFolderMentions: true, + excludeTypes: COMPOSER_EXCLUDED_TYPES, }) const { data: allWorkspaces, isPending: workspacesPending } = useOrderedWorkspacesQuery( Boolean(organizationId) && inventoryEnabled diff --git a/apps/sim/app/workspace/[workspaceId]/layout.tsx b/apps/sim/app/workspace/[workspaceId]/layout.tsx index d9fcd6f9a9e..bc627d5b735 100644 --- a/apps/sim/app/workspace/[workspaceId]/layout.tsx +++ b/apps/sim/app/workspace/[workspaceId]/layout.tsx @@ -8,6 +8,7 @@ import { isDashboardsEnabled } from '@/lib/dashboards/feature-flag' import { isMothershipModelSelectorEnabled, isPlanModeEnabled } from '@/lib/mothership/feature-flags' import { resolveOrganizationEntryPath } from '@/lib/navigation/resolve-app-entry' import { isTableRowTtlEnabled } from '@/lib/table/ttl-availability' +import { isWorkflowTestsEnabled } from '@/lib/workflow-tests/feature-flag' import { getQueryClient } from '@/app/_shell/providers/get-query-client' import { ImpersonationBanner } from '@/app/workspace/[workspaceId]/components/impersonation-banner' import { SessionExpired } from '@/app/workspace/[workspaceId]/components/session-expired' @@ -67,6 +68,7 @@ export default async function WorkspaceLayout({ planModeEnabled, organizationHref, dashboardsEnabled, + workflowTestsEnabled, ] = await Promise.all([ cookies(), hostContext.hostOrganizationId @@ -84,6 +86,7 @@ export default async function WorkspaceLayout({ isPlanModeEnabled(), resolveOrganizationEntryPath(session), isDashboardsEnabled(hostContext.hostOrganizationId), + isWorkflowTestsEnabled(hostContext.hostOrganizationId), prefetchWorkspaceAccess(queryClient, workspaceId, principal), prefetchWorkspaceForkAvailability(queryClient, workspaceId, principal, hostContext), ]) @@ -94,6 +97,7 @@ export default async function WorkspaceLayout({ +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/[name]/page.tsx b/apps/sim/app/workspace/[workspaceId]/tests/[name]/page.tsx new file mode 100644 index 00000000000..c00a9648606 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/[name]/page.tsx @@ -0,0 +1,13 @@ +import type { Metadata } from 'next' +import { TestDetail } from '@/app/workspace/[workspaceId]/tests/components' + +export const metadata: Metadata = { title: 'Test', robots: { index: false } } + +interface TestPageProps { + params: Promise<{ workspaceId: string; name: string }> +} + +export default async function TestPage({ params }: TestPageProps) { + const { workspaceId, name } = await params + return +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/index.ts b/apps/sim/app/workspace/[workspaceId]/tests/components/index.ts new file mode 100644 index 00000000000..e6e032c0ff0 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/index.ts @@ -0,0 +1,2 @@ +export { TestDetail, TestView, useTestRunAction } from './test-detail' +export { TestsFeatureGate } from './tests-feature-gate' diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx b/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx new file mode 100644 index 00000000000..08379776865 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx @@ -0,0 +1,470 @@ +'use client' + +import { lazy, type ReactNode, Suspense, useEffect, useState } from 'react' +import { + Badge, + Chip, + cn, + DashboardMetric, + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@sim/emcn' +import { CircleCheck, CircleX, Loader, Minus } from '@sim/emcn/icons' +import { formatDuration, formatRelativeTime } from '@sim/utils/formatting' +import type { WorkflowTestDetail } from '@/lib/api/contracts/workflow-tests' +import { SettingsSection } from '@/app/workspace/[workspaceId]/settings/components/settings-section/settings-section' +import { useWorkflowTestRun } from '@/hooks/queries/workflow-tests' + +const WorkflowVersionPreview = lazy(() => + import('@/app/workspace/[workspaceId]/tests/components/workflow-version-preview').then( + (module) => ({ default: module.WorkflowVersionPreview }) + ) +) + +type RunDetail = NonNullable +type Run = WorkflowTestDetail['history'][number] +type CaseResult = NonNullable['tests'][number] +type CaseState = 'queued' | 'running' | 'pass' | 'fail' | 'skip' | 'none' + +const RUNS_SHOWN = 5 + +const RUN_BADGE: Record = { + passed: 'green', + failed: 'red', + error: 'red', + running: 'amber', +} + +const CASE_STATE: Record = { + queued: { + label: 'Queued', + icon: , + }, + running: { + label: 'Running', + icon: , + }, + pass: { + label: 'Passed', + icon: , + }, + fail: { + label: 'Failed', + icon: , + }, + skip: { + label: 'Skipped', + icon: , + }, + none: { + label: 'Not run', + icon: , + }, +} + +interface TestDashboardProps { + workspaceId: string + name: string + detail: WorkflowTestDetail +} + +/** + * How the test is doing: totals and freshness for one run (the latest unless a row in Runs is + * chosen), what failed in it, and every recent run. A latest run still going shows live. + */ +export function TestDashboard({ workspaceId, name, detail }: TestDashboardProps) { + const [selectedRunId, setSelectedRunId] = useState(null) + const latestRunning = detail.latestRun?.status === 'running' + const viewedRunId = latestRunning ? null : selectedRunId + const selected = useWorkflowTestRun(workspaceId, name, viewedRunId) + const run = viewedRunId ? (selected.data ?? null) : detail.latestRun + const running = run?.status === 'running' + + const progress = run?.progress ?? {} + const results = new Map() + for (const result of run?.report?.tests ?? []) results.set(result.path.join(' > '), result) + /** An earlier run lists the cases it ran; otherwise the file's current cases, as they stand. */ + const casePaths = viewedRunId + ? (run?.report?.tests ?? []).map((result) => result.path) + : detail.test.cases.map((testCase) => testCase.path) + const cases = casePaths.map((path) => { + const key = path.join(' > ') + const result = results.get(key) + const state: CaseState = running ? (progress[key] ?? 'queued') : (result?.status ?? 'none') + return { key, name: caseName(path), state, result } + }) + const finished = cases.filter((c) => c.state !== 'queued' && c.state !== 'running').length + const hasResults = running || Boolean(run?.report) + const count = (state: CaseState) => cases.filter((testCase) => testCase.state === state).length + const durationMs = run?.report?.tests.reduce((total, result) => total + result.durationMs, 0) + + return ( +
+
+
+
+ + + {running && run ? ( + + ) : ( + + )} +
+ {running && ( +
+
+
+ )} + +
+ + + {cases.length === 0 ? ( +

No tests yet

+ ) : ( + + + + Test + Status + Duration + + + + {cases.map((testCase) => ( + + + {testCase.name} + {testCase.state === 'fail' && testCase.result && ( +

+ {failureReason(testCase.result)} +

+ )} +
+ + + {CASE_STATE[testCase.state].icon} + {CASE_STATE[testCase.state].label} + + + + {testCase.result ? formatDuration(testCase.result.durationMs) : '—'} + +
+ ))} +
+
+ )} +
+ + {detail.history.length > 0 && ( + setSelectedRunId(runId === detail.latestRun?.id ? null : runId)} + /> + )} + + {!running && run && run.ranAgainst.length > 0 && ( + + )} +
+
+ ) +} + +interface FreshnessProps { + run: RunDetail | null + loading: boolean +} + +/** Says what is off about the run on screen; a finished, current run needs no line. */ +function Freshness({ run, loading }: FreshnessProps) { + const line = loading + ? { + lead: , + text: 'Loading run…', + } + : freshnessLine(run) + if (!line) return null + return ( +

+ {line.lead} + {line.text} +

+ ) +} + +function freshnessLine(run: RunDetail | null): { lead: ReactNode; text: string } | null { + if (!run) return { lead: null, text: 'Not run yet' } + if (run.status === 'running') { + return { + lead: , + text: 'Running against the latest workflows…', + } + } + const when = formatRelativeTime(run.completedAt ?? run.startedAt) + if (run.status === 'error') { + return { + lead: ( + + Didn’t finish + + ), + text: `${when}: ${run.error ?? 'the run failed'}`, + } + } + if (!run.current) { + const changed = run.ranAgainst + .filter((workflow) => workflow.stale) + .map((workflow) => workflow.name ?? 'A deleted workflow') + return { + lead: ( + + Out of date + + ), + text: + changed.length > 0 + ? `${changed.join(', ')} changed after this run ${when}` + : `The test changed after this run ${when}`, + } + } + return null +} + +interface WorkflowsSectionProps { + workflows: RunDetail['ranAgainst'] +} + +/** + * Each workflow the run executed and the version it ran, marked when that version is no longer + * live. A deployed version opens on a read-only canvas. + */ +function WorkflowsSection({ workflows }: WorkflowsSectionProps) { + const [previewing, setPreviewing] = useState<{ + workflowId: string + version: number + title: string + } | null>(null) + return ( + + + + {workflows.map((workflow) => { + const label = `${workflow.name ?? 'Deleted workflow'}${ + workflow.draft ? ' · Draft' : workflow.version ? ` · v${workflow.version}` : '' + }` + const version = workflow.version + const preview = + workflow.name !== null && !workflow.draft && version !== null + ? () => setPreviewing({ workflowId: workflow.workflowId, version, title: label }) + : undefined + return ( + { + if (preview && (event.key === 'Enter' || event.key === ' ')) { + event.preventDefault() + preview() + } + }} + className={cn( + preview && 'cursor-pointer transition-colors hover:bg-[var(--surface-active)]' + )} + > + {label} + + {workflow.stale && ( + + Out of date + + )} + + + ) + })} + +
+ {previewing && ( + + setPreviewing(null)} /> + + )} +
+ ) +} + +interface RunsSectionProps { + runs: Run[] + latestRunId: string | null + selectedRunId: string | null + onSelect: (runId: string) => void +} + +function RunsSection({ runs, latestRunId, selectedRunId, onSelect }: RunsSectionProps) { + const [showAll, setShowAll] = useState(false) + const shown = showAll ? runs : runs.slice(0, RUNS_SHOWN) + return ( + onSelect(latestRunId)}>View latest + ) : null + } + > + + + {shown.map((run) => ( + onSelect(run.id)} + /> + ))} + +
+ {runs.length > RUNS_SHOWN && ( + setShowAll((value) => !value)}> + {showAll ? 'Show fewer' : `Show all ${runs.length}`} + + )} +
+ ) +} + +interface RunRowProps { + run: Run + latest: boolean + selected: boolean + onSelect: () => void +} + +/** One run: how it went, how long it took, and when. Opens its results on the page. */ +function RunRow({ run, latest, selected, onSelect }: RunRowProps) { + const result = + run.status === 'passed' || run.status === 'failed' + ? `${run.passed} of ${run.passed + run.failed} passed` + : run.status === 'error' + ? 'Didn’t finish' + : 'Running' + const durationMs = run.completedAt + ? Date.parse(run.completedAt) - Date.parse(run.startedAt) + : null + return ( + { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault() + onSelect() + } + }} + className={cn( + 'cursor-pointer transition-colors hover:bg-[var(--surface-active)]', + selected && 'bg-[var(--surface-active)]', + !run.current && run.status !== 'running' && 'text-[var(--text-muted)]' + )} + > + + + + {result} + + {latest && ( + + Latest + + )} + {run.version === 'draft' && ( + + Draft + + )} + + + + {durationMs === null ? '—' : formatDuration(durationMs)} + + + {formatRelativeTime(run.completedAt ?? run.startedAt)} + + + ) +} + +/** A case's name under its file's one top-level describe. */ +function caseName(path: string[]): string { + return path.slice(1).join(' › ') +} + +/** Why a case failed, in the words a reader can act on: the judge's reason, else the message. */ +function failureReason(result: CaseResult): string { + const check = result.checks.find((candidate) => candidate.status === 'fail') + return check?.judge ?? check?.message ?? result.error?.message ?? 'Failed' +} + +interface ElapsedMetricProps { + startedAt: string +} + +/** The running clock: time since the run started, ticking while it goes. */ +function ElapsedMetric({ startedAt }: ElapsedMetricProps) { + const [now, setNow] = useState(() => Date.now()) + useEffect(() => { + const timer = setInterval(() => setNow(Date.now()), 250) + return () => clearInterval(timer) + }, []) + return ( + + ) +} + +interface MetricProps { + label: string + value: string | number +} + +function Metric({ label, value }: MetricProps) { + return ( +
+ +
+ ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/test-detail.tsx b/apps/sim/app/workspace/[workspaceId]/tests/components/test-detail.tsx new file mode 100644 index 00000000000..c7f0ab6edad --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/test-detail.tsx @@ -0,0 +1,156 @@ +'use client' + +import { useState } from 'react' +import { toast } from '@sim/emcn' +import { Columns2, Eye, Pencil, PlayOutline, ShieldCheck } from '@sim/emcn/icons' +import { getErrorMessage } from '@sim/utils/errors' +import { useRouter } from 'next/navigation' +import type { WorkflowTestDetail } from '@/lib/api/contracts/workflow-tests' +import { Resource } from '@/app/workspace/[workspaceId]/components/resource/resource' +import { + FileViewer, + type PreviewMode, +} from '@/app/workspace/[workspaceId]/files/components/file-viewer' +import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' +import { TestDashboard } from '@/app/workspace/[workspaceId]/tests/components/test-dashboard' +import { + useRefreshWorkflowTest, + useRunWorkflowTests, + useWorkflowTest, +} from '@/hooks/queries/workflow-tests' +import { useAddressedWorkspaceFileRecord } from '@/hooks/queries/workspace-files' + +interface TestTarget { + workspaceId: string + name: string +} + +/** Tests run here against the deployed workflows; Sim runs drafts while it edits them. */ +const VERSION = 'deployed' as const + +const NEXT_PREVIEW_MODE: Record = { + editor: 'split', + split: 'preview', + preview: 'editor', +} + +/** The test's page: the shared view under a header that switches its mode and runs it. */ +export function TestDetail({ workspaceId, name }: TestTarget) { + const router = useRouter() + const query = useWorkflowTest(workspaceId, name) + const run = useTestRunAction({ workspaceId, name, running: isRunning(query.data) }) + const [previewMode, setPreviewMode] = useState('preview') + + return ( + + router.push(`/workspace/${workspaceId}/tests`), + }, + { label: query.data?.test.title ?? name }, + ]} + actions={[ + { + text: previewMode === 'editor' ? 'Split' : previewMode === 'split' ? 'Preview' : 'Edit', + icon: previewMode === 'editor' ? Columns2 : previewMode === 'split' ? Eye : Pencil, + onSelect: () => setPreviewMode((mode) => NEXT_PREVIEW_MODE[mode]), + }, + run, + ]} + /> + + + ) +} + +interface TestRunActionInput extends TestTarget { + running: boolean +} + +/** The Run action for one test, shared by the page header and the chat tab. */ +export function useTestRunAction({ workspaceId, name, running }: TestRunActionInput) { + const canEdit = useUserPermissionsContext().canEdit === true + const runTests = useRunWorkflowTests(workspaceId) + const busy = running || runTests.isPending + return { + id: 'run', + icon: PlayOutline, + text: busy ? 'Running…' : 'Run', + disabled: !canEdit || busy, + onSelect: () => + runTests.mutate( + { version: VERSION, names: [name] }, + { onError: (error) => toast.error(error.message) } + ), + } +} + +function isRunning(detail: WorkflowTestDetail | undefined): boolean { + return detail?.latestRun?.status === 'running' +} + +interface TestViewProps extends TestTarget { + previewMode: PreviewMode +} + +/** + * A test file shown the way an HTML file is: its source, its results as the preview, or both + * side by side. The results are what the file declared at its last save. Shared by the test page + * and its resource tab in Chat. Every save is checked on the server; a refused save keeps the + * edit and says why above the editor. + */ +export function TestView({ workspaceId, name, previewMode }: TestViewProps) { + const canEdit = useUserPermissionsContext().canEdit === true + const query = useWorkflowTest(workspaceId, name) + const refreshTest = useRefreshWorkflowTest(workspaceId, name) + const [saveError, setSaveError] = useState(null) + const detail = query.data + const file = useAddressedWorkspaceFileRecord(workspaceId, detail?.test.fileId ?? '', { + enabled: Boolean(detail), + }) + + const error = query.error ?? file.error + if (error) { + return ( +

+ {error.message} +

+ ) + } + if (!detail || !file.data) return null + + return ( +
+ {saveError && previewMode !== 'preview' && ( +
+ Not saved: + {saveError} +
+ )} + } + sourceSide='end' + enableFind + onSaveError={(saveFailure) => + setSaveError(getErrorMessage(saveFailure, 'The test file was not saved')) + } + onSaveStatusChange={(status) => { + if (status !== 'saved') return + setSaveError(null) + refreshTest() + }} + /> +
+ ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/test-status-badge.tsx b/apps/sim/app/workspace/[workspaceId]/tests/components/test-status-badge.tsx new file mode 100644 index 00000000000..cb25f17d688 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/test-status-badge.tsx @@ -0,0 +1,28 @@ +import { Badge } from '@sim/emcn' +import type { WorkflowTestRecord } from '@/lib/api/contracts/workflow-tests' + +type BadgeVariant = 'green' | 'red' | 'amber' | 'gray' +type TestStatus = WorkflowTestRecord['status'] + +const TEST_STATUS: Record = { + passing: { label: 'Passed', variant: 'green' }, + failing: { label: 'Failed', variant: 'red' }, + error: { label: 'Didn’t run', variant: 'red' }, + running: { label: 'Running', variant: 'amber' }, + changed: { label: 'Changed', variant: 'gray' }, + not_run: { label: 'Not run', variant: 'gray' }, +} + +interface TestStatusBadgeProps { + status: TestStatus +} + +/** A test's latest outcome, styled like a log's status. */ +export function TestStatusBadge({ status }: TestStatusBadgeProps) { + const config = TEST_STATUS[status] + return ( + + {config.label} + + ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/tests-feature-gate.tsx b/apps/sim/app/workspace/[workspaceId]/tests/components/tests-feature-gate.tsx new file mode 100644 index 00000000000..f83eafd4761 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/tests-feature-gate.tsx @@ -0,0 +1,19 @@ +'use client' + +import type { ReactNode } from 'react' +import { useFeatureFlag } from '@/app/workspace/[workspaceId]/providers/feature-flags-provider' + +interface TestsFeatureGateProps { + children: ReactNode +} + +export function TestsFeatureGate({ children }: TestsFeatureGateProps) { + const enabled = useFeatureFlag('workflow-tests') + return enabled ? ( + children + ) : ( +
+ Tests are not enabled +
+ ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/workflow-version-preview.tsx b/apps/sim/app/workspace/[workspaceId]/tests/components/workflow-version-preview.tsx new file mode 100644 index 00000000000..733be87472f --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/workflow-version-preview.tsx @@ -0,0 +1,50 @@ +'use client' + +import { useId } from 'react' +import { ChipModal, ChipModalBody, ChipModalHeader } from '@sim/emcn' +import { Preview } from '@/app/workspace/[workspaceId]/w/components/preview' +import { useDeploymentVersionState } from '@/hooks/queries/workflows' + +interface WorkflowVersionPreviewProps { + workflowId: string + version: number + title: string + onClose: () => void +} + +/** A deployed workflow version on a read-only canvas, as the deploy modal shows it. */ +export function WorkflowVersionPreview({ + workflowId, + version, + title, + onClose, +}: WorkflowVersionPreviewProps) { + const descriptionId = useId() + const { data: workflowState, error } = useDeploymentVersionState(workflowId, version) + return ( + { + if (!open) onClose() + }} + srTitle='Workflow preview' + aria-describedby={descriptionId} + size='full' + className='h-[90vh] [&>div]:h-full' + > + {title} + +

+ Visual preview of the workflow version this test ran against. +

+ {error ? ( +

+ {error.message} +

+ ) : workflowState ? ( + + ) : null} +
+
+ ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/layout.tsx b/apps/sim/app/workspace/[workspaceId]/tests/layout.tsx new file mode 100644 index 00000000000..fafe1fdf8a9 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/layout.tsx @@ -0,0 +1,10 @@ +import type { ReactNode } from 'react' +import { TestsFeatureGate } from '@/app/workspace/[workspaceId]/tests/components' + +interface TestsLayoutProps { + children: ReactNode +} + +export default function TestsLayout({ children }: TestsLayoutProps) { + return {children} +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/loading.tsx b/apps/sim/app/workspace/[workspaceId]/tests/loading.tsx new file mode 100644 index 00000000000..23cb60115e6 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/loading.tsx @@ -0,0 +1,8 @@ +'use client' + +import { ShieldCheck } from '@sim/emcn/icons' +import { ResourceChromeFallback } from '@/app/workspace/[workspaceId]/components/resource/components/resource-chrome-fallback' + +export default function TestsLoading() { + return +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/page.tsx b/apps/sim/app/workspace/[workspaceId]/tests/page.tsx new file mode 100644 index 00000000000..671433413e8 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/page.tsx @@ -0,0 +1,19 @@ +import { Suspense } from 'react' +import type { Metadata } from 'next' +import TestsLoading from '@/app/workspace/[workspaceId]/tests/loading' +import { Tests } from '@/app/workspace/[workspaceId]/tests/tests' + +export const metadata: Metadata = { title: 'Tests', robots: { index: false } } + +interface TestsPageProps { + params: Promise<{ workspaceId: string }> +} + +export default async function TestsPage({ params }: TestsPageProps) { + const { workspaceId } = await params + return ( + }> + + + ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/search-params.ts b/apps/sim/app/workspace/[workspaceId]/tests/search-params.ts new file mode 100644 index 00000000000..1d0a4236440 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/search-params.ts @@ -0,0 +1,18 @@ +import { parseAsString } from 'nuqs/server' +import { createSortParams } from '@/lib/url-state' + +/** Sortable columns, matching the `Resource.Options` sort menu. */ +const TEST_SORT_COLUMNS = ['name', 'cases', 'lastRun', 'updated'] as const + +/** Shared `sort` + `dir` params for the Tests list. Default: most recently updated first. */ +export const testsSortParams = createSortParams(TEST_SORT_COLUMNS, { + column: 'updated', + direction: 'desc', +}) + +/** `search` filters by title; the input is controlled by it and only its URL write is debounced. */ +export const testsParsers = { + search: parseAsString.withDefault(''), +} as const + +export const testsUrlKeys = { history: 'replace', clearOnDefault: true } as const diff --git a/apps/sim/app/workspace/[workspaceId]/tests/tests.tsx b/apps/sim/app/workspace/[workspaceId]/tests/tests.tsx new file mode 100644 index 00000000000..8654b807822 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/tests.tsx @@ -0,0 +1,163 @@ +'use client' + +import { useMemo } from 'react' +import { toast } from '@sim/emcn' +import { PlayOutline, ShieldCheck } from '@sim/emcn/icons' +import { useRouter } from 'next/navigation' +import { useQueryStates } from 'nuqs' +import type { WorkflowTestRecord } from '@/lib/api/contracts/workflow-tests' +import { SEARCH_DEBOUNCE_MS } from '@/lib/url-state' +import { ownerCell } from '@/app/workspace/[workspaceId]/components/resource/components/owner-cell' +import { ResourceNoResults } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state' +import type { + SearchConfig, + SortConfig, +} from '@/app/workspace/[workspaceId]/components/resource/components/resource-options' +import { timeCell } from '@/app/workspace/[workspaceId]/components/resource/components/time-cell' +import { + Resource, + type ResourceColumn, + type ResourceRow, +} from '@/app/workspace/[workspaceId]/components/resource/resource' +import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' +import { TestStatusBadge } from '@/app/workspace/[workspaceId]/tests/components/test-status-badge' +import { + testsParsers, + testsSortParams, + testsUrlKeys, +} from '@/app/workspace/[workspaceId]/tests/search-params' +import { useRunWorkflowTests, useWorkflowTests } from '@/hooks/queries/workflow-tests' +import { useWorkspaceMembersQuery } from '@/hooks/queries/workspace' +import { useDebouncedSearchSetter } from '@/hooks/use-debounced-search-setter' +import { useSearchFilterValue } from '@/hooks/use-search-filter-value' +import { useUrlSort } from '@/hooks/use-url-sort' + +const COLUMNS: ResourceColumn[] = [ + { id: 'name', header: 'Name' }, + { id: 'status', header: 'Status' }, + { id: 'cases', header: 'Cases' }, + { id: 'owner', header: 'Created by' }, + { id: 'lastRun', header: 'Last run' }, + { id: 'updated', header: 'Last updated' }, +] + +const SORT_OPTIONS = [ + { id: 'name', label: 'Name' }, + { id: 'cases', label: 'Cases' }, + { id: 'lastRun', label: 'Last run' }, + { id: 'updated', label: 'Last updated' }, +] + +function lastRunAt(test: WorkflowTestRecord): string | null { + const latest = test.recentRuns[0] + return latest ? (latest.completedAt ?? latest.startedAt) : null +} + +const SORT_KEYS: Record< + (typeof SORT_OPTIONS)[number]['id'], + (test: WorkflowTestRecord) => string | number +> = { + name: (test) => test.title.toLowerCase(), + cases: (test) => test.caseCount, + lastRun: (test) => lastRunAt(test) ?? '', + updated: (test) => test.updatedAt, +} + +interface TestsProps { + workspaceId: string +} + +export function Tests({ workspaceId }: TestsProps) { + const router = useRouter() + const canEdit = useUserPermissionsContext().canEdit === true + const [{ search }, setFilters] = useQueryStates(testsParsers, testsUrlKeys) + const setSearch = useDebouncedSearchSetter((value, options) => + setFilters({ search: value }, options) + ) + const debouncedSearch = useSearchFilterValue(search, SEARCH_DEBOUNCE_MS) + const { sort, dir, activeSort, onSort, onClear } = useUrlSort(testsSortParams, testsUrlKeys) + const query = useWorkflowTests(workspaceId) + const { data: members } = useWorkspaceMembersQuery(workspaceId) + const runTests = useRunWorkflowTests(workspaceId) + + const membersById = useMemo( + () => new Map((members ?? []).map((member) => [member.userId, member])), + [members] + ) + + const tests = useMemo(() => { + const term = debouncedSearch.trim().toLowerCase() + const matching = (query.data ?? []).filter( + (test) => !term || test.title.toLowerCase().includes(term) || test.name.includes(term) + ) + const key = SORT_KEYS[sort] + const direction = dir === 'asc' ? 1 : -1 + return [...matching].sort((a, b) => { + const left = key(a) + const right = key(b) + return left < right ? -direction : left > right ? direction : 0 + }) + }, [query.data, debouncedSearch, sort, dir]) + + const rows: ResourceRow[] = useMemo( + () => + tests.map((test) => ({ + id: test.name, + cells: { + name: { icon: , label: test.title }, + status: { content: }, + cases: { label: String(test.caseCount) }, + owner: ownerCell(test.createdByUserId, membersById), + lastRun: timeCell(lastRunAt(test)), + updated: timeCell(test.updatedAt), + }, + })), + [tests, membersById] + ) + + const searchConfig: SearchConfig = { + value: search, + onChange: setSearch, + onClearAll: () => setSearch(''), + placeholder: 'Search tests...', + } + const sortConfig: SortConfig = { options: SORT_OPTIONS, active: activeSort, onSort, onClear } + const running = runTests.isPending || (query.data ?? []).some((test) => test.status === 'running') + + return ( + + + runTests.mutate( + { version: 'deployed' }, + { onError: (error) => toast.error(error.message) } + ), + }, + ]} + /> + + router.push(`/workspace/${workspaceId}/tests/${name}`)} + emptyState={ + debouncedSearch.trim() && query.data?.length ? ( + setSearch('')} + /> + ) : undefined + } + /> + + ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx index 683439f271b..a6bf618b34f 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx @@ -34,6 +34,7 @@ import { Pin, Plus, Search, + ShieldCheck, Table, Task, Workflow, @@ -705,6 +706,7 @@ export const Sidebar = memo(function Sidebar({ organizationHref }: SidebarProps) ) const dashboardsEnabled = useFeatureFlag('dashboards') + const testsEnabled = useFeatureFlag('workflow-tests') const topNavItems = useMemo( () => [ @@ -780,6 +782,14 @@ export const Sidebar = memo(function Sidebar({ organizationHref }: SidebarProps) hidden: permissionConfig.hideKnowledgeBaseTab && !accessRequestsEnabled, restricted: permissionConfig.hideKnowledgeBaseTab, }, + { + id: 'tests', + label: 'Tests', + icon: ShieldCheck, + href: `/workspace/${workspaceId}/tests`, + hidden: !testsEnabled || (permissionConfig.hideFilesTab && !accessRequestsEnabled), + restricted: permissionConfig.hideFilesTab, + }, { id: 'logs', label: 'Logs', @@ -794,6 +804,7 @@ export const Sidebar = memo(function Sidebar({ organizationHref }: SidebarProps) permissionConfig.hideKnowledgeBaseTab, permissionConfig.hideTablesTab, accessRequestsEnabled, + testsEnabled, ] ) diff --git a/apps/sim/executor/execution/block-executor.ts b/apps/sim/executor/execution/block-executor.ts index 0623ffa828f..4b1a84cd33b 100644 --- a/apps/sim/executor/execution/block-executor.ts +++ b/apps/sim/executor/execution/block-executor.ts @@ -35,6 +35,7 @@ import { isRetryableBlockError, resolveBlockRetryPolicy } from '@/executor/execu import type { BlockStateWriter, ContextExtensions, + ExecutionTestHooks, WorkflowNodeMetadata, } from '@/executor/execution/types' import { @@ -272,6 +273,9 @@ export class BlockExecutor { * for the same reason `streamingPartialOutput` above is. */ let completedHandlerCost: TrustedExecutionCost | undefined + const testHooks = this.contextExtensions.testHooks + const mockedBlockId = node.metadata.originalBlockId ?? block.id + const isMocked = !isSentinel && testHooks?.mocksBlock(mockedBlockId) === true try { /** * Only the handler call is retried. A streaming handler returns before any @@ -279,6 +283,17 @@ export class BlockExecutor { * already seen. */ const output = await this.runHandlerWithRetry(blockCtx, block, blockLog, (retry) => { + if (isMocked && testHooks) { + return this.resolveMockedBlock( + blockCtx, + node, + block, + mockedBlockId, + testHooks, + inputsForLog, + inputDisplayRegistry + ) + } const invocationMetadata = retry ? { ...nodeMetadata, retry } : nodeMetadata return handler.executeWithNode ? handler.executeWithNode(blockCtx, block, resolvedInputs, invocationMetadata) @@ -433,6 +448,13 @@ export class BlockExecutor { output: displayOutput, }) this.setBlockLogDisplayProvenance(blockLog, displayProvenance) + if (!isMocked && testHooks?.spiesBlock(mockedBlockId)) { + testHooks.recordSpy({ + blockId: mockedBlockId, + input: displayInput, + output: displayOutput, + }) + } this.fireBlockCompleteCallback( blockStartPromise, blockCtx, @@ -929,6 +951,35 @@ export class BlockExecutor { return { result: output } } + /** A test mock sees exactly what the block's execution log shows, never raw resolved secrets. */ + private resolveMockedBlock( + ctx: ExecutionContext, + node: DAGNode, + block: SerializedBlock, + blockId: string, + testHooks: ExecutionTestHooks, + inputs: Record, + registry: ResolvedSecretTraceRegistry | undefined + ): Promise { + const blockName = block.metadata?.name ?? blockId + const projection = registry?.projectResolvedInputSelection(inputs) + if (projection && !projection.complete) { + throw new Error(`Mocked block "${blockName}" has inputs whose secrets cannot be redacted`) + } + return testHooks.resolveMock( + { + blockId, + blockName, + blockType: block.metadata?.id ?? '', + input: this.sanitizeInputsForLog(projection?.value ?? inputs, block), + ...(node.metadata.branchIndex !== undefined + ? { branchIndex: node.metadata.branchIndex } + : {}), + }, + ctx.abortSignal + ) + } + /** Builds the log-facing input copy from resolver-recorded projections only. */ private projectInputsForDisplay( inputs: Record, diff --git a/apps/sim/executor/execution/executor.ts b/apps/sim/executor/execution/executor.ts index 1e049bab378..8c819391120 100644 --- a/apps/sim/executor/execution/executor.ts +++ b/apps/sim/executor/execution/executor.ts @@ -87,6 +87,15 @@ export class DAGExecutor { } async execute(workflowId: string, triggerBlockId?: string): Promise { + await this.contextExtensions.testHooks?.enterWorkflow({ + workflowId, + blocks: this.workflow.blocks.map((block) => ({ + id: block.id, + name: block.metadata?.name ?? block.id, + type: block.metadata?.id ?? '', + params: block.config.params ?? {}, + })), + }) const savedIncomingEdges = this.contextExtensions.dagIncomingEdges const dag = this.dagBuilder.build(this.workflow, { triggerBlockId, @@ -444,6 +453,7 @@ export class DAGExecutor { isDeployedContext: this.contextExtensions.isDeployedContext, enforceCredentialAccess: this.contextExtensions.enforceCredentialAccess, piiBlockOutputRedaction: this.contextExtensions.piiBlockOutputRedaction, + testHooks: this.contextExtensions.testHooks, blockStates: state.getBlockStates(), blockLogs: restoredBlockLogs, /* diff --git a/apps/sim/executor/execution/test-hooks.test.ts b/apps/sim/executor/execution/test-hooks.test.ts new file mode 100644 index 00000000000..97dbdba0439 --- /dev/null +++ b/apps/sim/executor/execution/test-hooks.test.ts @@ -0,0 +1,229 @@ +import type { SessionPrincipal } from '@sim/auth/principal' +import { createSerializedBlock, createSerializedWorkflow } from '@sim/testing' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { + createMockChannel, + type MockChannel, + type MockMatcher, +} from '@/lib/workflow-tests/mock-channel' +import { BlockType } from '@/executor/constants' +import { DAGExecutor } from '@/executor/execution/executor' +import type { ExecutionResult } from '@/executor/types' +import { stripCloneSuffixes } from '@/executor/utils/subflow-node-id-codec' +import type { SerializedWorkflow } from '@/serializer/types' + +const { executed, inputsSeen } = vi.hoisted(() => ({ + executed: [] as string[], + inputsSeen: [] as Array<{ blockId: string; inputs: Record }>, +})) + +vi.mock('@/executor/handlers/registry', () => ({ + createBlockHandlers: () => [ + { + canHandle: () => true, + execute: async (_ctx: unknown, block: { id: string }, inputs: Record) => { + executed.push(block.id) + inputsSeen.push({ blockId: block.id, inputs }) + return { ok: true, from: block.id } + }, + }, + ], +})) + +const PRINCIPAL: SessionPrincipal = { kind: 'session', userId: 'user-1', sessionId: 'session-1' } + +const BLOCK_TYPES: Record = { + start: BlockType.STARTER, + loop: BlockType.LOOP, + fanOut: BlockType.PARALLEL, +} + +function workflow( + connections: SerializedWorkflow['connections'], + params: Record> = {}, + loops: SerializedWorkflow['loops'] = {}, + parallels: SerializedWorkflow['parallels'] = {} +): SerializedWorkflow { + const ids = new Set(connections.flatMap((c) => [c.source, c.target])) + const blocks = [...ids].map((id) => + createSerializedBlock({ + id, + name: id, + type: BLOCK_TYPES[id] ?? BlockType.API, + params: params[id] ?? {}, + }) + ) + return { ...createSerializedWorkflow(blocks, connections), loops, parallels } +} + +/** A test's matcher that mocks exactly these block ids and nothing else. */ +function mockingBlocks(blockIds: string[]): MockMatcher { + const mocked = new Set(blockIds) + return { + enterWorkflow: async () => {}, + mocksBlock: (blockId) => mocked.has(blockId), + mocksTool: () => false, + spiesBlock: () => false, + recordSpy: () => {}, + } +} + +function run( + wf: SerializedWorkflow, + channel: MockChannel, + abortSignal?: AbortSignal +): Promise { + return new DAGExecutor({ + workflow: wf, + contextExtensions: { + workspaceId: 'ws', + executionId: 'exec-1', + principal: PRINCIPAL, + testHooks: channel.hooks, + ...(abortSignal ? { abortSignal } : {}), + }, + }).execute('wf') +} + +const inputsOf = (blockId: string) => + inputsSeen.filter((seen) => stripCloneSuffixes(seen.blockId) === blockId).map((s) => s.inputs) + +describe('executor test hooks', () => { + beforeEach(() => { + executed.length = 0 + inputsSeen.length = 0 + }) + + const linear = workflow( + [ + { source: 'start', target: 'prep' }, + { source: 'prep', target: 'lookup' }, + { source: 'lookup', target: 'notify' }, + ], + { + lookup: { query: '' }, + notify: { plan: '' }, + } + ) + + it('parks a mocked block with its resolved inputs and feeds the reply downstream', async () => { + const channel = createMockChannel(mockingBlocks(['lookup'])) + const done = run(linear, channel) + + const parked = await channel.nextCall() + expect(parked.call.blockId).toBe('lookup') + expect(parked.call.blockName).toBe('lookup') + expect(parked.call.input).toEqual({ query: 'prep' }) + + channel.reply(parked.callId, { output: { plan: 'pro' } }) + const result = await done + + expect(result.success).toBe(true) + expect(executed).not.toContain('lookup') + expect(inputsOf('notify')).toEqual([{ plan: 'pro' }]) + }) + + it('fails the block, and the run, with the error a mock rejects with', async () => { + const channel = createMockChannel(mockingBlocks(['lookup'])) + const done = run(linear, channel) + + const parked = await channel.nextCall() + channel.reply(parked.callId, { error: 'rate limited (429)' }) + + await expect(done).rejects.toThrow('rate limited (429)') + expect(executed).not.toContain('notify') + }) + + it('stops a run that is cancelled while a mock is parked, and refuses a late reply', async () => { + const channel = createMockChannel(mockingBlocks(['lookup'])) + const abort = new AbortController() + const done = run(linear, channel, abort.signal) + + const parked = await channel.nextCall() + abort.abort() + const result = await done + + expect(result.success).toBe(false) + expect(executed).not.toContain('notify') + expect(() => channel.reply(parked.callId, { output: { plan: 'pro' } })).toThrow() + }) + + it('routes each parallel branch its own reply, whatever order the replies arrive in', async () => { + const parallel = workflow( + [ + { source: 'start', target: 'fanOut' }, + { source: 'fanOut', target: 'lookup', sourceHandle: 'parallel-start-source' }, + { source: 'lookup', target: 'notify' }, + ], + { notify: { plan: '' } }, + {}, + { + fanOut: { + id: 'fanOut', + nodes: ['lookup', 'notify'], + count: 3, + parallelType: 'count', + }, + } + ) + const channel = createMockChannel(mockingBlocks(['lookup'])) + const done = run(parallel, channel) + + const parked = [await channel.nextCall(), await channel.nextCall(), await channel.nextCall()] + for (const [i, call] of [...parked].reverse().entries()) { + channel.reply(call.callId, { output: { plan: `plan-${call.call.branchIndex}-${i}` } }) + } + const result = await done + + expect(result.success).toBe(true) + expect(parked.map((p) => p.call.branchIndex).sort()).toEqual([0, 1, 2]) + const plans = inputsOf('notify').map((inputs) => String(inputs.plan)) + expect(plans).toHaveLength(3) + expect(plans.map((plan) => plan.split('-')[1]).sort()).toEqual(['0', '1', '2']) + }) + + it('delivers one call per loop iteration, in order', async () => { + const looped = workflow( + [ + { source: 'start', target: 'loop' }, + { source: 'loop', target: 'lookup', sourceHandle: 'loop-start-source' }, + { source: 'lookup', target: 'notify' }, + ], + { notify: { plan: '' } }, + { loop: { id: 'loop', nodes: ['lookup', 'notify'], iterations: 2, loopType: 'for' } } + ) + const channel = createMockChannel(mockingBlocks(['lookup'])) + const done = run(looped, channel) + + for (const plan of ['first', 'second']) { + const parked = await channel.nextCall() + channel.reply(parked.callId, { output: { plan } }) + } + const result = await done + + expect(result.success).toBe(true) + expect(inputsOf('notify')).toEqual([{ plan: 'first' }, { plan: 'second' }]) + }) + + it('refuses a reply to a call that is unknown or already answered', async () => { + const channel = createMockChannel(mockingBlocks(['lookup'])) + const done = run(linear, channel) + + const parked = await channel.nextCall() + expect(() => channel.reply('nope', { output: {} })).toThrow() + channel.reply(parked.callId, { output: { plan: 'pro' } }) + expect(() => channel.reply(parked.callId, { output: { plan: 'pro' } })).toThrow() + await done + }) + + it('rejects every parked call when the channel closes, so the run cannot hang', async () => { + const channel = createMockChannel(mockingBlocks(['lookup'])) + const done = run(linear, channel) + + await channel.nextCall() + channel.close(new Error('test sandbox exited')) + + await expect(done).rejects.toThrow('test sandbox exited') + await expect(channel.nextCall()).rejects.toThrow('test sandbox exited') + }) +}) diff --git a/apps/sim/executor/execution/types.ts b/apps/sim/executor/execution/types.ts index 59e1679209c..84a59059a33 100644 --- a/apps/sim/executor/execution/types.ts +++ b/apps/sim/executor/execution/types.ts @@ -249,6 +249,54 @@ export interface PiiBlockOutputRedaction { customPatterns?: CustomPiiPattern[] } +/** A mocked block reached by a test run, carrying the inputs its execution log shows. */ +export interface MockedBlockCall { + blockId: string + blockName: string + blockType: string + input: Record + /** Set inside a parallel: which branch made the call. */ + branchIndex?: number +} + +/** A tool call an Agent block made that the test answers instead of the tool. */ +export interface MockedToolCall { + /** The calling Agent's workflow block id (never a parallel-branch clone id). */ + blockId: string + toolId: string + /** The arguments the model sent, with credentials and internal context removed. */ + input: Record +} + +/** One block of a workflow a test run entered, as the executor will run it. */ +export interface TestWorkflowBlock { + id: string + name: string + type: string + params: Record +} + +/** + * Hooks a workflow test run installs. Every workflow the run executes, the tested one and each + * child, announces its blocks with `enterWorkflow` before running; the test matches its mocks + * and spies against those blocks by name. A mocked block awaits `resolveMock` in place of its + * handler; everything after the handler (normalization, redaction, logging, edges) runs as + * usual. A mocked tool call awaits `resolveToolMock` in place of the tool. Child workflow + * executions inherit the hooks. + */ +export interface ExecutionTestHooks { + enterWorkflow(workflow: { workflowId: string; blocks: TestWorkflowBlock[] }): Promise + /** Whether this block's handler is replaced; ids are workflow block ids, never clone ids. */ + mocksBlock(blockId: string): boolean + resolveMock(call: MockedBlockCall, abortSignal?: AbortSignal): Promise + /** Whether this Agent block's calls to this tool are answered by the test. */ + mocksTool(blockId: string, toolId: string): boolean + resolveToolMock(call: MockedToolCall, abortSignal?: AbortSignal): Promise> + /** Whether the test records this block's inputs and output while it runs for real. */ + spiesBlock(blockId: string): boolean + recordSpy(call: { blockId: string; input: unknown; output: unknown }): void +} + export interface ContextExtensions { workspaceId?: string executionId?: string @@ -346,6 +394,8 @@ export interface ContextExtensions { */ stopAfterBlockId?: string + testHooks?: ExecutionTestHooks + /** * Ordered list of workflow IDs in the current call chain, used for cycle detection. * Each hop appends the current workflow ID before making outgoing requests. diff --git a/apps/sim/executor/handlers/workflow/workflow-handler.ts b/apps/sim/executor/handlers/workflow/workflow-handler.ts index 2138d89bb63..1e77461ab47 100644 --- a/apps/sim/executor/handlers/workflow/workflow-handler.ts +++ b/apps/sim/executor/handlers/workflow/workflow-handler.ts @@ -888,6 +888,8 @@ export class WorkflowBlockHandler implements BlockHandler { // Propagate in-flight block-output redaction into child workflows so // nested blocks mask outputs too (recurses: each child forwards it). piiBlockOutputRedaction: ctx.piiBlockOutputRedaction, + // A workflow test's mocks and spies reach blocks in every child it runs. + testHooks: ctx.testHooks, callChain: childCallChain, // A custom block's own session markers and the parent's live stream, fanned // out together — see `childCallbacks` above for why this is not two spreads. diff --git a/apps/sim/executor/types.ts b/apps/sim/executor/types.ts index c51495ed3cd..727203d6ef5 100644 --- a/apps/sim/executor/types.ts +++ b/apps/sim/executor/types.ts @@ -6,6 +6,7 @@ import type { BlockOutput } from '@/blocks/types' import type { ChildWorkflowContext, ExecutionCallbacks, + ExecutionTestHooks, IterationContext, ParentIteration, PiiBlockOutputRedaction, @@ -416,6 +417,8 @@ export interface ExecutorDelegationOrigin { export interface ExecutionContext { workflowId: string + /** Set during a workflow test run; Agent tool calls consult it before calling the tool. */ + testHooks?: ExecutionTestHooks workspaceId?: string executionId?: string largeValueExecutionIds?: string[] diff --git a/apps/sim/hooks/queries/workflow-tests.ts b/apps/sim/hooks/queries/workflow-tests.ts new file mode 100644 index 00000000000..97dab6c5ed3 --- /dev/null +++ b/apps/sim/hooks/queries/workflow-tests.ts @@ -0,0 +1,102 @@ +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +import { requestJson } from '@/lib/api/client/request' +import { + getWorkflowTestContract, + getWorkflowTestRunContract, + listWorkflowTestsContract, + runWorkflowTestsContract, +} from '@/lib/api/contracts/workflow-tests' + +export type WorkflowTestVersion = 'draft' | 'deployed' + +const WORKFLOW_TEST_STALE_TIME = 15_000 +/** While a run is in flight, so its result appears when it lands. */ +const WORKFLOW_TEST_RUNNING_REFETCH_INTERVAL = 1_000 + +export const workflowTestKeys = { + all: ['workflow-tests'] as const, + lists: () => [...workflowTestKeys.all, 'list'] as const, + list: (workspaceId: string) => [...workflowTestKeys.lists(), workspaceId] as const, + details: () => [...workflowTestKeys.all, 'detail'] as const, + test: (workspaceId: string, name: string) => + [...workflowTestKeys.details(), workspaceId, name] as const, + detail: (workspaceId: string, name: string) => + [...workflowTestKeys.test(workspaceId, name), 'detail'] as const, + run: (workspaceId: string, name: string, runId: string) => + [...workflowTestKeys.test(workspaceId, name), 'run', runId] as const, +} + +/** Every test with its recent runs, draft and deployed alike. */ +export function useWorkflowTests(workspaceId: string, options?: { enabled?: boolean }) { + return useQuery({ + queryKey: workflowTestKeys.list(workspaceId), + queryFn: ({ signal }) => + requestJson(listWorkflowTestsContract, { + params: { id: workspaceId }, + query: {}, + signal, + }), + enabled: Boolean(workspaceId) && (options?.enabled ?? true), + staleTime: WORKFLOW_TEST_STALE_TIME, + select: (data) => data.tests, + refetchInterval: (query) => + query.state.data?.tests.some((test) => test.status === 'running') + ? WORKFLOW_TEST_RUNNING_REFETCH_INTERVAL + : false, + }) +} + +/** One test with its latest run and history, draft and deployed alike. */ +export function useWorkflowTest(workspaceId: string, name: string) { + return useQuery({ + queryKey: workflowTestKeys.detail(workspaceId, name), + queryFn: ({ signal }) => + requestJson(getWorkflowTestContract, { + params: { id: workspaceId, name }, + query: {}, + signal, + }), + enabled: Boolean(workspaceId && name), + staleTime: WORKFLOW_TEST_STALE_TIME, + refetchInterval: (query) => + query.state.data?.latestRun?.status === 'running' + ? WORKFLOW_TEST_RUNNING_REFETCH_INTERVAL + : false, + }) +} + +/** One earlier run with its report; null `runId` reads nothing. */ +export function useWorkflowTestRun(workspaceId: string, name: string, runId: string | null) { + return useQuery({ + queryKey: workflowTestKeys.run(workspaceId, name, runId ?? ''), + queryFn: ({ signal }) => + requestJson(getWorkflowTestRunContract, { + params: { id: workspaceId, name, runId: runId ?? '' }, + signal, + }), + enabled: Boolean(workspaceId && name && runId), + staleTime: WORKFLOW_TEST_STALE_TIME, + select: (data) => data.run, + }) +} + +/** Refetches one test after its file changes, so its cases follow the edit. */ +export function useRefreshWorkflowTest(workspaceId: string, name: string) { + const queryClient = useQueryClient() + return () => { + queryClient.invalidateQueries({ queryKey: workflowTestKeys.lists() }) + queryClient.invalidateQueries({ queryKey: workflowTestKeys.test(workspaceId, name) }) + } +} + +export function useRunWorkflowTests(workspaceId: string) { + const queryClient = useQueryClient() + return useMutation({ + mutationFn: (body: { version: WorkflowTestVersion; names?: string[] }) => + requestJson(runWorkflowTestsContract, { params: { id: workspaceId }, body }), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: workflowTestKeys.lists() }) + queryClient.invalidateQueries({ queryKey: workflowTestKeys.details() }) + }, + }) +} diff --git a/apps/sim/lib/api/contracts/mothership-management-tools.ts b/apps/sim/lib/api/contracts/mothership-management-tools.ts index e2b839b55af..859c5830200 100644 --- a/apps/sim/lib/api/contracts/mothership-management-tools.ts +++ b/apps/sim/lib/api/contracts/mothership-management-tools.ts @@ -1,5 +1,6 @@ import { z } from 'zod' import { mothershipDashboardsInputSchema } from '@/lib/api/contracts/mothership-dashboards' +import { mothershipTestsInputSchema } from '@/lib/api/contracts/mothership-tests' import { createWorkspaceInputSchema } from '@/lib/workspaces/create-input' import { organizationSearchSourcesInputSchema } from './mothership-search-sources' import { mothershipSettingsInputSchema } from './mothership-settings' @@ -18,6 +19,14 @@ export const managementToolContracts = [ 'Read and save the selected workspace’s single dashboard, validated YAML over live tables. Load the create-dashboard skill for the schema. get returns content and revision, or nulls when the workspace has no dashboard yet; set with no revision creates it. Replacing an existing dashboard requires expectedRevision from get, so a concurrent edit is never overwritten. Use open_resource with type dashboard to show the result.', inputSchema: mothershipDashboardsInputSchema, }, + { + id: 'tests', + route: 'sim', + scope: 'all', + description: + 'Create and run the selected workspace\u2019s workflow tests. create takes a name, a one-line title for the concern, and an optional description, and returns tests/.test.js; write the cases into that file with the file tools. A test file is plain vitest: import { describe, it, expect, vi } from "vitest" and { runWorkflow, mockBlock, mockTool, spyOnBlock } from "sim:test", one top-level describe, an it per case. runWorkflow(name, input) runs a workflow and returns { output } (pass { trigger: \"Trigger block name\" } as a third argument when it has several triggers); mockBlock(blockName) returns a vi.fn whose value replaces that block\u2019s output and records its inputs; mockTool(toolId) or mockTool(agentBlockName, toolId) answers an Agent\u2019s calls to that tool the same way while the model still runs; .mockSampleOutput({ ...overrides }) on either mock returns a placeholder output shaped like the real one, with your fields merged in; await expect(value).toMatchRubric(rubric) asks a model judge for pass or fail. Every write is checked and refused if the file does not load. run takes a version (draft while editing, deployed before shipping), waits, and returns each file\u2019s failures with line numbers and messages. list and get report status; update changes title or description.', + inputSchema: mothershipTestsInputSchema, + }, { id: 'workspaces', route: 'sim', diff --git a/apps/sim/lib/api/contracts/mothership-tests.ts b/apps/sim/lib/api/contracts/mothership-tests.ts new file mode 100644 index 00000000000..d5526940523 --- /dev/null +++ b/apps/sim/lib/api/contracts/mothership-tests.ts @@ -0,0 +1,60 @@ +import { z } from 'zod' + +const scope = z.object({ workspaceId: z.string().min(1).max(100).optional() }) +const nameSchema = z + .string() + .trim() + .min(1) + .max(80) + .describe('Test file name: tests/.test.js, lowercase letters, numbers, and dashes.') +const versionSchema = z + .enum(['draft', 'deployed']) + .describe( + 'Which version of the workflows to test: draft while editing, deployed before shipping.' + ) + +/** Create, describe, and run the workspace's workflow tests. Cases live in `tests/.test.js`. */ +export const mothershipTestsInputSchema = z.discriminatedUnion('action', [ + scope + .extend({ + action: z.literal('create'), + name: nameSchema, + title: z + .string() + .trim() + .min(1) + .max(200) + .describe('The concern this file tests, in one line.'), + description: z.string().max(2000).optional(), + }) + .strict(), + scope + .extend({ + action: z.literal('update'), + name: nameSchema, + title: z.string().trim().min(1).max(200).optional(), + description: z.string().max(2000).nullable().optional(), + }) + .strict(), + scope.extend({ action: z.literal('list'), version: versionSchema }).strict(), + scope.extend({ action: z.literal('get'), name: nameSchema, version: versionSchema }).strict(), + scope + .extend({ + action: z.literal('run'), + version: versionSchema, + names: z + .array(nameSchema) + .min(1) + .max(100) + .optional() + .describe('Omit to run every test file.'), + only: z + .array(z.string().min(1).max(500)) + .min(1) + .max(100) + .optional() + .describe('With exactly one name: the "describe > it" test names to run in it.'), + }) + .strict(), + scope.extend({ action: z.literal('delete'), name: nameSchema }).strict(), +]) diff --git a/apps/sim/lib/api/contracts/workflow-tests.ts b/apps/sim/lib/api/contracts/workflow-tests.ts new file mode 100644 index 00000000000..395d7223c42 --- /dev/null +++ b/apps/sim/lib/api/contracts/workflow-tests.ts @@ -0,0 +1,119 @@ +import { z } from 'zod' +import { workspaceIdSchema } from '@/lib/api/contracts/primitives' +import { type ContractJsonResponse, defineRouteContract } from '@/lib/api/contracts/types' +import { testCaseStatusSchema, testReportSchema } from '@/lib/workflow-tests/protocol' + +const versionSchema = z.enum(['draft', 'deployed']) +const testNameSchema = z + .string() + .min(1) + .max(80, 'Test names are at most 80 characters, like billing-routing') + +const workspaceParams = z.object({ id: workspaceIdSchema }) +const testParams = z.object({ id: workspaceIdSchema, name: testNameSchema }) +/** Omitted, runs of both versions are read. */ +const versionQuery = z.object({ version: versionSchema.optional() }) + +const runSummarySchema = z.object({ + id: z.string(), + status: z.enum(['running', 'passed', 'failed', 'error']), + passed: z.number().int(), + failed: z.number().int(), + skipped: z.number().int(), + error: z.string().nullable(), + triggeredByUserId: z.string().nullable(), + version: versionSchema, + sourceChanged: z.boolean(), + ranAgainst: z.array( + z.object({ + workflowId: z.string(), + name: z.string().nullable(), + version: z.number().int().nullable(), + draft: z.boolean(), + liveVersion: z.number().int().nullable(), + stale: z.boolean(), + }) + ), + current: z.boolean(), + startedAt: z.string(), + completedAt: z.string().nullable(), +}) + +/** One run with its report, and each case's status while it goes. */ +const runDetailSchema = runSummarySchema.extend({ + report: testReportSchema.nullable(), + /** Each case's status by `describe > it` path while the run goes; null before any. */ + progress: z.record(z.string(), testCaseStatusSchema).nullable(), +}) + +const testRecordSchema = z.object({ + id: z.string(), + name: z.string(), + title: z.string(), + description: z.string().nullable(), + path: z.string(), + fileId: z.string(), + caseCount: z.number().int(), + createdByUserId: z.string().nullable(), + createdAt: z.string(), + status: z.enum(['passing', 'failing', 'error', 'running', 'changed', 'not_run']), + recentRuns: z.array(runSummarySchema), + updatedAt: z.string(), +}) + +const caseSchema = z.object({ + path: z.array(z.string()), + mode: z.enum(['run', 'only', 'skip']), + line: z.number().int().positive(), + endLine: z.number().int().positive(), +}) + +export const listWorkflowTestsContract = defineRouteContract({ + method: 'GET', + path: '/api/workspaces/[id]/tests', + params: workspaceParams, + query: versionQuery, + response: { mode: 'json', schema: z.object({ tests: z.array(testRecordSchema) }) }, +}) + +export type WorkflowTestRecord = ContractJsonResponse< + typeof listWorkflowTestsContract +>['tests'][number] + +export const getWorkflowTestContract = defineRouteContract({ + method: 'GET', + path: '/api/workspaces/[id]/tests/[name]', + params: testParams, + query: versionQuery, + response: { + mode: 'json', + schema: z.object({ + test: testRecordSchema.extend({ cases: z.array(caseSchema) }), + latestRun: runDetailSchema.nullable(), + history: z.array(runSummarySchema), + }), + }, +}) +export type WorkflowTestDetail = ContractJsonResponse + +export const getWorkflowTestRunContract = defineRouteContract({ + method: 'GET', + path: '/api/workspaces/[id]/tests/[name]/runs/[runId]', + params: testParams.extend({ runId: z.string().min(1).max(64) }), + response: { mode: 'json', schema: z.object({ run: runDetailSchema }) }, +}) + +export const runWorkflowTestsContract = defineRouteContract({ + method: 'POST', + path: '/api/workspaces/[id]/tests/run', + params: workspaceParams, + body: z.object({ + version: versionSchema, + names: z.array(testNameSchema).min(1).max(100).optional(), + only: z.array(z.string().min(1).max(500)).min(1).max(100).optional(), + }), + response: { + mode: 'json', + schema: z.object({ runs: z.array(z.object({ runId: z.string(), name: z.string() })) }), + }, +}) diff --git a/apps/sim/lib/api/contracts/workspace-files.ts b/apps/sim/lib/api/contracts/workspace-files.ts index 22453430e7e..d8b58df82a8 100644 --- a/apps/sim/lib/api/contracts/workspace-files.ts +++ b/apps/sim/lib/api/contracts/workspace-files.ts @@ -162,7 +162,7 @@ export const workspaceFileRecordSchema = z.object({ contentUpdatedAt: z.coerce.date().nullable().optional(), revision: z.string().optional(), storageContext: z.enum(['workspace', 'mothership']).optional(), - vfsNamespace: z.literal('uploads').optional(), + vfsNamespace: z.enum(['uploads', 'tests']).optional(), share: shareRecordSchema.nullable().optional(), }) diff --git a/apps/sim/lib/billing/usage-sources.ts b/apps/sim/lib/billing/usage-sources.ts index 0e7faeae394..375038684c6 100644 --- a/apps/sim/lib/billing/usage-sources.ts +++ b/apps/sim/lib/billing/usage-sources.ts @@ -10,6 +10,7 @@ export const INTERNAL_USAGE_LOG_SOURCES = [ 'enrichment', 'voice-output', 'api-tool', + 'workflow-test', ] as const export type InternalUsageLogSource = (typeof INTERNAL_USAGE_LOG_SOURCES)[number] @@ -41,12 +42,13 @@ const INTERNAL_TO_BILLING_SOURCE = { enrichment: 'enrichment', 'voice-output': 'voice-output', 'api-tool': 'api-tool', + 'workflow-test': 'workflow', } as const satisfies Record const INTERNAL_USAGE_LOG_SOURCE_SET = new Set(INTERNAL_USAGE_LOG_SOURCES) const BILLING_TO_INTERNAL_SOURCES = { - workflow: ['workflow'], + workflow: ['workflow', 'workflow-test'], wand: ['wand'], 'sim-chat': ['copilot', 'workspace-chat'], mcp_copilot: ['mcp_copilot'], diff --git a/apps/sim/lib/core/config/env.ts b/apps/sim/lib/core/config/env.ts index 32c747e15e1..7b76747f2d2 100644 --- a/apps/sim/lib/core/config/env.ts +++ b/apps/sim/lib/core/config/env.ts @@ -600,6 +600,7 @@ export const env = createEnv({ MSHIP_PLAN_MODE: z.boolean().optional(), DASHBOARDS: z.boolean().optional(), PROJECT_API_ENABLED: z.boolean().optional(), // Fallback for the `projects` feature flag off AppConfig + WORKFLOW_TESTS: z.boolean().optional(), // Fallback for the `workflow-tests` feature flag off AppConfig MSHIP_MODEL_SELECTOR: z.boolean().optional(), MSHIP_DESKTOP_BACKGROUND_EXECUTOR: z.boolean().optional(), // Fallback for the `mothership-desktop-background-executor` feature flag off AppConfig INBOX_ENABLED: z.boolean().optional(), // Enable inbox (Sim Mailer) on self-hosted (bypasses hosted requirements) diff --git a/apps/sim/lib/core/config/feature-flags.ts b/apps/sim/lib/core/config/feature-flags.ts index 4a823d1c6eb..a4f55e34d49 100644 --- a/apps/sim/lib/core/config/feature-flags.ts +++ b/apps/sim/lib/core/config/feature-flags.ts @@ -51,6 +51,11 @@ const FEATURE_FLAGS = { 'Enable dashboard resources, rendering, analytics, and Mothership authoring. Supports global and organization rollout; disabled by default.', fallback: 'DASHBOARDS', }, + 'workflow-tests': { + description: + 'Enable workflow tests: the workspace Tests page, tests/.test.js files, and the Sim tests tool. Supports global and organization rollout; disabled by default.', + fallback: 'WORKFLOW_TESTS', + }, 'mothership-model-selector': { description: 'Show the Mothership model selector, model-specific effort levels, and Fast for supported ' + diff --git a/apps/sim/lib/execution/isolated-vm-worker.cjs b/apps/sim/lib/execution/isolated-vm-worker.cjs index a5eeb2e6dd0..4d2ff9ce5f8 100644 --- a/apps/sim/lib/execution/isolated-vm-worker.cjs +++ b/apps/sim/lib/execution/isolated-vm-worker.cjs @@ -23,6 +23,7 @@ const SANDBOX_BUNDLE_FILES = { pptxgenjs: 'pptxgenjs.cjs', docx: 'docx.cjs', 'pdf-lib': 'pdf-lib.cjs', + 'vitest-expect': 'vitest-expect.cjs', } const bundleSourceCache = new Map() const activeIsolates = new Map() @@ -600,6 +601,34 @@ async function executeCode(request, executionId) { * executes user code, then runs `finalize` (must return a Uint8Array). The * resulting bytes are returned as base64 in `bytesBase64`. */ +/** + * Evaluates task user code as an ES module that may import only the task's own modules + * (`userModule.modules`: specifier -> source). Failures throw, so `executeTask`'s catch + * classifies them exactly as it does a script's (user error, timeout, cancellation, OOM). + */ +async function evaluateUserModule(isolate, context, code, userModule, timeoutMs, releaseables) { + const provided = new Map() + for (const [specifier, source] of Object.entries(userModule.modules)) { + const providedModule = await isolate.compileModule(source, { + filename: `sandbox/modules/${specifier}.js`, + }) + releaseables.push(providedModule) + await providedModule.instantiate(context, (dependency) => { + throw new Error(`Sandbox module "${specifier}" cannot import "${dependency}"`) + }) + provided.set(specifier, providedModule) + } + const importable = [...provided.keys()].map((specifier) => `"${specifier}"`).join(' and ') + const entry = await isolate.compileModule(code, { filename: userModule.filename }) + releaseables.push(entry) + await entry.instantiate(context, (specifier) => { + const match = provided.get(specifier) + if (!match) throw new Error(`Cannot import "${specifier}": only ${importable} can be imported`) + return match + }) + await entry.evaluate({ timeout: timeoutMs, promise: true }) +} + async function executeTask(request, executionId) { const { code, timeoutMs, task } = request const stdoutChunks = [] @@ -981,7 +1010,12 @@ async function executeTask(request, executionId) { timings.harden = Date.now() - tPhase tPhase = Date.now() - const wrappedUserCode = ` + let userResult + if (task.userModule) { + await evaluateUserModule(isolate, context, code, task.userModule, timeoutMs, releaseables) + userResult = { success: true } + } else { + const wrappedUserCode = ` (async () => { try { await (async () => { @@ -1000,21 +1034,23 @@ async function executeTask(request, executionId) { } })() ` - const userScript = await isolate.compileScript(wrappedUserCode, { - filename: 'user-function.js', - }) - releaseables.push(userScript) - const userResultJson = await userScript.run(context, { timeout: timeoutMs, promise: true }) + const userScript = await isolate.compileScript(wrappedUserCode, { + filename: 'user-function.js', + }) + releaseables.push(userScript) + const userResultJson = await userScript.run(context, { timeout: timeoutMs, promise: true }) + try { + userResult = JSON.parse(userResultJson) + } catch { + userResult = { + success: false, + errorInfo: { message: 'Invalid user result', name: 'Error' }, + } + } + } timings.userCode = Date.now() - tPhase tPhase = Date.now() - let userResult - try { - userResult = JSON.parse(userResultJson) - } catch { - userResult = { success: false, errorInfo: { message: 'Invalid user result', name: 'Error' } } - } - if (!userResult.success) { timings.total = Date.now() - tStart return { diff --git a/apps/sim/lib/execution/isolated-vm.ts b/apps/sim/lib/execution/isolated-vm.ts index d257c08c8fa..8292b05beba 100644 --- a/apps/sim/lib/execution/isolated-vm.ts +++ b/apps/sim/lib/execution/isolated-vm.ts @@ -58,6 +58,8 @@ interface IsolatedVMTaskRequest { bootstrap: string brokers: string[] finalize: string + /** Evaluate `code` as an ES module named `filename` that may import only `modules`. */ + userModule?: { filename: string; modules: Record } } /** diff --git a/apps/sim/lib/execution/sandbox/bundles/build.ts b/apps/sim/lib/execution/sandbox/bundles/build.ts index 8d380bb4276..41d2133bae9 100644 --- a/apps/sim/lib/execution/sandbox/bundles/build.ts +++ b/apps/sim/lib/execution/sandbox/bundles/build.ts @@ -54,6 +54,7 @@ interface BundleSpec { } const POLYFILLS_PATH = join(HERE, '_polyfills.ts') +const EVENT_POLYFILLS_PATH = join(HERE, 'event-polyfills.ts') const POLYFILL_PRELUDE = ` // Isolate-side polyfills must execute BEFORE any other import (process/browser // captures setTimeout at module-init time). Keep this as the first import. @@ -93,6 +94,65 @@ ${POLYFILL_PRELUDE} import PptxGenJS from 'pptxgenjs' globalThis.__bundles = globalThis.__bundles || {} globalThis.__bundles['pptxgenjs'] = PptxGenJS +`, + }, + { + /** + * Vitest's own `expect` and spies for workflow tests. `vitest` itself can't be bundled for + * an isolate (its expect is entangled with the runner's worker RPC), so this builds the + * standalone packages vitest is made of the way vitest's `createExpect` assembles them. + */ + name: 'vitest-expect', + outFile: 'vitest-expect.cjs', + entry: ` +import '${POLYFILLS_PATH}' +import '${EVENT_POLYFILLS_PATH}' +import { disableDefaultColors } from 'tinyrainbow' +import * as chai from 'chai' +import { + ASYMMETRIC_MATCHERS_OBJECT, + JestAsymmetricMatchers, + JestChaiExpect, + JestExtend, + getState, + setState, +} from '@vitest/expect' +import { fn, isMockFunction, spyOn } from '@vitest/spy' + +disableDefaultColors() +chai.use(JestExtend) +chai.use(JestChaiExpect) +chai.use(JestAsymmetricMatchers) + +function createExpect() { + const expect = (value, message) => { + const { assertionCalls } = getState(expect) + setState({ assertionCalls: assertionCalls + 1 }, expect) + return chai.expect(value, message) + } + Object.assign(expect, chai.expect) + Object.assign(expect, globalThis[ASYMMETRIC_MATCHERS_OBJECT]) + expect.getState = () => getState(expect) + expect.setState = (state) => setState(state, expect) + setState( + { + assertionCalls: 0, + isExpectingAssertions: false, + isExpectingAssertionsError: null, + expectedAssertionsNumber: null, + expectedAssertionsNumberErrorGen: null, + }, + expect + ) + expect.assert = chai.assert + expect.extend = (matchers) => chai.expect.extend(expect, matchers) + expect.unreachable = (message) => + chai.assert.fail('expected' + (message ? ' "' + message + '" ' : ' ') + 'not to be reached') + return expect +} + +globalThis.__bundles = globalThis.__bundles || {} +globalThis.__bundles['vitest-expect'] = { createExpect, fn, spyOn, isMockFunction } `, }, ] diff --git a/apps/sim/lib/execution/sandbox/bundles/event-polyfills.ts b/apps/sim/lib/execution/sandbox/bundles/event-polyfills.ts new file mode 100644 index 00000000000..a7a092df241 --- /dev/null +++ b/apps/sim/lib/execution/sandbox/bundles/event-polyfills.ts @@ -0,0 +1,38 @@ +/** + * chai 6 announces plugin registration through a module-level `EventTarget` and an `Event` + * subclass. A bare isolate has neither, so the vitest-expect bundle imports this first. + */ + +type Listener = (event: { type: string }) => void + +const g: typeof globalThis & { Event?: unknown; EventTarget?: unknown } = globalThis + +if (typeof g.Event === 'undefined') { + g.Event = class Event { + readonly type: string + constructor(type: string) { + this.type = type + } + } +} + +if (typeof g.EventTarget === 'undefined') { + g.EventTarget = class EventTarget { + readonly #listeners = new Map>() + + addEventListener(type: string, listener: Listener): void { + const listeners = this.#listeners.get(type) ?? new Set() + listeners.add(listener) + this.#listeners.set(type, listeners) + } + + removeEventListener(type: string, listener: Listener): void { + this.#listeners.get(type)?.delete(listener) + } + + dispatchEvent(event: { type: string }): boolean { + for (const listener of this.#listeners.get(event.type) ?? []) listener(event) + return true + } + } +} diff --git a/apps/sim/lib/execution/sandbox/bundles/verify.test.ts b/apps/sim/lib/execution/sandbox/bundles/verify.test.ts index 4e0cce4cac0..4fb1134f0cb 100644 --- a/apps/sim/lib/execution/sandbox/bundles/verify.test.ts +++ b/apps/sim/lib/execution/sandbox/bundles/verify.test.ts @@ -30,6 +30,23 @@ describe('sandbox bundles', () => { expect(typeof pdfLib.StandardFonts).toBe('object') }) + it('vitest-expect evaluates in a bare context and fails with uncolored vitest messages', () => { + const bundle = loadCheckedInBundle('vitest-expect') as { + createExpect: () => (value: unknown) => { toEqual: (expected: unknown) => void } + fn: () => unknown + } + expect(typeof bundle.fn).toBe('function') + const sandboxExpect = bundle.createExpect() + let message = '' + try { + sandboxExpect({ channel: '#support' }).toEqual({ channel: '#billing' }) + } catch (error) { + message = (error as Error).message + } + expect(message).toMatch(/channel: '#support'.*to deeply equal.*channel: '#billing'/) + expect(message).not.toContain('\u001b[') + }) + it('pptxgenjs evaluates in a bare context and exposes its constructor', () => { const source = readFileSync(new URL('./pptxgenjs.cjs', import.meta.url), 'utf-8') expect(typeof evaluateSandboxBundle(source, 'pptxgenjs')).toBe('function') diff --git a/apps/sim/lib/execution/sandbox/bundles/vitest-expect.cjs b/apps/sim/lib/execution/sandbox/bundles/vitest-expect.cjs new file mode 100644 index 00000000000..89feed3133c --- /dev/null +++ b/apps/sim/lib/execution/sandbox/bundles/vitest-expect.cjs @@ -0,0 +1,90 @@ +// sandbox bundle: vitest-expect +// generated by apps/sim/lib/execution/sandbox/bundles/build.ts +// do not edit by hand. run `bun run build:sandbox-bundles` to regenerate. +(()=>{var g7=Object.create;var{getPrototypeOf:m7,defineProperty:m9,getOwnPropertyNames:d7}=Object;var c7=Object.prototype.hasOwnProperty;function l7(Z){return this[Z]}var p7,i7,n7=(Z,$,Q)=>{var X=Z!=null&&typeof Z==="object";if(X){var J=$?p7??=new WeakMap:i7??=new WeakMap,W=J.get(Z);if(W)return W}Q=Z!=null?g7(m7(Z)):{};let G=$||!Z||!Z.__esModule?m9(Q,"default",{value:Z,enumerable:!0}):Q;for(let Y of d7(Z))if(!c7.call(G,Y))m9(G,Y,{get:l7.bind(Z,Y),enumerable:!0});if(X)J.set(Z,G);return G};var o7=(Z,$)=>()=>($||Z(($={exports:{}}).exports,$),$.exports);var d9=o7(()=>{var z0=globalThis;if(typeof z0.Event>"u")z0.Event=class{type;constructor($){this.type=$}};if(typeof z0.EventTarget>"u")z0.EventTarget=class{#Z=new Map;addEventListener($,Q){let X=this.#Z.get($)??new Set;X.add(Q),this.#Z.set($,X)}removeEventListener($,Q){this.#Z.get($)?.delete(Q)}dispatchEvent($){for(let Q of this.#Z.get($.type)??[])Q($);return!0}}});var U0=globalThis;if(typeof U0.global>"u")U0.global=globalThis;if(typeof U0.__require>"u")U0.__require=(Z)=>{throw Error(`Dynamic require of "${Z}" is not supported in the sandbox`)};var eX=n7(d9(),1);var Z5=(Z)=>String(Z);Z5.open=Z5.close="";var p1=()=>Z5,l="\x1B[39m",p="\x1B[49m",r7=(Z,$,Q,X,J)=>{let W="",G=0;do W+=Z.substring(G,X)+Q,G=X+J,X=Z.indexOf($,G);while(~X);return W+Z.substring(G)},l9=(Z,$,Q=Z,X=Z.length)=>{let J=$.length,W=(G)=>{let Y=G+"",z=Y.indexOf($,X);return~z?Z+r7(Y,$,Q,z,J)+$:Z+Y+$};return W.open=Z,W.close=$,W},K0=(Z,$)=>l9(Z,$,Z,5),c9=(Z)=>{let $=Z.charCodeAt(0)===35?Z.slice(1):Z,Q=parseInt($,16)||0;return $.length<6?`${(Q>>8&15)*17};${(Q>>4&15)*17};${(Q&15)*17}`:`${Q>>16&255};${Q>>8&255};${Q&255}`};function p9(Z){let $=Z?l9:p1;return{isColorSupported:Z,reset:$("\x1B[0m","\x1B[0m"),bold:$("\x1B[1m","\x1B[22m","\x1B[22m\x1B[1m"),dim:$("\x1B[2m","\x1B[22m","\x1B[22m\x1B[2m"),italic:$("\x1B[3m","\x1B[23m"),underline:$("\x1B[4m","\x1B[24m"),inverse:$("\x1B[7m","\x1B[27m"),hidden:$("\x1B[8m","\x1B[28m"),strikethrough:$("\x1B[9m","\x1B[29m"),black:$("\x1B[30m",l),red:$("\x1B[31m",l),green:$("\x1B[32m",l),yellow:$("\x1B[33m",l),blue:$("\x1B[34m",l),magenta:$("\x1B[35m",l),cyan:$("\x1B[36m",l),white:$("\x1B[37m",l),gray:$("\x1B[90m",l),bgBlack:$("\x1B[40m",p),bgRed:$("\x1B[41m",p),bgGreen:$("\x1B[42m",p),bgYellow:$("\x1B[43m",p),bgBlue:$("\x1B[44m",p),bgMagenta:$("\x1B[45m",p),bgCyan:$("\x1B[46m",p),bgWhite:$("\x1B[47m",p),blackBright:$("\x1B[90m",l),redBright:$("\x1B[91m",l),greenBright:$("\x1B[92m",l),yellowBright:$("\x1B[93m",l),blueBright:$("\x1B[94m",l),magentaBright:$("\x1B[95m",l),cyanBright:$("\x1B[96m",l),whiteBright:$("\x1B[97m",l),bgBlackBright:$("\x1B[100m",p),bgRedBright:$("\x1B[101m",p),bgGreenBright:$("\x1B[102m",p),bgYellowBright:$("\x1B[103m",p),bgBlueBright:$("\x1B[104m",p),bgMagentaBright:$("\x1B[105m",p),bgCyanBright:$("\x1B[106m",p),bgWhiteBright:$("\x1B[107m",p),rgb:Z?(Q,X,J)=>K0(`\x1B[38;2;${Q};${X};${J}m`,l):p1,bgRgb:Z?(Q,X,J)=>K0(`\x1B[48;2;${Q};${X};${J}m`,p):p1,hex:Z?(Q)=>K0(`\x1B[38;2;${c9(Q)}m`,l):p1,bgHex:Z?(Q)=>K0(`\x1B[48;2;${c9(Q)}m`,p):p1}}function a7(){return p9(!1)}function s7(){let Z=typeof process<"u"?process:void 0,$=(Z==null?void 0:Z.env)||{},Q=$.FORCE_TTY!=="false",X=(Z==null?void 0:Z.argv)||[];return!(("NO_COLOR"in $)||X.includes("--no-color"))&&(("FORCE_COLOR"in $)||X.includes("--color")||(Z==null?void 0:Z.platform)==="win32"||Q&&$.TERM!=="dumb"||("CI"in $))||typeof window<"u"&&!!window.chrome}function t7({force:Z}={}){let $=Z||s7();return p9($)}var i9=t7();function n9(){Object.assign(i9,a7())}var f=i9;var D5=Object.defineProperty,e7=(Z,$,Q)=>($ in Z)?D5(Z,$,{enumerable:!0,configurable:!0,writable:!0,value:Q}):Z[$]=Q,F=(Z,$)=>D5(Z,"name",{value:$,configurable:!0}),R5=(Z,$)=>{for(var Q in $)D5(Z,Q,{get:$[Q],enumerable:!0})},X8=(Z,$,Q)=>e7(Z,typeof $!=="symbol"?$+"":$,Q),K1={};R5(K1,{addChainableMethod:()=>E5,addLengthGuard:()=>s1,addMethod:()=>S5,addProperty:()=>k5,checkError:()=>J1,compareByInspect:()=>q0,eql:()=>j8,events:()=>M0,expectTypes:()=>K8,flag:()=>k,getActual:()=>R0,getMessage:()=>O5,getName:()=>O0,getOperator:()=>y5,getOwnEnumerableProperties:()=>h5,getOwnEnumerablePropertySymbols:()=>v5,getPathInfo:()=>T5,hasProperty:()=>F0,inspect:()=>_,isNaN:()=>H0,isNumeric:()=>i,isProxyEnabled:()=>a1,isRegExp:()=>P0,objDisplay:()=>_1,overwriteChainableMethod:()=>x5,overwriteMethod:()=>j5,overwriteProperty:()=>_5,proxify:()=>u1,test:()=>F5,transferFlags:()=>V1,type:()=>h});var J1={};R5(J1,{compatibleConstructor:()=>G8,compatibleInstance:()=>W8,compatibleMessage:()=>B8,getConstructorName:()=>Y8,getMessage:()=>U8});function D0(Z){return Z instanceof Error||Object.prototype.toString.call(Z)==="[object Error]"}F(D0,"isErrorInstance");function J8(Z){return Object.prototype.toString.call(Z)==="[object RegExp]"}F(J8,"isRegExp");function W8(Z,$){return D0($)&&Z===$}F(W8,"compatibleInstance");function G8(Z,$){if(D0($))return Z.constructor===$.constructor||Z instanceof $.constructor;else if((typeof $==="object"||typeof $==="function")&&$.prototype)return Z.constructor===$||Z instanceof $;return!1}F(G8,"compatibleConstructor");function B8(Z,$){let Q=typeof Z==="string"?Z:Z.message;if(Q===void 0)return!1;if(J8($))return $.test(Q);else if(typeof $==="string")return Q.indexOf($)!==-1;return!1}F(B8,"compatibleMessage");function Y8(Z){let $=Z;if(D0(Z))$=Z.constructor.name;else if(typeof Z==="function"){if($=Z.name,$==="")$=new Z().name||$}return $}F(Y8,"getConstructorName");function U8(Z){let $="";if(Z&&Z.message)$=Z.message;else if(typeof Z==="string")$=Z;return $}F(U8,"getMessage");function k(Z,$,Q){let X=Z.__flags||(Z.__flags=Object.create(null));if(arguments.length===3)X[$]=Q;else return X[$]}F(k,"flag");function F5(Z,$){let Q=k(Z,"negate"),X=$[0];return Q?!X:X}F(F5,"test");function h(Z){if(typeof Z>"u")return"undefined";if(Z===null)return"null";let $=Z[Symbol.toStringTag];if(typeof $==="string")return $;return Object.prototype.toString.call(Z).slice(8,-1)}F(h,"type");var ZZ="captureStackTrace"in Error,z8=class Z extends Error{constructor($="Unspecified AssertionError",Q,X){super($);if(X8(this,"message"),this.message=$,ZZ)Error.captureStackTrace(this,X||Z);for(let J in Q)if(!(J in this))this[J]=Q[J]}get name(){return"AssertionError"}get ok(){return!1}toJSON($){return{...this,name:this.name,message:this.message,ok:!1,stack:$!==!1?this.stack:void 0}}};F(z8,"AssertionError");var j=z8;function K8(Z,$){let Q=k(Z,"message"),X=k(Z,"ssfi");Q=Q?Q+": ":"",Z=k(Z,"object"),$=$.map(function(G){return G.toLowerCase()}),$.sort();let J=$.map(function(G,Y){let z=~["a","e","i","o","u"].indexOf(G.charAt(0))?"an":"a";return($.length>1&&Y===$.length-1?"or ":"")+z+" "+G}).join(", "),W=h(Z).toLowerCase();if(!$.some(function(G){return W===G}))throw new j(Q+"object tested must be "+J+", but "+W+" given",void 0,X)}F(K8,"expectTypes");function R0(Z,$){return $.length>4?$[4]:Z._obj}F(R0,"getActual");var o9={bold:["1","22"],dim:["2","22"],italic:["3","23"],underline:["4","24"],inverse:["7","27"],hidden:["8","28"],strike:["9","29"],black:["30","39"],red:["31","39"],green:["32","39"],yellow:["33","39"],blue:["34","39"],magenta:["35","39"],cyan:["36","39"],white:["37","39"],brightblack:["30;1","39"],brightred:["31;1","39"],brightgreen:["32;1","39"],brightyellow:["33;1","39"],brightblue:["34;1","39"],brightmagenta:["35;1","39"],brightcyan:["36;1","39"],brightwhite:["37;1","39"],grey:["90","39"]},$Z={special:"cyan",number:"yellow",bigint:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",symbol:"green",date:"magenta",regexp:"red"},y1="…";function V8(Z,$){let Q=o9[$Z[$]]||o9[$]||"";if(!Q)return String(Z);return`\x1B[${Q[0]}m${String(Z)}\x1B[${Q[1]}m`}F(V8,"colorise");function w8({showHidden:Z=!1,depth:$=2,colors:Q=!1,customInspect:X=!0,showProxy:J=!1,maxArrayLength:W=1/0,breakLength:G=1/0,seen:Y=[],truncate:z=1/0,stylize:B=String}={},U){let K={showHidden:Boolean(Z),depth:Number($),colors:Boolean(Q),customInspect:Boolean(X),showProxy:Boolean(J),maxArrayLength:Number(W),breakLength:Number(G),truncate:Number(z),seen:Y,inspect:U,stylize:B};if(K.colors)K.stylize=V8;return K}F(w8,"normaliseOptions");function q8(Z){return Z>="\uD800"&&Z<="\uDBFF"}F(q8,"isHighSurrogate");function R1(Z,$,Q=y1){Z=String(Z);let X=Q.length,J=Z.length;if(X>$&&J>X)return Q;if(J>$&&J>X){let W=$-X;if(W>0&&q8(Z[W-1]))W=W-1;return`${Z.slice(0,W)}${Q}`}return Z}F(R1,"truncate");function B1(Z,$,Q,X=", "){Q=Q||$.inspect;let J=Z.length;if(J===0)return"";let W=$.truncate,G="",Y="",z="";for(let B=0;BW&&G.length+z.length<=W)break;if(!U&&!K&&H>W)break;if(Y=U?"":Q(Z[B+1],$)+(K?"":X),!U&&K&&H>W&&N+Y.length>W)break;if(G+=w,!U&&!K&&N+Y.length>=W){z=`${y1}(${Z.length-B-1})`;break}z=""}return`${G}${z}`}F(B1,"inspectList");function H8(Z){if(Z.match(/^[a-zA-Z_][a-zA-Z_0-9]*$/))return Z;return JSON.stringify(Z).replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'")}F(H8,"quoteComplexKey");function b1([Z,$],Q){if(Q.truncate-=2,typeof Z==="string")Z=H8(Z);else if(typeof Z!=="number")Z=`[${Q.inspect(Z,Q)}]`;return Q.truncate-=Z.length,$=Q.inspect($,Q),`${Z}: ${$}`}F(b1,"inspectProperty");function P8(Z,$){let Q=Object.keys(Z).slice(Z.length);if(!Z.length&&!Q.length)return"[]";$.truncate-=4;let X=B1(Z,$);$.truncate-=X.length;let J="";if(Q.length)J=B1(Q.map((W)=>[W,Z[W]]),$,b1);return`[ ${X}${J?`, ${J}`:""} ]`}F(P8,"inspectArray");var QZ=F((Z)=>{if(typeof Buffer==="function"&&Z instanceof Buffer)return"Buffer";if(Z[Symbol.toStringTag])return Z[Symbol.toStringTag];return Z.constructor.name},"getArrayName");function P1(Z,$){let Q=QZ(Z);$.truncate-=Q.length+4;let X=Object.keys(Z).slice(Z.length);if(!Z.length&&!X.length)return`${Q}[]`;let J="";for(let G=0;G[G,Z[G]]),$,b1);return`${Q}[ ${J}${W?`, ${W}`:""} ]`}F(P1,"inspectTypedArray");function N8(Z,$){let Q=Z.toJSON();if(Q===null)return"Invalid Date";let X=Q.split("T"),J=X[0];return $.stylize(`${J}T${R1(X[1],$.truncate-J.length-1)}`,"date")}F(N8,"inspectDate");function J5(Z,$){let Q=Z[Symbol.toStringTag]||"Function",X=Z.name;if(!X)return $.stylize(`[${Q}]`,"special");return $.stylize(`[${Q} ${R1(X,$.truncate-11)}]`,"special")}F(J5,"inspectFunction");function D8([Z,$],Q){return Q.truncate-=4,Z=Q.inspect(Z,Q),Q.truncate-=Z.length,$=Q.inspect($,Q),`${Z} => ${$}`}F(D8,"inspectMapEntry");function R8(Z){let $=[];return Z.forEach((Q,X)=>{$.push([X,Q])}),$}F(R8,"mapToEntries");function F8(Z,$){if(Z.size===0)return"Map{}";return $.truncate-=7,`Map{ ${B1(R8(Z),$,D8)} }`}F(F8,"inspectMap");var XZ=Number.isNaN||((Z)=>Z!==Z);function W5(Z,$){if(XZ(Z))return $.stylize("NaN","number");if(Z===1/0)return $.stylize("Infinity","number");if(Z===-1/0)return $.stylize("-Infinity","number");if(Z===0)return $.stylize(1/Z===1/0?"+0":"-0","number");return $.stylize(R1(String(Z),$.truncate),"number")}F(W5,"inspectNumber");function G5(Z,$){let Q=R1(Z.toString(),$.truncate-1);if(Q!==y1)Q+="n";return $.stylize(Q,"bigint")}F(G5,"inspectBigInt");function M8(Z,$){let Q=Z.toString().split("/")[2],X=$.truncate-(2+Q.length),J=Z.source;return $.stylize(`/${R1(J,X)}/${Q}`,"regexp")}F(M8,"inspectRegExp");function O8(Z){let $=[];return Z.forEach((Q)=>{$.push(Q)}),$}F(O8,"arrayFromSet");function I8(Z,$){if(Z.size===0)return"Set{}";return $.truncate-=7,`Set{ ${B1(O8(Z),$)} }`}F(I8,"inspectSet");var r9=new RegExp("['\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]","g"),JZ={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r","'":"\\'","\\":"\\\\"},WZ=16,GZ=4;function C8(Z){return JZ[Z]||`\\u${`0000${Z.charCodeAt(0).toString(WZ)}`.slice(-GZ)}`}F(C8,"escape");function B5(Z,$){if(r9.test(Z))Z=Z.replace(r9,C8);return $.stylize(`'${R1(Z,$.truncate-2)}'`,"string")}F(B5,"inspectString");function Y5(Z){if("description"in Symbol.prototype)return Z.description?`Symbol(${Z.description})`:"Symbol()";return Z.toString()}F(Y5,"inspectSymbol");var BZ=F(()=>"Promise{…}","getPromiseValue"),YZ=BZ;function n1(Z,$){let Q=Object.getOwnPropertyNames(Z),X=Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(Z):[];if(Q.length===0&&X.length===0)return"{}";if($.truncate-=4,$.seen=$.seen||[],$.seen.includes(Z))return"[Circular]";$.seen.push(Z);let J=B1(Q.map((Y)=>[Y,Z[Y]]),$,b1),W=B1(X.map((Y)=>[Y,Z[Y]]),$,b1);$.seen.pop();let G="";if(J&&W)G=", ";return`{ ${J}${G}${W} }`}F(n1,"inspectObject");var $5=typeof Symbol<"u"&&Symbol.toStringTag?Symbol.toStringTag:!1;function L8(Z,$){let Q="";if($5&&$5 in Z)Q=Z[$5];if(Q=Q||Z.constructor.name,!Q||Q==="_class")Q="";return $.truncate-=Q.length,`${Q}${n1(Z,$)}`}F(L8,"inspectClass");function T8(Z,$){if(Z.length===0)return"Arguments[]";return $.truncate-=13,`Arguments[ ${B1(Z,$)} ]`}F(T8,"inspectArguments");var UZ=["stack","line","column","name","message","fileName","lineNumber","columnNumber","number","description","cause"];function A8(Z,$){let Q=Object.getOwnPropertyNames(Z).filter((G)=>UZ.indexOf(G)===-1),X=Z.name;$.truncate-=X.length;let J="";if(typeof Z.message==="string")J=R1(Z.message,$.truncate);else Q.unshift("message");if(J=J?`: ${J}`:"",$.truncate-=J.length+5,$.seen=$.seen||[],$.seen.includes(Z))return"[Circular]";$.seen.push(Z);let W=B1(Q.map((G)=>[G,Z[G]]),$,b1);return`${X}${J}${W?` { ${W} }`:""}`}F(A8,"inspectObject");function k8([Z,$],Q){if(Q.truncate-=3,!$)return`${Q.stylize(String(Z),"yellow")}`;return`${Q.stylize(String(Z),"yellow")}=${Q.stylize(`"${$}"`,"string")}`}F(k8,"inspectAttribute");function V0(Z,$){return B1(Z,$,S8,` +`)}F(V0,"inspectNodeCollection");function S8(Z,$){switch(Z.nodeType){case 1:return M5(Z,$);case 3:return $.inspect(Z.data,$);default:return $.inspect(Z,$)}}F(S8,"inspectNode");function M5(Z,$){let Q=Z.getAttributeNames(),X=Z.tagName.toLowerCase(),J=$.stylize(`<${X}`,"special"),W=$.stylize(">","special"),G=$.stylize(``,"special");$.truncate-=X.length*2+5;let Y="";if(Q.length>0)Y+=" ",Y+=B1(Q.map((U)=>[U,Z.getAttribute(U)]),$,k8," ");$.truncate-=Y.length;let z=$.truncate,B=V0(Z.children,$);if(B&&B.length>z)B=`${y1}(${Z.children.length})`;return`${J}${Y}${W}${B}${G}`}F(M5,"inspectHTML");var zZ=typeof Symbol==="function"&&typeof Symbol.for==="function",Q5=zZ?Symbol.for("chai/inspect"):"@@chai/inspect",X5=Symbol.for("nodejs.util.inspect.custom"),a9=new WeakMap,s9={},t9={undefined:F((Z,$)=>$.stylize("undefined","undefined"),"undefined"),null:F((Z,$)=>$.stylize("null","null"),"null"),boolean:F((Z,$)=>$.stylize(String(Z),"boolean"),"boolean"),Boolean:F((Z,$)=>$.stylize(String(Z),"boolean"),"Boolean"),number:W5,Number:W5,bigint:G5,BigInt:G5,string:B5,String:B5,function:J5,Function:J5,symbol:Y5,Symbol:Y5,Array:P8,Date:N8,Map:F8,Set:I8,RegExp:M8,Promise:YZ,WeakSet:F((Z,$)=>$.stylize("WeakSet{…}","special"),"WeakSet"),WeakMap:F((Z,$)=>$.stylize("WeakMap{…}","special"),"WeakMap"),Arguments:T8,Int8Array:P1,Uint8Array:P1,Uint8ClampedArray:P1,Int16Array:P1,Uint16Array:P1,Int32Array:P1,Uint32Array:P1,Float32Array:P1,Float64Array:P1,Generator:F(()=>"","Generator"),DataView:F(()=>"","DataView"),ArrayBuffer:F(()=>"","ArrayBuffer"),Error:A8,HTMLCollection:V0,NodeList:V0},KZ=F((Z,$,Q,X)=>{if(Q5 in Z&&typeof Z[Q5]==="function")return Z[Q5]($);if(X5 in Z&&typeof Z[X5]==="function")return Z[X5]($.depth,$,X);if("inspect"in Z&&typeof Z.inspect==="function")return Z.inspect($.depth,$);if("constructor"in Z&&a9.has(Z.constructor))return a9.get(Z.constructor)(Z,$);if(s9[Q])return s9[Q](Z,$);return""},"inspectCustom"),VZ=Object.prototype.toString;function o1(Z,$={}){let Q=w8($,o1),{customInspect:X}=Q,J=Z===null?"null":typeof Z;if(J==="object")J=VZ.call(Z).slice(8,-1);if(J in t9)return t9[J](Z,Q);if(X&&Z){let G=KZ(Z,Q,J,o1);if(G){if(typeof G==="string")return G;return o1(G,Q)}}let W=Z?Object.getPrototypeOf(Z):!1;if(W===Object.prototype||W===null)return n1(Z,Q);if(Z&&typeof HTMLElement==="function"&&Z instanceof HTMLElement)return M5(Z,Q);if("constructor"in Z){if(Z.constructor!==Object)return L8(Z,Q);return n1(Z,Q)}if(Z===Object(Z))return n1(Z,Q);return Q.stylize(String(Z),J)}F(o1,"inspect");var $1={includeStack:!1,showDiff:!0,truncateThreshold:40,useProxy:!0,proxyExcludedKeys:["then","catch","inspect","toJSON"],deepEqual:null};function _(Z,$,Q,X){let J={colors:X,depth:typeof Q>"u"?2:Q,showHidden:$,truncate:$1.truncateThreshold?$1.truncateThreshold:1/0};return o1(Z,J)}F(_,"inspect");function _1(Z){let $=_(Z),Q=Object.prototype.toString.call(Z);if($1.truncateThreshold&&$.length>=$1.truncateThreshold)if(Q==="[object Function]"){let X=Z;return!X.name||X.name===""?"[Function]":"[Function: "+X.name+"]"}else if(Q==="[object Array]")return"[ Array("+Z.length+") ]";else if(Q==="[object Object]"){let J=Object.keys(Z);return"{ Object ("+(J.length>2?J.splice(0,2).join(", ")+", ...":J.join(", "))+") }"}else return $;else return $}F(_1,"objDisplay");function O5(Z,$){let Q=k(Z,"negate"),X=k(Z,"object"),J=$[3],W=R0(Z,$),G=Q?$[2]:$[1],Y=k(Z,"message");if(typeof G==="function")G=G();let B=(G||"").replace(/#\{(this|act|exp)\}/g,function(U,K){if(K==="this")return _1(X);if(K==="act")return _1(W);return _1(J)});return Y?Y+": "+B:B}F(O5,"getMessage");function V1(Z,$,Q){let X=Z,J=$,W=X.__flags||(X.__flags=Object.create(null)),G=J.__flags||(J.__flags=Object.create(null));Q=arguments.length===3?Q:!0;for(let Y in W)if(Q||Y!=="object"&&Y!=="ssfi"&&Y!=="lockSsfi"&&Y!="message")G[Y]=W[Y]}F(V1,"transferFlags");function U5(Z){if(typeof Z>"u")return"undefined";if(Z===null)return"null";let $=Z[Symbol.toStringTag];if(typeof $==="string")return $;let Q=8,X=-1;return Object.prototype.toString.call(Z).slice(Q,X)}F(U5,"type");function I5(){this._key="chai/deep-eql__"+Math.random()+Date.now()}F(I5,"FakeMap");I5.prototype={get:F(function($){return $[this._key]},"get"),set:F(function($,Q){if(Object.isExtensible($))Object.defineProperty($,this._key,{value:Q,configurable:!0})},"set")};var _8=typeof WeakMap==="function"?WeakMap:I5;function z5(Z,$,Q){if(!Q||j1(Z)||j1($))return null;var X=Q.get(Z);if(X){var J=X.get($);if(typeof J==="boolean")return J}return null}F(z5,"memoizeCompare");function i1(Z,$,Q,X){if(!Q||j1(Z)||j1($))return;var J=Q.get(Z);if(J)J.set($,X);else J=new _8,J.set($,X),Q.set(Z,J)}F(i1,"memoizeSet");var j8=r1;function r1(Z,$,Q){if(Q&&Q.comparator)return K5(Z,$,Q);var X=C5(Z,$);if(X!==null)return X;return K5(Z,$,Q)}F(r1,"deepEqual");function C5(Z,$){if(Z===$)return Z!==0||1/Z===1/$;if(Z!==Z&&$!==$)return!0;if(j1(Z)||j1($))return!1;return null}F(C5,"simpleEqual");function K5(Z,$,Q){Q=Q||{},Q.memoize=Q.memoize===!1?!1:Q.memoize||new _8;var X=Q&&Q.comparator,J=z5(Z,$,Q.memoize);if(J!==null)return J;var W=z5($,Z,Q.memoize);if(W!==null)return W;if(X){var G=X(Z,$);if(G===!1||G===!0)return i1(Z,$,Q.memoize,G),G;var Y=C5(Z,$);if(Y!==null)return Y}var z=U5(Z);if(z!==U5($))return i1(Z,$,Q.memoize,!1),!1;i1(Z,$,Q.memoize,!0);var B=E8(Z,$,z,Q);return i1(Z,$,Q.memoize,B),B}F(K5,"extensiveDeepEqual");function E8(Z,$,Q,X){switch(Q){case"String":case"Number":case"Boolean":case"Date":return r1(Z.valueOf(),$.valueOf());case"Promise":case"Symbol":case"function":case"WeakMap":case"WeakSet":return Z===$;case"Error":return L5(Z,$,["name","message","code"],X);case"Arguments":case"Int8Array":case"Uint8Array":case"Uint8ClampedArray":case"Int16Array":case"Uint16Array":case"Int32Array":case"Uint32Array":case"Float32Array":case"Float64Array":case"Array":return C1(Z,$,X);case"RegExp":return x8(Z,$);case"Generator":return v8(Z,$,X);case"DataView":return C1(new Uint8Array(Z.buffer),new Uint8Array($.buffer),X);case"ArrayBuffer":return C1(new Uint8Array(Z),new Uint8Array($),X);case"Set":return V5(Z,$,X);case"Map":return V5(Z,$,X);case"Temporal.PlainDate":case"Temporal.PlainTime":case"Temporal.PlainDateTime":case"Temporal.Instant":case"Temporal.ZonedDateTime":case"Temporal.PlainYearMonth":case"Temporal.PlainMonthDay":return Z.equals($);case"Temporal.Duration":return Z.total("nanoseconds")===$.total("nanoseconds");case"Temporal.TimeZone":case"Temporal.Calendar":return Z.toString()===$.toString();default:return y8(Z,$,X)}}F(E8,"extensiveDeepEqualByType");function x8(Z,$){return Z.toString()===$.toString()}F(x8,"regexpEqual");function V5(Z,$,Q){try{if(Z.size!==$.size)return!1;if(Z.size===0)return!0}catch(W){return!1}var X=[],J=[];return Z.forEach(F(function(G,Y){X.push([G,Y])},"gatherEntries")),$.forEach(F(function(G,Y){J.push([G,Y])},"gatherEntries")),C1(X.sort(),J.sort(),Q)}F(V5,"entriesEqual");function C1(Z,$,Q){var X=Z.length;if(X!==$.length)return!1;if(X===0)return!0;var J=-1;while(++J"u"||Z===null)return!1;return $ in Object(Z)}F(F0,"hasProperty");function b8(Z){return Z.replace(/([^\\])\[/g,"$1.[").match(/(\\\.|[^.]+?)+/g).map((X)=>{if(X==="constructor"||X==="__proto__"||X==="prototype")return{};let W=/^\[(\d+)\]$/.exec(X),G=null;if(W)G={i:parseFloat(W[1])};else G={p:X.replace(/\\([.[\]])/g,"$1")};return G})}F(b8,"parsePath");function N5(Z,$,Q){let X=Z,J=null;Q=typeof Q>"u"?$.length:Q;for(let W=0;W"u")X=X[G.i];else X=X[G.p];if(W===Q-1)J=X}}return J}F(N5,"internalGetPathValue");function T5(Z,$){let Q=b8($),X=Q[Q.length-1],J={parent:Q.length>1?N5(Z,Q,Q.length-1):Z,name:X.p||X.i,value:N5(Z,Q)};return J.exists=F0(J.parent,J.name),J}F(T5,"getPathInfo");var u8=class Z{constructor($,Q,X,J){return X8(this,"__flags",{}),k(this,"ssfi",X||Z),k(this,"lockSsfi",J),k(this,"object",$),k(this,"message",Q),k(this,"eql",$1.deepEqual||j8),u1(this)}static get includeStack(){return console.warn("Assertion.includeStack is deprecated, use chai.config.includeStack instead."),$1.includeStack}static set includeStack($){console.warn("Assertion.includeStack is deprecated, use chai.config.includeStack instead."),$1.includeStack=$}static get showDiff(){return console.warn("Assertion.showDiff is deprecated, use chai.config.showDiff instead."),$1.showDiff}static set showDiff($){console.warn("Assertion.showDiff is deprecated, use chai.config.showDiff instead."),$1.showDiff=$}static addProperty($,Q){k5(this.prototype,$,Q)}static addMethod($,Q){S5(this.prototype,$,Q)}static addChainableMethod($,Q,X){E5(this.prototype,$,Q,X)}static overwriteProperty($,Q){_5(this.prototype,$,Q)}static overwriteMethod($,Q){j5(this.prototype,$,Q)}static overwriteChainableMethod($,Q,X){x5(this.prototype,$,Q,X)}assert($,Q,X,J,W,G){let Y=F5(this,arguments);if(G!==!1)G=!0;if(J===void 0&&W===void 0)G=!1;if($1.showDiff!==!0)G=!1;if(!Y){Q=O5(this,arguments);let B={actual:R0(this,arguments),expected:J,showDiff:G},U=y5(this,arguments);if(U)B.operator=U;throw new j(Q,B,$1.includeStack?this.assert:k(this,"ssfi"))}}get _obj(){return k(this,"object")}set _obj($){k(this,"object",$)}};F(u8,"Assertion");var q=u8,M0=new EventTarget,f8=class extends Event{constructor($,Q,X){super($);this.name=String(Q),this.fn=X}};F(f8,"PluginEvent");var A5=f8;function a1(){return $1.useProxy&&typeof Proxy<"u"&&typeof Reflect<"u"}F(a1,"isProxyEnabled");function k5(Z,$,Q){Q=Q===void 0?function(){}:Q,Object.defineProperty(Z,$,{get:F(function X(){if(!a1()&&!k(this,"lockSsfi"))k(this,"ssfi",X);let J=Q.call(this);if(J!==void 0)return J;let W=new q;return V1(this,W),W},"propertyGetter"),configurable:!0}),M0.dispatchEvent(new A5("addProperty",$,Q))}F(k5,"addProperty");var wZ=Object.getOwnPropertyDescriptor(function(){},"length");function s1(Z,$,Q){if(!wZ?.configurable)return Z;return Object.defineProperty(Z,"length",{get:F(function(){if(Q)throw Error("Invalid Chai property: "+$+'.length. Due to a compatibility issue, "length" cannot directly follow "'+$+'". Use "'+$+'.lengthOf" instead.');throw Error("Invalid Chai property: "+$+'.length. See docs for proper usage of "'+$+'".')},"get")}),Z}F(s1,"addLengthGuard");function g8(Z){let $=Object.getOwnPropertyNames(Z);function Q(J){if($.indexOf(J)===-1)$.push(J)}F(Q,"addProperty");let X=Object.getPrototypeOf(Z);while(X!==null)Object.getOwnPropertyNames(X).forEach(Q),X=Object.getPrototypeOf(X);return $}F(g8,"getProperties");var e9=["__flags","__methods","_obj","assert"];function u1(Z,$){if(!a1())return Z;return new Proxy(Z,{get:F(function Q(X,J){if(typeof J==="string"&&$1.proxyExcludedKeys.indexOf(J)===-1&&!Reflect.has(X,J)){if($)throw Error("Invalid Chai property: "+String($)+"."+J+'. See docs for proper usage of "'+String($)+'".');let W=null,G=4;if(g8(X).forEach(function(Y){if(!Object.prototype.hasOwnProperty(Y)&&e9.indexOf(Y)===-1){let z=m8(J,Y,G);if(z=Q)return Q;let X=[];for(let J=0;J<=Z.length;J++)X[J]=Array($.length+1).fill(0),X[J][0]=J;for(let J=0;J<$.length;J++)X[0][J]=J;for(let J=1;J<=Z.length;J++){let W=Z.charCodeAt(J-1);for(let G=1;G<=$.length;G++){if(Math.abs(J-G)>=Q){X[J][G]=Q;continue}X[J][G]=Math.min(X[J-1][G]+1,X[J][G-1]+1,X[J-1][G-1]+(W===$.charCodeAt(G-1)?0:1))}}return X[Z.length][$.length]}F(m8,"stringDistanceCapped");function S5(Z,$,Q){let X=F(function(){if(!k(this,"lockSsfi"))k(this,"ssfi",X);let J=Q.apply(this,arguments);if(J!==void 0)return J;let W=new q;return V1(this,W),W},"methodWrapper");s1(X,$,!1),Z[$]=u1(X,$),M0.dispatchEvent(new A5("addMethod",$,Q))}F(S5,"addMethod");function _5(Z,$,Q){let X=Object.getOwnPropertyDescriptor(Z,$),J=F(function(){return},"_super");if(X&&typeof X.get==="function")J=X.get;Object.defineProperty(Z,$,{get:F(function W(){if(!a1()&&!k(this,"lockSsfi"))k(this,"ssfi",W);let G=k(this,"lockSsfi");k(this,"lockSsfi",!0);let Y=Q(J).call(this);if(k(this,"lockSsfi",G),Y!==void 0)return Y;let z=new q;return V1(this,z),z},"overwritingPropertyGetter"),configurable:!0})}F(_5,"overwriteProperty");function j5(Z,$,Q){let X=Z[$],J=F(function(){throw Error(String($)+" is not a function")},"_super");if(X&&typeof X==="function")J=X;function W(){if(!k(this,"lockSsfi"))k(this,"ssfi",W);let G=k(this,"lockSsfi");k(this,"lockSsfi",!0);let Y=Q(J).apply(this,arguments);if(k(this,"lockSsfi",G),Y!==void 0)return Y;let z=new q;return V1(this,z),z}F(W,"overwritingMethodWrapper"),s1(W,String($),!1),Z[$]=u1(W,$)}F(j5,"overwriteMethod");var qZ=typeof Object.setPrototypeOf==="function",Z8=F(function(){},"testFn"),HZ=Object.getOwnPropertyNames(Z8).filter(function(Z){let $=Object.getOwnPropertyDescriptor(Z8,Z);if(typeof $!=="object")return!0;return!$.configurable}),PZ=Function.prototype.call,NZ=Function.prototype.apply,d8=class extends A5{constructor($,Q,X,J){super($,Q,X);this.chainingBehavior=J}};F(d8,"PluginAddChainableMethodEvent");var DZ=d8;function E5(Z,$,Q,X){let J=Z;if(typeof X!=="function")X=F(function(){},"chainingBehavior");let W={method:Q,chainingBehavior:X};if(!J.__methods)J.__methods={};J.__methods[$]=W,Object.defineProperty(Z,$,{get:F(function(){W.chainingBehavior.call(this);function Y(...z){if(!k(this,"lockSsfi"))k(this,"ssfi",Y);let B=W.method.apply(this,z);if(B!==void 0)return B;let U=new q;return V1(this,U),U}if(F(Y,"chainableMethodWrapper"),s1(Y,$,!0),qZ){let z=Object.create(this);z.call=PZ,z.apply=NZ,Object.setPrototypeOf(Y,z)}else Object.getOwnPropertyNames(Z).forEach(function(B){if(HZ.indexOf(B)!==-1)return;let U=Object.getOwnPropertyDescriptor(Z,B);if(U)Object.defineProperty(Y,B,U)});return V1(this,Y),u1(Y)},"chainableMethodGetter"),configurable:!0}),M0.dispatchEvent(new DZ("addChainableMethod",$,Q,X))}F(E5,"addChainableMethod");function x5(Z,$,Q,X){let J=Z.__methods[$],W=J.chainingBehavior;J.chainingBehavior=F(function(){let z=X(W).call(this);if(z!==void 0)return z;let B=new q;return V1(this,B),B},"overwritingChainableMethodGetter");let G=J.method;J.method=F(function(){let z=Q(G).apply(this,arguments);if(z!==void 0)return z;let B=new q;return V1(this,B),B},"overwritingChainableMethodWrapper")}F(x5,"overwriteChainableMethod");function q0(Z,$){return _(Z)<_($)?-1:1}F(q0,"compareByInspect");function v5(Z){if(typeof Object.getOwnPropertySymbols!=="function")return[];return Object.getOwnPropertySymbols(Z).filter(function($){return Object.getOwnPropertyDescriptor(Z,$)?.enumerable})}F(v5,"getOwnEnumerablePropertySymbols");function h5(Z){return Object.keys(Z).concat(v5(Z))}F(h5,"getOwnEnumerableProperties");var H0=Number.isNaN;function c8(Z){let $=h(Z);return["Array","Object","Function"].indexOf($)!==-1}F(c8,"isObjectType");function y5(Z,$){let Q=k(Z,"operator"),X=k(Z,"negate"),J=$[3],W=X?$[2]:$[1];if(Q)return String(Q);if(typeof W==="function")W=W();if(W=W||"",!W)return;let G=String(W);if(/\shave\s/.test(G))return;let Y=c8(J);if(/\snot\s/.test(G))return Y?"notDeepStrictEqual":"notStrictEqual";return Y?"deepStrictEqual":"strictEqual"}F(y5,"getOperator");function O0(Z){return Z.name}F(O0,"getName");function P0(Z){return Object.prototype.toString.call(Z)==="[object RegExp]"}F(P0,"isRegExp");function i(Z){return["Number","BigInt"].includes(h(Z))}F(i,"isNumeric");var{flag:D}=K1;["to","be","been","is","and","has","have","with","that","which","at","of","same","but","does","still","also"].forEach(function(Z){q.addProperty(Z)});q.addProperty("not",function(){D(this,"negate",!0)});q.addProperty("deep",function(){D(this,"deep",!0)});q.addProperty("nested",function(){D(this,"nested",!0)});q.addProperty("own",function(){D(this,"own",!0)});q.addProperty("ordered",function(){D(this,"ordered",!0)});q.addProperty("any",function(){D(this,"any",!0),D(this,"all",!1)});q.addProperty("all",function(){D(this,"all",!0),D(this,"any",!1)});var $8={function:["function","asyncfunction","generatorfunction","asyncgeneratorfunction"],asyncfunction:["asyncfunction","asyncgeneratorfunction"],generatorfunction:["generatorfunction","asyncgeneratorfunction"],asyncgeneratorfunction:["asyncgeneratorfunction"]};function b5(Z,$){if($)D(this,"message",$);Z=Z.toLowerCase();let Q=D(this,"object"),X=~["a","e","i","o","u"].indexOf(Z.charAt(0))?"an ":"a ",J=h(Q).toLowerCase();if($8.function.includes(Z))this.assert($8[Z].includes(J),"expected #{this} to be "+X+Z,"expected #{this} not to be "+X+Z);else this.assert(Z===J,"expected #{this} to be "+X+Z,"expected #{this} not to be "+X+Z)}F(b5,"an");q.addChainableMethod("an",b5);q.addChainableMethod("a",b5);function l8(Z,$){return H0(Z)&&H0($)||Z===$}F(l8,"SameValueZero");function t1(){D(this,"contains",!0)}F(t1,"includeChainingBehavior");function e1(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=h(Q).toLowerCase(),J=D(this,"message"),W=D(this,"negate"),G=D(this,"ssfi"),Y=D(this,"deep"),z=Y?"deep ":"",B=Y?D(this,"eql"):l8;J=J?J+": ":"";let U=!1;switch(X){case"string":U=Q.indexOf(Z)!==-1;break;case"weakset":if(Y)throw new j(J+"unable to use .deep.include with WeakSet",void 0,G);U=Q.has(Z);break;case"map":Q.forEach(function(K){U=U||B(K,Z)});break;case"set":if(Y)Q.forEach(function(K){U=U||B(K,Z)});else U=Q.has(Z);break;case"array":if(Y)U=Q.some(function(K){return B(K,Z)});else U=Q.indexOf(Z)!==-1;break;default:{if(Z!==Object(Z))throw new j(J+"the given combination of arguments ("+X+" and "+h(Z).toLowerCase()+") is invalid for this assertion. You can use an array, a map, an object, a set, a string, or a weakset instead of a "+h(Z).toLowerCase(),void 0,G);let K=Object.keys(Z),P=null,w=0;if(K.forEach(function(N){let H=new q(Q);if(V1(this,H,!0),D(H,"lockSsfi",!0),!W||K.length===1){H.property(N,Z[N]);return}try{H.property(N,Z[N])}catch(R){if(!J1.compatibleConstructor(R,j))throw R;if(P===null)P=R;w++}},this),W&&K.length>1&&w===K.length)throw P;return}}this.assert(U,"expected #{this} to "+z+"include "+_(Z),"expected #{this} to not "+z+"include "+_(Z))}F(e1,"include");q.addChainableMethod("include",e1,t1);q.addChainableMethod("contain",e1,t1);q.addChainableMethod("contains",e1,t1);q.addChainableMethod("includes",e1,t1);q.addProperty("ok",function(){this.assert(D(this,"object"),"expected #{this} to be truthy","expected #{this} to be falsy")});q.addProperty("true",function(){this.assert(D(this,"object")===!0,"expected #{this} to be true","expected #{this} to be false",D(this,"negate")?!1:!0)});q.addProperty("numeric",function(){let Z=D(this,"object");this.assert(["Number","BigInt"].includes(h(Z)),"expected #{this} to be numeric","expected #{this} to not be numeric",D(this,"negate")?!1:!0)});q.addProperty("callable",function(){let Z=D(this,"object"),$=D(this,"ssfi"),Q=D(this,"message"),X=Q?`${Q}: `:"",J=D(this,"negate"),W=J?`${X}expected ${_(Z)} not to be a callable function`:`${X}expected ${_(Z)} to be a callable function`,G=["Function","AsyncFunction","GeneratorFunction","AsyncGeneratorFunction"].includes(h(Z));if(G&&J||!G&&!J)throw new j(W,void 0,$)});q.addProperty("false",function(){this.assert(D(this,"object")===!1,"expected #{this} to be false","expected #{this} to be true",D(this,"negate")?!0:!1)});q.addProperty("null",function(){this.assert(D(this,"object")===null,"expected #{this} to be null","expected #{this} not to be null")});q.addProperty("undefined",function(){this.assert(D(this,"object")===void 0,"expected #{this} to be undefined","expected #{this} not to be undefined")});q.addProperty("NaN",function(){this.assert(H0(D(this,"object")),"expected #{this} to be NaN","expected #{this} not to be NaN")});function u5(){let Z=D(this,"object");this.assert(Z!==null&&Z!==void 0,"expected #{this} to exist","expected #{this} to not exist")}F(u5,"assertExist");q.addProperty("exist",u5);q.addProperty("exists",u5);q.addProperty("empty",function(){let Z=D(this,"object"),$=D(this,"ssfi"),Q=D(this,"message"),X;switch(Q=Q?Q+": ":"",h(Z).toLowerCase()){case"array":case"string":X=Z.length;break;case"map":case"set":X=Z.size;break;case"weakmap":case"weakset":throw new j(Q+".empty was passed a weak collection",void 0,$);case"function":{let J=Q+".empty was passed a function "+O0(Z);throw new j(J.trim(),void 0,$)}default:if(Z!==Object(Z))throw new j(Q+".empty was passed non-string primitive "+_(Z),void 0,$);X=Object.keys(Z).length}this.assert(X===0,"expected #{this} to be empty","expected #{this} not to be empty")});function f5(){let Z=D(this,"object"),$=h(Z);this.assert($==="Arguments","expected #{this} to be arguments but got "+$,"expected #{this} to not be arguments")}F(f5,"checkArguments");q.addProperty("arguments",f5);q.addProperty("Arguments",f5);function I0(Z,$){if($)D(this,"message",$);let Q=D(this,"object");if(D(this,"deep")){let X=D(this,"lockSsfi");D(this,"lockSsfi",!0),this.eql(Z),D(this,"lockSsfi",X)}else this.assert(Z===Q,"expected #{this} to equal #{exp}","expected #{this} to not equal #{exp}",Z,this._obj,!0)}F(I0,"assertEqual");q.addMethod("equal",I0);q.addMethod("equals",I0);q.addMethod("eq",I0);function g5(Z,$){if($)D(this,"message",$);let Q=D(this,"eql");this.assert(Q(Z,D(this,"object")),"expected #{this} to deeply equal #{exp}","expected #{this} to not deeply equal #{exp}",Z,this._obj,!0)}F(g5,"assertEql");q.addMethod("eql",g5);q.addMethod("eqls",g5);function C0(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=D(this,"doLength"),J=D(this,"message"),W=J?J+": ":"",G=D(this,"ssfi"),Y=h(Q).toLowerCase(),z=h(Z).toLowerCase();if(X&&Y!=="map"&&Y!=="set")new q(Q,J,G,!0).to.have.property("length");if(!X&&Y==="date"&&z!=="date")throw new j(W+"the argument to above must be a date",void 0,G);else if(!i(Z)&&(X||i(Q)))throw new j(W+"the argument to above must be a number",void 0,G);else if(!X&&Y!=="date"&&!i(Q)){let B=Y==="string"?"'"+Q+"'":Q;throw new j(W+"expected "+B+" to be a number or a date",void 0,G)}if(X){let B="length",U;if(Y==="map"||Y==="set")B="size",U=Q.size;else U=Q.length;this.assert(U>Z,"expected #{this} to have a "+B+" above #{exp} but got #{act}","expected #{this} to not have a "+B+" above #{exp}",Z,U)}else this.assert(Q>Z,"expected #{this} to be above #{exp}","expected #{this} to be at most #{exp}",Z)}F(C0,"assertAbove");q.addMethod("above",C0);q.addMethod("gt",C0);q.addMethod("greaterThan",C0);function L0(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=D(this,"doLength"),J=D(this,"message"),W=J?J+": ":"",G=D(this,"ssfi"),Y=h(Q).toLowerCase(),z=h(Z).toLowerCase(),B,U=!0;if(X&&Y!=="map"&&Y!=="set")new q(Q,J,G,!0).to.have.property("length");if(!X&&Y==="date"&&z!=="date")B=W+"the argument to least must be a date";else if(!i(Z)&&(X||i(Q)))B=W+"the argument to least must be a number";else if(!X&&Y!=="date"&&!i(Q)){let K=Y==="string"?"'"+Q+"'":Q;B=W+"expected "+K+" to be a number or a date"}else U=!1;if(U)throw new j(B,void 0,G);if(X){let K="length",P;if(Y==="map"||Y==="set")K="size",P=Q.size;else P=Q.length;this.assert(P>=Z,"expected #{this} to have a "+K+" at least #{exp} but got #{act}","expected #{this} to have a "+K+" below #{exp}",Z,P)}else this.assert(Q>=Z,"expected #{this} to be at least #{exp}","expected #{this} to be below #{exp}",Z)}F(L0,"assertLeast");q.addMethod("least",L0);q.addMethod("gte",L0);q.addMethod("greaterThanOrEqual",L0);function T0(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=D(this,"doLength"),J=D(this,"message"),W=J?J+": ":"",G=D(this,"ssfi"),Y=h(Q).toLowerCase(),z=h(Z).toLowerCase(),B,U=!0;if(X&&Y!=="map"&&Y!=="set")new q(Q,J,G,!0).to.have.property("length");if(!X&&Y==="date"&&z!=="date")B=W+"the argument to below must be a date";else if(!i(Z)&&(X||i(Q)))B=W+"the argument to below must be a number";else if(!X&&Y!=="date"&&!i(Q)){let K=Y==="string"?"'"+Q+"'":Q;B=W+"expected "+K+" to be a number or a date"}else U=!1;if(U)throw new j(B,void 0,G);if(X){let K="length",P;if(Y==="map"||Y==="set")K="size",P=Q.size;else P=Q.length;this.assert(P=Z&&H<=$,"expected #{this} to have a "+N+" within "+w,"expected #{this} to not have a "+N+" within "+w)}else this.assert(X>=Z&&X<=$,"expected #{this} to be within "+w,"expected #{this} to not be within "+w)});function m5(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=D(this,"ssfi"),J=D(this,"message"),W;try{W=Q instanceof Z}catch(Y){if(Y instanceof TypeError)throw J=J?J+": ":"",new j(J+"The instanceof assertion needs a constructor but "+h(Z)+" was given.",void 0,X);throw Y}let G=O0(Z);if(G==null)G="an unnamed constructor";this.assert(W,"expected #{this} to be an instance of "+G,"expected #{this} to not be an instance of "+G)}F(m5,"assertInstanceOf");q.addMethod("instanceof",m5);q.addMethod("instanceOf",m5);function d5(Z,$,Q){if(Q)D(this,"message",Q);let X=D(this,"nested"),J=D(this,"own"),W=D(this,"message"),G=D(this,"object"),Y=D(this,"ssfi"),z=typeof Z;if(W=W?W+": ":"",X){if(z!=="string")throw new j(W+"the argument to property must be a string when using nested syntax",void 0,Y)}else if(z!=="string"&&z!=="number"&&z!=="symbol")throw new j(W+"the argument to property must be a string, number, or symbol",void 0,Y);if(X&&J)throw new j(W+'The "nested" and "own" flags cannot be combined.',void 0,Y);if(G===null||G===void 0)throw new j(W+"Target cannot be null or undefined.",void 0,Y);let B=D(this,"deep"),U=D(this,"negate"),K=X?T5(G,Z):null,P=X?K.value:G[Z],w=B?D(this,"eql"):(R,O)=>R===O,N="";if(B)N+="deep ";if(J)N+="own ";if(X)N+="nested ";N+="property ";let H;if(J)H=Object.prototype.hasOwnProperty.call(G,Z);else if(X)H=K.exists;else H=F0(G,Z);if(!U||arguments.length===1)this.assert(H,"expected #{this} to have "+N+_(Z),"expected #{this} to not have "+N+_(Z));if(arguments.length>1)this.assert(H&&w($,P),"expected #{this} to have "+N+_(Z)+" of #{exp}, but got #{act}","expected #{this} to not have "+N+_(Z)+" of #{act}",$,P);D(this,"object",P)}F(d5,"assertProperty");q.addMethod("property",d5);function c5(Z,$,Q){D(this,"own",!0),d5.apply(this,arguments)}F(c5,"assertOwnProperty");q.addMethod("ownProperty",c5);q.addMethod("haveOwnProperty",c5);function l5(Z,$,Q){if(typeof $==="string")Q=$,$=null;if(Q)D(this,"message",Q);let X=D(this,"object"),J=Object.getOwnPropertyDescriptor(Object(X),Z),W=D(this,"eql");if(J&&$)this.assert(W($,J),"expected the own property descriptor for "+_(Z)+" on #{this} to match "+_($)+", got "+_(J),"expected the own property descriptor for "+_(Z)+" on #{this} to not match "+_($),$,J,!0);else this.assert(J,"expected #{this} to have an own property descriptor for "+_(Z),"expected #{this} to not have an own property descriptor for "+_(Z));D(this,"object",J)}F(l5,"assertOwnPropertyDescriptor");q.addMethod("ownPropertyDescriptor",l5);q.addMethod("haveOwnPropertyDescriptor",l5);function p5(){D(this,"doLength",!0)}F(p5,"assertLengthChain");function i5(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=h(Q).toLowerCase(),J=D(this,"message"),W=D(this,"ssfi"),G="length",Y;switch(X){case"map":case"set":G="size",Y=Q.size;break;default:new q(Q,J,W,!0).to.have.property("length"),Y=Q.length}this.assert(Y==Z,"expected #{this} to have a "+G+" of #{exp} but got #{act}","expected #{this} to not have a "+G+" of #{act}",Z,Y)}F(i5,"assertLength");q.addChainableMethod("length",i5,p5);q.addChainableMethod("lengthOf",i5,p5);function n5(Z,$){if($)D(this,"message",$);let Q=D(this,"object");this.assert(Z.exec(Q),"expected #{this} to match "+Z,"expected #{this} not to match "+Z)}F(n5,"assertMatch");q.addMethod("match",n5);q.addMethod("matches",n5);q.addMethod("string",function(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=D(this,"message"),J=D(this,"ssfi");new q(Q,X,J,!0).is.a("string"),this.assert(~Q.indexOf(Z),"expected #{this} to contain "+_(Z),"expected #{this} to not contain "+_(Z))});function o5(Z){let $=D(this,"object"),Q=h($),X=h(Z),J=D(this,"ssfi"),W=D(this,"deep"),G,Y="",z,B=!0,U=D(this,"message");U=U?U+": ":"";let K=U+"when testing keys against an object or an array you must give a single Array|Object|String argument or multiple String arguments";if(Q==="Map"||Q==="Set"){if(Y=W?"deeply ":"",z=[],$.forEach(function(M,T){z.push(T)}),X!=="Array")Z=Array.prototype.slice.call(arguments)}else{switch(z=h5($),X){case"Array":if(arguments.length>1)throw new j(K,void 0,J);break;case"Object":if(arguments.length>1)throw new j(K,void 0,J);Z=Object.keys(Z);break;default:Z=Array.prototype.slice.call(arguments)}Z=Z.map(function(M){return typeof M==="symbol"?M:String(M)})}if(!Z.length)throw new j(U+"keys required",void 0,J);let P=Z.length,w=D(this,"any"),N=D(this,"all"),H=Z,R=W?D(this,"eql"):(M,T)=>M===T;if(!w&&!N)N=!0;let O=[...new Set(H)];if(w)B=O.some(function(M){return z.some(function(T){return R(M,T)})});if(N){if(B=O.every(function(M){return z.some(function(T){return R(M,T)})}),!D(this,"contains"))B=B&&O.length==z.length}if(P>1){Z=Z.map(function(T){return _(T)});let M=Z.pop();if(N)G=Z.join(", ")+", and "+M;if(w)G=Z.join(", ")+", or "+M}else G=_(Z[0]);G=(P>1?"keys ":"key ")+G,G=(D(this,"contains")?"contain ":"have ")+G,this.assert(B,"expected #{this} to "+Y+G,"expected #{this} to not "+Y+G,H.slice(0).sort(q0),z.sort(q0),!0)}F(o5,"assertKeys");q.addMethod("keys",o5);q.addMethod("key",o5);function k0(Z,$,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),J=D(this,"ssfi"),W=D(this,"message"),G=D(this,"negate")||!1;if(new q(X,W,J,!0).is.a("function"),P0(Z)||typeof Z==="string")$=Z,Z=null;let Y,z=!1;try{X()}catch(w){z=!0,Y=w}let B=Z===void 0&&$===void 0,U=Boolean(Z&&$),K=!1,P=!1;if(B||!B&&!G){let w="an error";if(Z instanceof Error)w="#{exp}";else if(Z)w=J1.getConstructorName(Z);let N=Y;if(Y instanceof Error)N=Y.toString();else if(typeof Y==="string")N=Y;else if(Y&&(typeof Y==="object"||typeof Y==="function"))try{N=J1.getConstructorName(Y)}catch(H){}this.assert(z,"expected #{this} to throw "+w,"expected #{this} to not throw an error but #{act} was thrown",Z&&Z.toString(),N)}if(Z&&Y){if(Z instanceof Error){if(J1.compatibleInstance(Y,Z)===G)if(U&&G)K=!0;else this.assert(G,"expected #{this} to throw #{exp} but #{act} was thrown","expected #{this} to not throw #{exp}"+(Y&&!G?" but #{act} was thrown":""),Z.toString(),Y.toString())}if(J1.compatibleConstructor(Y,Z)===G)if(U&&G)K=!0;else this.assert(G,"expected #{this} to throw #{exp} but #{act} was thrown","expected #{this} to not throw #{exp}"+(Y?" but #{act} was thrown":""),Z instanceof Error?Z.toString():Z&&J1.getConstructorName(Z),Y instanceof Error?Y.toString():Y&&J1.getConstructorName(Y))}if(Y&&$!==void 0&&$!==null){let w="including";if(P0($))w="matching";if(J1.compatibleMessage(Y,$)===G)if(U&&G)P=!0;else this.assert(G,"expected #{this} to throw error "+w+" #{exp} but got #{act}","expected #{this} to throw error not "+w+" #{exp}",$,J1.getMessage(Y))}if(K&&P)this.assert(G,"expected #{this} to throw #{exp} but #{act} was thrown","expected #{this} to not throw #{exp}"+(Y?" but #{act} was thrown":""),Z instanceof Error?Z.toString():Z&&J1.getConstructorName(Z),Y instanceof Error?Y.toString():Y&&J1.getConstructorName(Y));D(this,"object",Y)}F(k0,"assertThrows");q.addMethod("throw",k0);q.addMethod("throws",k0);q.addMethod("Throw",k0);function r5(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=D(this,"itself"),J=typeof Q==="function"&&!X?Q.prototype[Z]:Q[Z];this.assert(typeof J==="function","expected #{this} to respond to "+_(Z),"expected #{this} to not respond to "+_(Z))}F(r5,"respondTo");q.addMethod("respondTo",r5);q.addMethod("respondsTo",r5);q.addProperty("itself",function(){D(this,"itself",!0)});function a5(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=Z(Q);this.assert(X,"expected #{this} to satisfy "+_1(Z),"expected #{this} to not satisfy"+_1(Z),D(this,"negate")?!1:!0,X)}F(a5,"satisfy");q.addMethod("satisfy",a5);q.addMethod("satisfies",a5);function s5(Z,$,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),J=D(this,"message"),W=D(this,"ssfi");new q(X,J,W,!0).is.numeric;let G="A `delta` value is required for `closeTo`";if($==null)throw new j(J?`${J}: ${G}`:G,void 0,W);if(new q($,J,W,!0).is.numeric,G="A `expected` value is required for `closeTo`",Z==null)throw new j(J?`${J}: ${G}`:G,void 0,W);new q(Z,J,W,!0).is.numeric;let Y=F((B)=>B<0?-B:B,"abs"),z=F((B)=>parseFloat(parseFloat(B).toPrecision(12)),"strip");this.assert(z(Y(X-Z))<=$,"expected #{this} to be close to "+Z+" +/- "+$,"expected #{this} not to be close to "+Z+" +/- "+$)}F(s5,"closeTo");q.addMethod("closeTo",s5);q.addMethod("approximately",s5);function p8(Z,$,Q,X,J){let W=Array.from($),G=Array.from(Z);if(!X){if(G.length!==W.length)return!1;W=W.slice()}return G.every(function(Y,z){if(J)return Q?Q(Y,W[z]):Y===W[z];if(!Q){let B=W.indexOf(Y);if(B===-1)return!1;if(!X)W.splice(B,1);return!0}return W.some(function(B,U){if(!Q(Y,B))return!1;if(!X)W.splice(U,1);return!0})})}F(p8,"isSubsetOf");q.addMethod("members",function(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=D(this,"message"),J=D(this,"ssfi");new q(Q,X,J,!0).to.be.iterable,new q(Z,X,J,!0).to.be.iterable;let W=D(this,"contains"),G=D(this,"ordered"),Y,z,B;if(W)Y=G?"an ordered superset":"a superset",z="expected #{this} to be "+Y+" of #{exp}",B="expected #{this} to not be "+Y+" of #{exp}";else Y=G?"ordered members":"members",z="expected #{this} to have the same "+Y+" as #{exp}",B="expected #{this} to not have the same "+Y+" as #{exp}";let U=D(this,"deep")?D(this,"eql"):void 0;this.assert(p8(Z,Q,U,W,G),z,B,Z,Q,!0)});q.addProperty("iterable",function(Z){if(Z)D(this,"message",Z);let $=D(this,"object");this.assert($!=null&&typeof $[Symbol.iterator]==="function","expected #{this} to be an iterable","expected #{this} to not be an iterable",$)});function i8(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=D(this,"message"),J=D(this,"ssfi"),W=D(this,"contains"),G=D(this,"deep"),Y=D(this,"eql");if(new q(Z,X,J,!0).to.be.an("array"),W)this.assert(Z.some(function(z){return Q.indexOf(z)>-1||G&&Array.isArray(Q)&&Q.some(function(B){return Y(B,z)})}),"expected #{this} to "+(G?"deeply ":"")+"contain one of #{exp}","expected #{this} to not "+(G?"deeply ":"")+"contain one of #{exp}",Z,Q);else if(G)this.assert(Z.some(function(z){return Y(Q,z)}),"expected #{this} to deeply equal one of #{exp}","expected #{this} to not deeply equal one of #{exp}",Z,Q);else this.assert(Z.indexOf(Q)>-1,"expected #{this} to be one of #{exp}","expected #{this} to not be one of #{exp}",Z,Q)}F(i8,"oneOf");q.addMethod("oneOf",i8);function t5(Z,$,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),J=D(this,"message"),W=D(this,"ssfi");new q(X,J,W,!0).is.a("function");let G;if($===null||$===void 0)new q(Z,J,W,!0).is.a("function"),G=Z();else new q(Z,J,W,!0).to.have.property($),G=Z[$];X();let Y=$===void 0||$===null?Z():Z[$],z=$===void 0||$===null?G:"."+$;D(this,"deltaMsgObj",z),D(this,"initialDeltaValue",G),D(this,"finalDeltaValue",Y),D(this,"deltaBehavior","change"),D(this,"realDelta",Y!==G),this.assert(G!==Y,"expected "+z+" to change","expected "+z+" to not change")}F(t5,"assertChanges");q.addMethod("change",t5);q.addMethod("changes",t5);function e5(Z,$,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),J=D(this,"message"),W=D(this,"ssfi");new q(X,J,W,!0).is.a("function");let G;if($===null||$===void 0)new q(Z,J,W,!0).is.a("function"),G=Z();else new q(Z,J,W,!0).to.have.property($),G=Z[$];new q(G,J,W,!0).is.a("number"),X();let Y=$===void 0||$===null?Z():Z[$],z=$===void 0||$===null?G:"."+$;D(this,"deltaMsgObj",z),D(this,"initialDeltaValue",G),D(this,"finalDeltaValue",Y),D(this,"deltaBehavior","increase"),D(this,"realDelta",Y-G),this.assert(Y-G>0,"expected "+z+" to increase","expected "+z+" to not increase")}F(e5,"assertIncreases");q.addMethod("increase",e5);q.addMethod("increases",e5);function Z9(Z,$,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),J=D(this,"message"),W=D(this,"ssfi");new q(X,J,W,!0).is.a("function");let G;if($===null||$===void 0)new q(Z,J,W,!0).is.a("function"),G=Z();else new q(Z,J,W,!0).to.have.property($),G=Z[$];new q(G,J,W,!0).is.a("number"),X();let Y=$===void 0||$===null?Z():Z[$],z=$===void 0||$===null?G:"."+$;D(this,"deltaMsgObj",z),D(this,"initialDeltaValue",G),D(this,"finalDeltaValue",Y),D(this,"deltaBehavior","decrease"),D(this,"realDelta",G-Y),this.assert(Y-G<0,"expected "+z+" to decrease","expected "+z+" to not decrease")}F(Z9,"assertDecreases");q.addMethod("decrease",Z9);q.addMethod("decreases",Z9);function n8(Z,$){if($)D(this,"message",$);let Q=D(this,"deltaMsgObj"),X=D(this,"initialDeltaValue"),J=D(this,"finalDeltaValue"),W=D(this,"deltaBehavior"),G=D(this,"realDelta"),Y;if(W==="change")Y=Math.abs(J-X)===Math.abs(Z);else Y=G===Math.abs(Z);this.assert(Y,"expected "+Q+" to "+W+" by "+Z,"expected "+Q+" to not "+W+" by "+Z)}F(n8,"assertDelta");q.addMethod("by",n8);q.addProperty("extensible",function(){let Z=D(this,"object"),$=Z===Object(Z)&&Object.isExtensible(Z);this.assert($,"expected #{this} to be extensible","expected #{this} to not be extensible")});q.addProperty("sealed",function(){let Z=D(this,"object"),$=Z===Object(Z)?Object.isSealed(Z):!0;this.assert($,"expected #{this} to be sealed","expected #{this} to not be sealed")});q.addProperty("frozen",function(){let Z=D(this,"object"),$=Z===Object(Z)?Object.isFrozen(Z):!0;this.assert($,"expected #{this} to be frozen","expected #{this} to not be frozen")});q.addProperty("finite",function(Z){let $=D(this,"object");this.assert(typeof $==="number"&&isFinite($),"expected #{this} to be a finite number","expected #{this} to not be a finite number")});function N0(Z,$){if(Z===$)return!0;if(typeof $!==typeof Z)return!1;if(typeof Z!=="object"||Z===null)return Z===$;if(!$)return!1;if(Array.isArray(Z)){if(!Array.isArray($))return!1;return Z.every(function(Q){return $.some(function(X){return N0(Q,X)})})}if(Z instanceof Date)if($ instanceof Date)return Z.getTime()===$.getTime();else return!1;return Object.keys(Z).every(function(Q){let X=Z[Q],J=$[Q];if(typeof X==="object"&&X!==null&&J!==null)return N0(X,J);if(typeof X==="function")return X(J);return J===X})}F(N0,"compareSubset");q.addMethod("containSubset",function(Z){let $=k(this,"object"),Q=$1.showDiff;this.assert(N0(Z,$),"expected #{act} to contain subset #{exp}","expected #{act} to not contain subset #{exp}",Z,$,Q)});function L1(Z,$){return new q(Z,$)}F(L1,"expect");L1.fail=function(Z,$,Q,X){let J=Q;if(arguments.length<2)J=Z,Z=void 0;throw new j(J||"expect.fail()",{actual:Z,expected:$,operator:X},L1.fail)};var o8={};R5(o8,{Should:()=>FZ,should:()=>RZ});function $9(){function Z(){if(this instanceof String||this instanceof Number||this instanceof Boolean||typeof Symbol==="function"&&this instanceof Symbol||typeof BigInt==="function"&&this instanceof BigInt)return new q(this.valueOf(),null,Z);return new q(this,null,Z)}F(Z,"shouldGetter");function $(X){Object.defineProperty(this,"should",{value:X,enumerable:!0,configurable:!0,writable:!0})}F($,"shouldSetter"),Object.defineProperty(Object.prototype,"should",{set:$,get:Z,configurable:!0});let Q={};return Q.fail=function(X,J,W,G){if(arguments.length<2)W=X,X=void 0;throw W=W||"should.fail()",new j(W,{actual:X,expected:J,operator:G},Q.fail)},Q.equal=function(X,J,W){new q(X,W).to.equal(J)},Q.Throw=function(X,J,W,G){new q(X,G).to.Throw(J,W)},Q.exist=function(X,J){new q(X,J).to.exist},Q.not={},Q.not.equal=function(X,J,W){new q(X,W).to.not.equal(J)},Q.not.Throw=function(X,J,W,G){new q(X,G).to.not.Throw(J,W)},Q.not.exist=function(X,J){new q(X,J).to.not.exist},Q.throw=Q.Throw,Q.not.throw=Q.not.Throw,Q}F($9,"loadShould");var RZ=$9,FZ=$9;function V(Z,$){new q(null,null,V,!0).assert(Z,$,"[ negation message unavailable ]")}F(V,"assert");V.fail=function(Z,$,Q,X){if(arguments.length<2)Q=Z,Z=void 0;throw Q=Q||"assert.fail()",new j(Q,{actual:Z,expected:$,operator:X},V.fail)};V.isOk=function(Z,$){new q(Z,$,V.isOk,!0).is.ok};V.isNotOk=function(Z,$){new q(Z,$,V.isNotOk,!0).is.not.ok};V.equal=function(Z,$,Q){let X=new q(Z,Q,V.equal,!0);X.assert($==k(X,"object"),"expected #{this} to equal #{exp}","expected #{this} to not equal #{act}",$,Z,!0)};V.notEqual=function(Z,$,Q){let X=new q(Z,Q,V.notEqual,!0);X.assert($!=k(X,"object"),"expected #{this} to not equal #{exp}","expected #{this} to equal #{act}",$,Z,!0)};V.strictEqual=function(Z,$,Q){new q(Z,Q,V.strictEqual,!0).to.equal($)};V.notStrictEqual=function(Z,$,Q){new q(Z,Q,V.notStrictEqual,!0).to.not.equal($)};V.deepEqual=V.deepStrictEqual=function(Z,$,Q){new q(Z,Q,V.deepEqual,!0).to.eql($)};V.notDeepEqual=function(Z,$,Q){new q(Z,Q,V.notDeepEqual,!0).to.not.eql($)};V.isAbove=function(Z,$,Q){new q(Z,Q,V.isAbove,!0).to.be.above($)};V.isAtLeast=function(Z,$,Q){new q(Z,Q,V.isAtLeast,!0).to.be.least($)};V.isBelow=function(Z,$,Q){new q(Z,Q,V.isBelow,!0).to.be.below($)};V.isAtMost=function(Z,$,Q){new q(Z,Q,V.isAtMost,!0).to.be.most($)};V.isTrue=function(Z,$){new q(Z,$,V.isTrue,!0).is.true};V.isNotTrue=function(Z,$){new q(Z,$,V.isNotTrue,!0).to.not.equal(!0)};V.isFalse=function(Z,$){new q(Z,$,V.isFalse,!0).is.false};V.isNotFalse=function(Z,$){new q(Z,$,V.isNotFalse,!0).to.not.equal(!1)};V.isNull=function(Z,$){new q(Z,$,V.isNull,!0).to.equal(null)};V.isNotNull=function(Z,$){new q(Z,$,V.isNotNull,!0).to.not.equal(null)};V.isNaN=function(Z,$){new q(Z,$,V.isNaN,!0).to.be.NaN};V.isNotNaN=function(Z,$){new q(Z,$,V.isNotNaN,!0).not.to.be.NaN};V.exists=function(Z,$){new q(Z,$,V.exists,!0).to.exist};V.notExists=function(Z,$){new q(Z,$,V.notExists,!0).to.not.exist};V.isUndefined=function(Z,$){new q(Z,$,V.isUndefined,!0).to.equal(void 0)};V.isDefined=function(Z,$){new q(Z,$,V.isDefined,!0).to.not.equal(void 0)};V.isCallable=function(Z,$){new q(Z,$,V.isCallable,!0).is.callable};V.isNotCallable=function(Z,$){new q(Z,$,V.isNotCallable,!0).is.not.callable};V.isObject=function(Z,$){new q(Z,$,V.isObject,!0).to.be.a("object")};V.isNotObject=function(Z,$){new q(Z,$,V.isNotObject,!0).to.not.be.a("object")};V.isArray=function(Z,$){new q(Z,$,V.isArray,!0).to.be.an("array")};V.isNotArray=function(Z,$){new q(Z,$,V.isNotArray,!0).to.not.be.an("array")};V.isString=function(Z,$){new q(Z,$,V.isString,!0).to.be.a("string")};V.isNotString=function(Z,$){new q(Z,$,V.isNotString,!0).to.not.be.a("string")};V.isNumber=function(Z,$){new q(Z,$,V.isNumber,!0).to.be.a("number")};V.isNotNumber=function(Z,$){new q(Z,$,V.isNotNumber,!0).to.not.be.a("number")};V.isNumeric=function(Z,$){new q(Z,$,V.isNumeric,!0).is.numeric};V.isNotNumeric=function(Z,$){new q(Z,$,V.isNotNumeric,!0).is.not.numeric};V.isFinite=function(Z,$){new q(Z,$,V.isFinite,!0).to.be.finite};V.isBoolean=function(Z,$){new q(Z,$,V.isBoolean,!0).to.be.a("boolean")};V.isNotBoolean=function(Z,$){new q(Z,$,V.isNotBoolean,!0).to.not.be.a("boolean")};V.typeOf=function(Z,$,Q){new q(Z,Q,V.typeOf,!0).to.be.a($)};V.notTypeOf=function(Z,$,Q){new q(Z,Q,V.notTypeOf,!0).to.not.be.a($)};V.instanceOf=function(Z,$,Q){new q(Z,Q,V.instanceOf,!0).to.be.instanceOf($)};V.notInstanceOf=function(Z,$,Q){new q(Z,Q,V.notInstanceOf,!0).to.not.be.instanceOf($)};V.include=function(Z,$,Q){new q(Z,Q,V.include,!0).include($)};V.notInclude=function(Z,$,Q){new q(Z,Q,V.notInclude,!0).not.include($)};V.deepInclude=function(Z,$,Q){new q(Z,Q,V.deepInclude,!0).deep.include($)};V.notDeepInclude=function(Z,$,Q){new q(Z,Q,V.notDeepInclude,!0).not.deep.include($)};V.nestedInclude=function(Z,$,Q){new q(Z,Q,V.nestedInclude,!0).nested.include($)};V.notNestedInclude=function(Z,$,Q){new q(Z,Q,V.notNestedInclude,!0).not.nested.include($)};V.deepNestedInclude=function(Z,$,Q){new q(Z,Q,V.deepNestedInclude,!0).deep.nested.include($)};V.notDeepNestedInclude=function(Z,$,Q){new q(Z,Q,V.notDeepNestedInclude,!0).not.deep.nested.include($)};V.ownInclude=function(Z,$,Q){new q(Z,Q,V.ownInclude,!0).own.include($)};V.notOwnInclude=function(Z,$,Q){new q(Z,Q,V.notOwnInclude,!0).not.own.include($)};V.deepOwnInclude=function(Z,$,Q){new q(Z,Q,V.deepOwnInclude,!0).deep.own.include($)};V.notDeepOwnInclude=function(Z,$,Q){new q(Z,Q,V.notDeepOwnInclude,!0).not.deep.own.include($)};V.match=function(Z,$,Q){new q(Z,Q,V.match,!0).to.match($)};V.notMatch=function(Z,$,Q){new q(Z,Q,V.notMatch,!0).to.not.match($)};V.property=function(Z,$,Q){new q(Z,Q,V.property,!0).to.have.property($)};V.notProperty=function(Z,$,Q){new q(Z,Q,V.notProperty,!0).to.not.have.property($)};V.propertyVal=function(Z,$,Q,X){new q(Z,X,V.propertyVal,!0).to.have.property($,Q)};V.notPropertyVal=function(Z,$,Q,X){new q(Z,X,V.notPropertyVal,!0).to.not.have.property($,Q)};V.deepPropertyVal=function(Z,$,Q,X){new q(Z,X,V.deepPropertyVal,!0).to.have.deep.property($,Q)};V.notDeepPropertyVal=function(Z,$,Q,X){new q(Z,X,V.notDeepPropertyVal,!0).to.not.have.deep.property($,Q)};V.ownProperty=function(Z,$,Q){new q(Z,Q,V.ownProperty,!0).to.have.own.property($)};V.notOwnProperty=function(Z,$,Q){new q(Z,Q,V.notOwnProperty,!0).to.not.have.own.property($)};V.ownPropertyVal=function(Z,$,Q,X){new q(Z,X,V.ownPropertyVal,!0).to.have.own.property($,Q)};V.notOwnPropertyVal=function(Z,$,Q,X){new q(Z,X,V.notOwnPropertyVal,!0).to.not.have.own.property($,Q)};V.deepOwnPropertyVal=function(Z,$,Q,X){new q(Z,X,V.deepOwnPropertyVal,!0).to.have.deep.own.property($,Q)};V.notDeepOwnPropertyVal=function(Z,$,Q,X){new q(Z,X,V.notDeepOwnPropertyVal,!0).to.not.have.deep.own.property($,Q)};V.nestedProperty=function(Z,$,Q){new q(Z,Q,V.nestedProperty,!0).to.have.nested.property($)};V.notNestedProperty=function(Z,$,Q){new q(Z,Q,V.notNestedProperty,!0).to.not.have.nested.property($)};V.nestedPropertyVal=function(Z,$,Q,X){new q(Z,X,V.nestedPropertyVal,!0).to.have.nested.property($,Q)};V.notNestedPropertyVal=function(Z,$,Q,X){new q(Z,X,V.notNestedPropertyVal,!0).to.not.have.nested.property($,Q)};V.deepNestedPropertyVal=function(Z,$,Q,X){new q(Z,X,V.deepNestedPropertyVal,!0).to.have.deep.nested.property($,Q)};V.notDeepNestedPropertyVal=function(Z,$,Q,X){new q(Z,X,V.notDeepNestedPropertyVal,!0).to.not.have.deep.nested.property($,Q)};V.lengthOf=function(Z,$,Q){new q(Z,Q,V.lengthOf,!0).to.have.lengthOf($)};V.hasAnyKeys=function(Z,$,Q){new q(Z,Q,V.hasAnyKeys,!0).to.have.any.keys($)};V.hasAllKeys=function(Z,$,Q){new q(Z,Q,V.hasAllKeys,!0).to.have.all.keys($)};V.containsAllKeys=function(Z,$,Q){new q(Z,Q,V.containsAllKeys,!0).to.contain.all.keys($)};V.doesNotHaveAnyKeys=function(Z,$,Q){new q(Z,Q,V.doesNotHaveAnyKeys,!0).to.not.have.any.keys($)};V.doesNotHaveAllKeys=function(Z,$,Q){new q(Z,Q,V.doesNotHaveAllKeys,!0).to.not.have.all.keys($)};V.hasAnyDeepKeys=function(Z,$,Q){new q(Z,Q,V.hasAnyDeepKeys,!0).to.have.any.deep.keys($)};V.hasAllDeepKeys=function(Z,$,Q){new q(Z,Q,V.hasAllDeepKeys,!0).to.have.all.deep.keys($)};V.containsAllDeepKeys=function(Z,$,Q){new q(Z,Q,V.containsAllDeepKeys,!0).to.contain.all.deep.keys($)};V.doesNotHaveAnyDeepKeys=function(Z,$,Q){new q(Z,Q,V.doesNotHaveAnyDeepKeys,!0).to.not.have.any.deep.keys($)};V.doesNotHaveAllDeepKeys=function(Z,$,Q){new q(Z,Q,V.doesNotHaveAllDeepKeys,!0).to.not.have.all.deep.keys($)};V.throws=function(Z,$,Q,X){if(typeof $==="string"||$ instanceof RegExp)Q=$,$=null;let J=new q(Z,X,V.throws,!0).to.throw($,Q);return k(J,"object")};V.doesNotThrow=function(Z,$,Q,X){if(typeof $==="string"||$ instanceof RegExp)Q=$,$=null;new q(Z,X,V.doesNotThrow,!0).to.not.throw($,Q)};V.operator=function(Z,$,Q,X){let J;switch($){case"==":J=Z==Q;break;case"===":J=Z===Q;break;case">":J=Z>Q;break;case">=":J=Z>=Q;break;case"<":J=ZB instanceof DataView,z=Y(Z)?Z.byteLength:Z.length;if(z>0){G+=$.spacingOuter;let B=Q+$.indent;for(let U=0;U0){Y+=$.spacingOuter;let B=Q+$.indent;for(let U=0;U{let G=Z.toString();if(G==="ArrayContaining"||G==="ArrayNotContaining"){if(++X>$.maxDepth)return`[${G}]`;return`${G+S0}[${E0(Z.sample,$,Q,X,J,W)}]`}if(G==="ObjectContaining"||G==="ObjectNotContaining"){if(++X>$.maxDepth)return`[${G}]`;return`${G+S0}{${v0(Z.sample,$,Q,X,J,W)}}`}if(G==="StringMatching"||G==="StringNotMatching")return G+S0+W(Z.sample,$,Q,X,J);if(G==="StringContaining"||G==="StringNotContaining")return G+S0+W(Z.sample,$,Q,X,J);if(typeof Z.toAsymmetricMatcher!=="function")throw TypeError(`Asymmetric matcher ${Z.constructor.name} does not implement toAsymmetricMatcher()`);return Z.toAsymmetricMatcher()},AZ=(Z)=>Z&&Z.$$typeof===LZ,kZ={serialize:TZ,test:AZ},SZ=" ",U6=new Set(["DOMStringMap","NamedNodeMap"]),_Z=/^(?:HTML\w*Collection|NodeList)$/;function jZ(Z){return U6.has(Z)||_Z.test(Z)}var EZ=(Z)=>Z&&Z.constructor&&!!Z.constructor.name&&jZ(Z.constructor.name);function xZ(Z){return Z.constructor.name==="NamedNodeMap"}var vZ=(Z,$,Q,X,J,W)=>{let G=Z.constructor.name;if(++X>$.maxDepth)return`[${G}]`;return($.min?"":G+SZ)+(U6.has(G)?`{${v0(xZ(Z)?[...Z].reduce((Y,z)=>{return Y[z.name]=z.value,Y},{}):{...Z},$,Q,X,J,W)}}`:`[${E0([...Z],$,Q,X,J,W)}]`)},hZ={serialize:vZ,test:EZ};function z6(Z){return Z.replaceAll("<","<").replaceAll(">",">")}function G9(Z,$,Q,X,J,W,G){let Y=X+Q.indent,z=Q.colors;return Z.map((B)=>{let U=$[B];if(typeof U==="string"&&U[0]==="_"&&U.startsWith("__vitest_")&&/__vitest_\d+__/.test(U))return"";let K=G(U,Q,Y,J,W);if(typeof U!=="string"){if(K.includes(` +`))K=Q.spacingOuter+Y+K+Q.spacingOuter+X;K=`{${K}}`}return`${Q.spacingInner+X+z.prop.open+B+z.prop.close}=${z.value.open}${K}${z.value.close}`}).join("")}function h0(Z,$,Q,X,J,W){return Z.map((G)=>$.spacingOuter+Q+(typeof G==="string"?K6(G,$):W(G,$,Q,X,J))).join("")}function yZ(Z,$,Q,X,J,W){if($.printShadowRoot===!1)return"";return[`${$.spacingOuter+Q}#shadow-root`,h0(Z,$,Q+$.indent,X,J,W)].join("")}function K6(Z,$){let Q=$.colors.content;return Q.open+z6(Z)+Q.close}function bZ(Z,$){let Q=$.colors.comment;return`${Q.open}${Q.close}`}function B9(Z,$,Q,X,J){let W=X.colors.tag;return`${W.open}<${Z}${$&&W.close+$+X.spacingOuter+J+W.open}${Q?`>${W.close}${Q}${X.spacingOuter}${J}${W.open}${W.close}`}function Y9(Z,$){let Q=$.colors.tag;return`${Q.open}<${Z}${Q.close} …${Q.open} />${Q.close}`}var uZ=1,U9=3,V6=8,w6=11,fZ=/^(?:(?:HTML|SVG)\w*)?Element$/;function gZ(Z){try{return typeof Z.hasAttribute==="function"&&Z.hasAttribute("is")}catch{return!1}}function mZ(Z){let $=Z.constructor.name,{nodeType:Q,tagName:X}=Z,J=typeof X==="string"&&X.includes("-")||gZ(Z);return Q===uZ&&(fZ.test($)||J)||Q===U9&&$==="Text"||Q===V6&&$==="Comment"||Q===w6&&$==="DocumentFragment"}var q6=(Z)=>Z?.constructor?.name&&mZ(Z);function dZ(Z){return Z.nodeType===U9}function cZ(Z){return Z.nodeType===V6}function _0(Z){return Z.nodeType===w6}function a8(Z,$){let Q=Z.filter((X)=>{if(X.nodeType===U9)return(X.data||"").trim().length>0;return!0});if($)Q=Q.filter($);return Q}function H6(Z,$,Q,X,J,W,G){if(dZ(Z))return K6(Z.data,$);if(cZ(Z))return bZ(Z.data,$);let Y=_0(Z)?"DocumentFragment":Z.tagName.toLowerCase();if(++X>$.maxDepth)return Y9(Y,$);let z=Array.prototype.slice.call(Z.childNodes||Z.children),B=_0(Z)||!Z.shadowRoot?[]:Array.prototype.slice.call(Z.shadowRoot.children),U=G?a8(z,G):z,K=G?a8(B,G):B;return B9(Y,G9(_0(Z)?[]:Array.from(Z.attributes,(P)=>P.name).sort(),_0(Z)?{}:[...Z.attributes].reduce((P,w)=>{return P[w.name]=w.value,P},{}),$,Q+$.indent,X,J,W),(K.length>0?yZ(K,$,Q+$.indent,X,J,W):"")+h0(U,$,Q+$.indent,X,J,W),$,Q)}var lZ=(Z,$,Q,X,J,W)=>H6(Z,$,Q,X,J,W);function P6(Z){return{test:q6,serialize:($,Q,X,J,W,G)=>H6($,Q,X,J,W,G,Z)}}var pZ={serialize:lZ,test:q6},iZ="@@__IMMUTABLE_ITERABLE__@@",nZ="@@__IMMUTABLE_LIST__@@",oZ="@@__IMMUTABLE_KEYED__@@",rZ="@@__IMMUTABLE_MAP__@@",s8="@@__IMMUTABLE_ORDERED__@@",aZ="@@__IMMUTABLE_RECORD__@@",sZ="@@__IMMUTABLE_SEQ__@@",tZ="@@__IMMUTABLE_SET__@@",eZ="@@__IMMUTABLE_STACK__@@",f1=(Z)=>`Immutable.${Z}`,y0=(Z)=>`[${Z}]`,Z0=" ",t8="…";function Z$(Z,$,Q,X,J,W,G){return++X>$.maxDepth?y0(f1(G)):`${f1(G)+Z0}{${x0(Z.entries(),$,Q,X,J,W)}}`}function $$(Z){let $=0;return{next(){if($$.maxDepth?y0(G):`${G+Z0}{${x0($$(Z),$,Q,X,J,W)}}`}function X$(Z,$,Q,X,J,W){let G=f1("Seq");if(++X>$.maxDepth)return y0(G);if(Z[oZ])return`${G+Z0}{${Z._iter||Z._object?x0(Z.entries(),$,Q,X,J,W):t8}}`;return`${G+Z0}[${Z._iter||Z._array||Z._collection||Z._iterable?W9(Z.values(),$,Q,X,J,W):t8}]`}function Q9(Z,$,Q,X,J,W,G){return++X>$.maxDepth?y0(f1(G)):`${f1(G)+Z0}[${W9(Z.values(),$,Q,X,J,W)}]`}var J$=(Z,$,Q,X,J,W)=>{if(Z[rZ])return Z$(Z,$,Q,X,J,W,Z[s8]?"OrderedMap":"Map");if(Z[nZ])return Q9(Z,$,Q,X,J,W,"List");if(Z[tZ])return Q9(Z,$,Q,X,J,W,Z[s8]?"OrderedSet":"Set");if(Z[eZ])return Q9(Z,$,Q,X,J,W,"Stack");if(Z[sZ])return X$(Z,$,Q,X,J,W);return Q$(Z,$,Q,X,J,W)},W$=(Z)=>Z&&(Z[iZ]===!0||Z[aZ]===!0),G$={serialize:J$,test:W$};function N6(Z){return Z&&Z.__esModule&&Object.prototype.hasOwnProperty.call(Z,"default")?Z.default:Z}var X9={exports:{}},y={},e8;function B$(){if(e8)return y;e8=1;var Z=Symbol.for("react.transitional.element"),$=Symbol.for("react.portal"),Q=Symbol.for("react.fragment"),X=Symbol.for("react.strict_mode"),J=Symbol.for("react.profiler"),W=Symbol.for("react.consumer"),G=Symbol.for("react.context"),Y=Symbol.for("react.forward_ref"),z=Symbol.for("react.suspense"),B=Symbol.for("react.suspense_list"),U=Symbol.for("react.memo"),K=Symbol.for("react.lazy"),P=Symbol.for("react.view_transition"),w=Symbol.for("react.client.reference");function N(H){if(typeof H==="object"&&H!==null){var R=H.$$typeof;switch(R){case Z:switch(H=H.type,H){case Q:case J:case X:case z:case B:case P:return H;default:switch(H=H&&H.$$typeof,H){case G:case Y:case K:case U:return H;case W:return H;default:return R}}case $:return R}}}return y.ContextConsumer=W,y.ContextProvider=G,y.Element=Z,y.ForwardRef=Y,y.Fragment=Q,y.Lazy=K,y.Memo=U,y.Portal=$,y.Profiler=J,y.StrictMode=X,y.Suspense=z,y.SuspenseList=B,y.isContextConsumer=function(H){return N(H)===W},y.isContextProvider=function(H){return N(H)===G},y.isElement=function(H){return typeof H==="object"&&H!==null&&H.$$typeof===Z},y.isForwardRef=function(H){return N(H)===Y},y.isFragment=function(H){return N(H)===Q},y.isLazy=function(H){return N(H)===K},y.isMemo=function(H){return N(H)===U},y.isPortal=function(H){return N(H)===$},y.isProfiler=function(H){return N(H)===J},y.isStrictMode=function(H){return N(H)===X},y.isSuspense=function(H){return N(H)===z},y.isSuspenseList=function(H){return N(H)===B},y.isValidElementType=function(H){return typeof H==="string"||typeof H==="function"||H===Q||H===J||H===X||H===z||H===B||typeof H==="object"&&H!==null&&(H.$$typeof===K||H.$$typeof===U||H.$$typeof===G||H.$$typeof===W||H.$$typeof===Y||H.$$typeof===w||H.getModuleId!==void 0)?!0:!1},y.typeOf=N,y}var Z6;function Y$(){if(Z6)return X9.exports;return Z6=1,X9.exports=B$(),X9.exports}var D6=Y$(),U$=N6(D6),z$=Y6({__proto__:null,default:U$},[D6]),J9={exports:{}},x={},$6;function K$(){if($6)return x;$6=1;var Z=Symbol.for("react.element"),$=Symbol.for("react.portal"),Q=Symbol.for("react.fragment"),X=Symbol.for("react.strict_mode"),J=Symbol.for("react.profiler"),W=Symbol.for("react.provider"),G=Symbol.for("react.context"),Y=Symbol.for("react.server_context"),z=Symbol.for("react.forward_ref"),B=Symbol.for("react.suspense"),U=Symbol.for("react.suspense_list"),K=Symbol.for("react.memo"),P=Symbol.for("react.lazy"),w=Symbol.for("react.offscreen"),N;N=Symbol.for("react.module.reference");function H(R){if(typeof R==="object"&&R!==null){var O=R.$$typeof;switch(O){case Z:switch(R=R.type,R){case Q:case J:case X:case B:case U:return R;default:switch(R=R&&R.$$typeof,R){case Y:case G:case z:case P:case K:case W:return R;default:return O}}case $:return O}}}return x.ContextConsumer=G,x.ContextProvider=W,x.Element=Z,x.ForwardRef=z,x.Fragment=Q,x.Lazy=P,x.Memo=K,x.Portal=$,x.Profiler=J,x.StrictMode=X,x.Suspense=B,x.SuspenseList=U,x.isAsyncMode=function(){return!1},x.isConcurrentMode=function(){return!1},x.isContextConsumer=function(R){return H(R)===G},x.isContextProvider=function(R){return H(R)===W},x.isElement=function(R){return typeof R==="object"&&R!==null&&R.$$typeof===Z},x.isForwardRef=function(R){return H(R)===z},x.isFragment=function(R){return H(R)===Q},x.isLazy=function(R){return H(R)===P},x.isMemo=function(R){return H(R)===K},x.isPortal=function(R){return H(R)===$},x.isProfiler=function(R){return H(R)===J},x.isStrictMode=function(R){return H(R)===X},x.isSuspense=function(R){return H(R)===B},x.isSuspenseList=function(R){return H(R)===U},x.isValidElementType=function(R){return typeof R==="string"||typeof R==="function"||R===Q||R===J||R===X||R===B||R===U||R===w||typeof R==="object"&&R!==null&&(R.$$typeof===P||R.$$typeof===K||R.$$typeof===W||R.$$typeof===G||R.$$typeof===z||R.$$typeof===N||R.getModuleId!==void 0)?!0:!1},x.typeOf=H,x}var Q6;function V$(){if(Q6)return J9.exports;return Q6=1,J9.exports=K$(),J9.exports}var R6=V$(),w$=N6(R6),q$=Y6({__proto__:null,default:w$},[R6]),H$=["isAsyncMode","isConcurrentMode","isContextConsumer","isContextProvider","isElement","isForwardRef","isFragment","isLazy","isMemo","isPortal","isProfiler","isStrictMode","isSuspense","isSuspenseList","isValidElementType"],E1=Object.fromEntries(H$.map((Z)=>[Z,($)=>q$[Z]($)||z$[Z]($)]));function F6(Z,$=[]){if(Array.isArray(Z))for(let Q of Z)F6(Q,$);else if(Z!=null&&Z!==!1&&Z!=="")$.push(Z);return $}function X6(Z){let $=Z.type;if(typeof $==="string")return $;if(typeof $==="function")return $.displayName||$.name||"Unknown";if(E1.isFragment(Z))return"React.Fragment";if(E1.isSuspense(Z))return"React.Suspense";if(typeof $==="object"&&$!==null){if(E1.isContextProvider(Z))return"Context.Provider";if(E1.isContextConsumer(Z))return"Context.Consumer";if(E1.isForwardRef(Z)){if($.displayName)return $.displayName;let Q=$.render.displayName||$.render.name||"";return Q===""?"ForwardRef":`ForwardRef(${Q})`}if(E1.isMemo(Z)){let Q=$.displayName||$.type.displayName||$.type.name||"";return Q===""?"Memo":`Memo(${Q})`}}return"UNDEFINED"}function P$(Z){let{props:$}=Z;return Object.keys($).filter((Q)=>Q!=="children"&&$[Q]!==void 0).sort()}var N$=(Z,$,Q,X,J,W)=>++X>$.maxDepth?Y9(X6(Z),$):B9(X6(Z),G9(P$(Z),Z.props,$,Q+$.indent,X,J,W),h0(F6(Z.props.children),$,Q+$.indent,X,J,W),$,Q),D$=(Z)=>Z!=null&&E1.isElement(Z),R$={serialize:N$,test:D$},F$=typeof Symbol==="function"&&Symbol.for?Symbol.for("react.test.json"):245830487;function M$(Z){let{props:$}=Z;return $?Object.keys($).filter((Q)=>$[Q]!==void 0).sort():[]}var O$=(Z,$,Q,X,J,W)=>++X>$.maxDepth?Y9(Z.type,$):B9(Z.type,Z.props?G9(M$(Z),Z.props,$,Q+$.indent,X,J,W):"",Z.children?h0(Z.children,$,Q+$.indent,X,J,W):"",$,Q),I$=(Z)=>Z&&Z.$$typeof===F$,C$={serialize:O$,test:I$},M6=Object.prototype.toString,L$=Date.prototype.toISOString,T$=Error.prototype.toString,J6=RegExp.prototype.toString;function j0(Z){return typeof Z.constructor==="function"&&Z.constructor.name||"Object"}function A$(Z){return typeof window<"u"&&Z===window}var k$=/^Symbol\((.*)\)(.*)$/,S$=/\n/g;class z9 extends Error{constructor(Z,$){super(Z);this.stack=$,this.name=this.constructor.name}}function _$(Z){return Z==="[object Array]"||Z==="[object ArrayBuffer]"||Z==="[object DataView]"||Z==="[object Float32Array]"||Z==="[object Float64Array]"||Z==="[object Int8Array]"||Z==="[object Int16Array]"||Z==="[object Int32Array]"||Z==="[object Uint8Array]"||Z==="[object Uint8ClampedArray]"||Z==="[object Uint16Array]"||Z==="[object Uint32Array]"}function j$(Z){return Object.is(Z,-0)?"-0":String(Z)}function E$(Z){return String(`${Z}n`)}function W6(Z,$){if(!$)return"[Function]";return`[Function ${Z.name||"anonymous"}]`}function G6(Z){return String(Z).replace(k$,"Symbol($1)")}function B6(Z){return`[${T$.call(Z)}]`}function O6(Z,$,Q,X,J){if(Z===!0||Z===!1)return`${Z}`;if(Z===void 0)return"undefined";if(Z===null)return"null";let W=typeof Z;if(W==="number")return j$(Z);if(W==="bigint")return E$(Z);if(W==="string"){let Y=J?"'":'"';if(X){let z=J?/['\\]/g:/["\\]/g;return`${Y}${Z.replaceAll(z,"\\$&")}${Y}`}return`${Y}${Z}${Y}`}if(W==="function")return W6(Z,$);if(W==="symbol")return G6(Z);let G=M6.call(Z);if(G==="[object WeakMap]")return"WeakMap {}";if(G==="[object WeakSet]")return"WeakSet {}";if(G==="[object Function]"||G==="[object GeneratorFunction]")return W6(Z,$);if(G==="[object Symbol]")return G6(Z);if(G==="[object Date]")return Number.isNaN(+Z)?"Date { NaN }":L$.call(Z);if(G==="[object Error]")return B6(Z);if(G==="[object RegExp]"){if(Q)return J6.call(Z).replaceAll(/[$()*+.?[\\\]^{|}]/g,"\\$&");return J6.call(Z)}if(Z instanceof Error)return B6(Z);return null}function I6(Z,$,Q,X,J,W){if(J.includes(Z))return"[Circular]";J=[...J],J.push(Z);let G=++X>$.maxDepth,Y=$.min;if($.callToJSON&&!G&&Z.toJSON&&typeof Z.toJSON==="function"&&!W)return A1(Z.toJSON(),$,Q,X,J,!0);let z=M6.call(Z);if(z==="[object Arguments]")return G?"[Arguments]":`${Y?"":"Arguments "}[${E0(Z,$,Q,X,J,A1)}]`;if(_$(z))return G?`[${Z.constructor.name}]`:`${!$.printBasicPrototype&&Z.constructor.name==="Array"?"":`${Z.constructor.name} `}[${E0(Z,$,Q,X,J,A1)}]`;if(z==="[object Map]")return G?"[Map]":`Map {${x0(Z.entries(),$,Q,X,J,A1," => ",Z.size)}}`;if(z==="[object Set]")return G?"[Set]":`Set {${W9(Z.values(),$,Q,X,J,A1,Z.size)}}`;return G||A$(Z)?`[${j0(Z)}]`:`${!$.printBasicPrototype&&j0(Z)==="Object"?"":`${j0(Z)} `}{${v0(Z,$,Q,X,J,A1)}}`}var x$={test:(Z)=>Z&&Z instanceof Error,serialize(Z,$,Q,X,J,W){if(J.includes(Z))return"[Circular]";J=[...J,Z];let G=++X>$.maxDepth,{message:Y,cause:z,...B}=Z,U={message:Y,...typeof z<"u"?{cause:z}:{},...Z instanceof AggregateError?{errors:Z.errors}:{},...B},K=Z.name!=="Error"?Z.name:j0(Z);return G?`[${K}]`:`${K} {${v0(U,$,Q,X,J,W,null)}}`}};function v$(Z){return Z.serialize!=null}function C6(Z,$,Q,X,J,W){let G;try{G=v$(Z)?Z.serialize($,Q,X,J,W,A1):Z.print($,(Y)=>A1(Y,Q,X,J,W),(Y)=>{let z=X+Q.indent;return z+Y.replaceAll(S$,` +${z}`)},{edgeSpacing:Q.spacingOuter,min:Q.min,spacing:Q.spacingInner},Q.colors)}catch(Y){throw new z9(Y.message,Y.stack)}if(typeof G!=="string")throw TypeError(`pretty-format: Plugin must return type "string" but instead returned "${typeof G}".`);return G}function L6(Z,$){for(let Q of Z)try{if(Q.test($))return Q}catch(X){throw new z9(X.message,X.stack)}return null}function A1(Z,$,Q,X,J,W){let G,Y=L6($.plugins,Z);if(Y!==null)G=C6(Y,Z,$,Q,X,J);else{let z=O6(Z,$.printFunctionName,$.escapeRegex,$.escapeString,$.singleQuote);if(z!==null)G=z;else G=I6(Z,$,Q,X,J,W)}if($._outputLengthPerDepth[X]??=0,$._outputLengthPerDepth[X]+=G.length,$._outputLengthPerDepth[X]>$.maxOutputLength)$.maxDepth=0;return G}var K9={comment:"gray",content:"reset",prop:"yellow",tag:"cyan",value:"green"},T6=Object.keys(K9),Q1={callToJSON:!0,compareKeys:void 0,escapeRegex:!1,escapeString:!0,highlight:!1,indent:2,maxDepth:Number.POSITIVE_INFINITY,maxOutputLength:1e6,maxWidth:Number.POSITIVE_INFINITY,min:!1,plugins:[],printBasicPrototype:!0,printFunctionName:!0,printShadowRoot:!0,theme:K9,singleQuote:!1,quoteKeys:!0,spacingInner:` +`,spacingOuter:` +`};function h$(Z){for(let $ of Object.keys(Z))if(!Object.hasOwn(Q1,$))throw Error(`pretty-format: Unknown option "${$}".`);if(Z.min&&Z.indent!==void 0&&Z.indent!==0)throw Error('pretty-format: Options "min" and "indent" cannot be used together.')}function y$(){return T6.reduce((Z,$)=>{let Q=K9[$],X=Q&&f[Q];if(X&&typeof X.close==="string"&&typeof X.open==="string")Z[$]=X;else throw Error(`pretty-format: Option "theme" has a key "${$}" whose value "${Q}" is undefined in ansi-styles.`);return Z},Object.create(null))}function b$(){return T6.reduce((Z,$)=>{return Z[$]={close:"",open:""},Z},Object.create(null))}function u$(Z){return Z?.printFunctionName??Q1.printFunctionName}function f$(Z){return Z?.escapeRegex??Q1.escapeRegex}function g$(Z){return Z?.escapeString??Q1.escapeString}function m$(Z){return{callToJSON:Z?.callToJSON??Q1.callToJSON,colors:Z?.highlight?y$():b$(),compareKeys:typeof Z?.compareKeys==="function"||Z?.compareKeys===null?Z.compareKeys:Q1.compareKeys,escapeRegex:f$(Z),escapeString:g$(Z),indent:Z?.min?"":d$(Z?.indent??Q1.indent),maxDepth:Z?.maxDepth??Q1.maxDepth,maxWidth:Z?.maxWidth??Q1.maxWidth,min:Z?.min??Q1.min,plugins:Z?.plugins??Q1.plugins,printBasicPrototype:Z?.printBasicPrototype??!Z?.min,printFunctionName:u$(Z),printShadowRoot:Z?.printShadowRoot??!0,spacingInner:Z?.spacingInner??(Z?.min?" ":` +`),spacingOuter:Z?.spacingOuter??(Z?.min?"":` +`),singleQuote:Z?.singleQuote??Q1.singleQuote,quoteKeys:Z?.quoteKeys??Q1.quoteKeys,maxOutputLength:Z?.maxOutputLength??Q1.maxOutputLength,_outputLengthPerDepth:[]}}function d$(Z){return Array.from({length:Z+1}).join(" ")}function X1(Z,$){if($)h$($);let Q=m$($),X=L6(Q.plugins,Z);if(X!==null)return C6(X,Z,Q,"",0,[]);let J=O6(Z,Q.printFunctionName,Q.escapeRegex,Q.escapeString,Q.singleQuote);if(J!==null)return J;return I6(Z,Q,"",0,[])}var $0={AsymmetricMatcher:kZ,DOMCollection:hZ,DOMElement:pZ,Immutable:G$,ReactElement:R$,ReactTestComponent:C$,Error:x$};var{AsymmetricMatcher:A6,DOMCollection:k6,DOMElement:c$,Immutable:S6,ReactElement:_6,ReactTestComponent:j6}=$0,l$=[j6,_6,c$,k6,S6,A6];function a(Z,$=10,{maxLength:Q,filterNode:X,...J}={}){let W=Q??1e4,G,Y=typeof X==="string"?p$(X):X,z=Y?[j6,_6,P6(Y),k6,S6,A6]:l$;try{G=X1(Z,{maxDepth:$,escapeString:!1,plugins:z,...J})}catch{G=X1(Z,{callToJSON:!1,maxDepth:$,escapeString:!1,plugins:z,...J})}return G.length>=W&&$>1?a(Z,Math.floor(Math.min($,Number.MAX_SAFE_INTEGER)/2),{maxLength:Q,filterNode:X,...J}):G}function p$(Z){return(X)=>{if(X.nodeType===8)return!1;if(X.nodeType===1&&X.matches)try{return!X.matches(Z)}catch{return!0}return!0}}function E6(Z,$){let{truncate:Q,multiline:X,...J}=$??{},W={singleQuote:!0,quoteKeys:!1,min:!0,spacingInner:" ",spacingOuter:" ",printBasicPrototype:!1,compareKeys:null,...X?{min:!1,spacingInner:void 0,spacingOuter:void 0}:{}},G=Q??0,Y=a(Z,void 0,{...W,...J,maxLength:G||void 0});if(G===0||Y.length<=G)return Y;let z=Object.prototype.toString.call(Z);if(typeof Z==="string"){let B=G-1;if(B>0&&o$(Y[B-1]))B=B-1;return`'${Y.slice(1,B)}…'`}if(z==="[object Array]"||z==="[object Object]"||z==="[object Set]"||z==="[object Map]")return i$(Z,G,{...W,...J,maxDepth:1});return a(Z,void 0,{...W,...J,maxDepth:0})}function i$(Z,$,Q){function X(W){return a(Z,void 0,{...Q,maxWidth:W})}let J=n$(0,$,(W)=>X(W).length<=$);return X(J)}function n$(Z,$,Q){while(Z+1<$){let X=Math.floor((Z+$)/2);if(Q(X))Z=X;else $=X}return Z}function o$(Z){return Z>="\uD800"&&Z<="\uDBFF"}function N1(Z,$,Q){let X=typeof Z;if(!Q.includes(X))throw TypeError(`${$} value must be ${Q.join(" or ")}, received "${X}"`)}function Q0(Z){return Z!=null&&typeof Z==="object"&&!Array.isArray(Z)}function r$(Z){return Z===Object.prototype||Z===Function.prototype||Z===RegExp.prototype}function g1(Z){return Object.prototype.toString.apply(Z).slice(8,-1)}function a$(Z,$){let Q=typeof $==="function"?$:(X)=>$.add(X);Object.getOwnPropertyNames(Z).forEach(Q),Object.getOwnPropertySymbols(Z).forEach(Q)}function w9(Z){let $=new Set;if(r$(Z))return[];return a$(Z,$),Array.from($)}var x6={forceWritable:!1};function q9(Z,$=x6){return V9(Z,new WeakMap,$)}function V9(Z,$,Q=x6){let X,J;if($.has(Z))return $.get(Z);if(Array.isArray(Z)){J=Array.from({length:X=Z.length}),$.set(Z,J);while(X--)J[X]=V9(Z[X],$,Q);return J}if(Object.prototype.toString.call(Z)==="[object Object]"){J=Object.create(Object.getPrototypeOf(Z)),$.set(Z,J);let W=w9(Z);for(let G of W){let Y=Object.getOwnPropertyDescriptor(Z,G);if(!Y)continue;let z=V9(Z[G],$,Q);if(Q.forceWritable)Object.defineProperty(J,G,{enumerable:Y.enumerable,configurable:!0,writable:!0,value:z});else if("get"in Y)Object.defineProperty(J,G,{...Y,get(){return z}});else Object.defineProperty(J,G,{...Y,value:z})}return J}return Z}function H9(){}function m1(Z){let $=Z%10,Q=Z%100;if($===1&&Q!==11)return`${Z}st`;if($===2&&Q!==12)return`${Z}nd`;if($===3&&Q!==13)return`${Z}rd`;return`${Z}th`}var s=-1,o=1,c=0;class m{0;1;constructor(Z,$){this[0]=Z,this[1]=$}}function s$(Z,$){if(!Z||!$||Z.charAt(0)!==$.charAt(0))return 0;let Q=0,X=Math.min(Z.length,$.length),J=X,W=0;while(QX)Z=Z.substring(Q-X);else if(Q0?Q[X-1]:-1,G=0,Y=0,z=0,B=0,J=null,$=!0}W++}if($)r6(Z);$Q(Z),W=1;while(W=w){if(P>=U.length/2||P>=K.length/2)Z.splice(W,0,new m(c,K.substring(0,P))),Z[W-1][1]=U.substring(0,U.length-P),Z[W+1][1]=K.substring(P),W++}else if(w>=U.length/2||w>=K.length/2)Z.splice(W,0,new m(c,U.substring(0,w))),Z[W-1][0]=o,Z[W-1][1]=K.substring(0,K.length-w),Z[W+1][0]=s,Z[W+1][1]=U.substring(w),W++;W++}W++}}var h6=/[^a-z0-9]/i,y6=/\s/,b6=/[\r\n]/,e$=/\n\r?\n$/,ZQ=/^\r?\n\r?\n/;function $Q(Z){let $=1;while($=B)B=U,G=Q,Y=X,z=J}if(Z[$-1][1]!==G){if(G)Z[$-1][1]=G;else Z.splice($-1,1),$--;if(Z[$][1]=Y,z)Z[$+1][1]=z;else Z.splice($+1,1),$--}}$++}}function r6(Z){Z.push(new m(c,""));let $=0,Q=0,X=0,J="",W="",G;while($1){if(Q!==0&&X!==0){if(G=s$(W,J),G!==0){if($-Q-X>0&&Z[$-Q-X-1][0]===c)Z[$-Q-X-1][1]+=W.substring(0,G);else Z.splice(0,0,new m(c,W.substring(0,G))),$++;W=W.substring(G),J=J.substring(G)}if(G=o6(W,J),G!==0)Z[$][1]=W.substring(W.length-G)+Z[$][1],W=W.substring(0,W.length-G),J=J.substring(0,J.length-G)}if($-=Q+X,Z.splice($,Q+X),J.length)Z.splice($,0,new m(s,J)),$++;if(W.length)Z.splice($,0,new m(o,W)),$++;$++}else if($!==0&&Z[$-1][0]===c)Z[$-1][1]+=Z[$][1],Z.splice($,1);else $++;X=0,Q=0,J="",W="";break}if(Z.at(-1)?.[1]==="")Z.pop();let Y=!1;$=1;while(${let M=0;while(w{let M=0;while(w<=N&&H<=R&&O(N,R))N-=1,R-=1,M+=1;return M},J=(w,N,H,R,O,M,T)=>{let I=0,C=-w,A=M[I],L=A;M[I]+=Q(A+1,N,R+A-C+1,H,O);let S=w{let I=0,C=w,A=M[I],L=A;M[I]-=X(N,A-1,H,R+A-C-1,O);let S=w{let S=R-N,b=H-N,u=O-R-b,v=-u-(w-1),e=-u+(w-1),r=$,g=w{let S=O-H,b=H-N,u=O-R-b,v=u-w,e=u+w,r=$,g=w{let A=R-N,L=O-H,S=H-N,b=O-R,t=b-S,u=S,v=S;if(T[0]=N-1,I[0]=H,t%2===0){let e=(w||t)/2,r=(S+b)/2;for(let g=1;g<=r;g+=1)if(u=J(g,H,O,A,M,T,u),g{if(O-R{z1(H1,I1,G1)},isCommon:(H1,G1)=>S1(G1,H1)}}let O1=N,U1=H;N=R,H=O,R=O1,O=U1}let{foundSubsequence:L,isCommon:S}=T[M?1:0];z(w,N,H,R,O,S,I,C,A);let{nChangePreceding:b,aEndPreceding:t,bEndPreceding:u,nCommonPreceding:v,aCommonPreceding:e,bCommonPreceding:r,nCommonFollowing:g,aCommonFollowing:d,bCommonFollowing:n,nChangeFollowing:D1,aStartFollowing:Z1,bStartFollowing:Y1}=A;if(N{if(typeof N!=="number")throw TypeError(`${Z}: ${w} typeof ${typeof N} is not a number`);if(!Number.isSafeInteger(N))throw RangeError(`${Z}: ${w} value ${N} is not a safe integer`);if(N<0)throw RangeError(`${Z}: ${w} value ${N} is a negative integer`)},K=(w,N)=>{let H=typeof N;if(H!=="function")throw TypeError(`${Z}: ${w} typeof ${H} is not a function`)};function P(w,N,H,R){U("aLength",w),U("bLength",N),K("isCommon",H),K("foundSubsequence",R);let O=Q(0,w,0,N,H);if(O!==0)R(O,0,0);if(w!==O||N!==O){let M=O,T=O,I=X(M,w-1,T,N-1,H),C=w-I,A=N-I,L=O+I;if(w!==L&&N!==L)B(0,M,C,T,A,!1,[{foundSubsequence:R,isCommon:H}],[$],[$],{aCommonFollowing:$,aCommonPreceding:$,aEndPreceding:$,aStartFollowing:$,bCommonFollowing:$,bCommonPreceding:$,bEndPreceding:$,bStartFollowing:$,nChangeFollowing:$,nChangePreceding:$,nCommonFollowing:$,nCommonPreceding:$});if(I!==0)R(I,C,A)}}return u0}var WQ=JQ(),s6=XQ(WQ);function GQ(Z,$){return Z.replace(/\s+$/,(Q)=>$(Q))}function F9(Z,$,Q,X,J,W){return Z.length!==0?Q(`${X} ${GQ(Z,J)}`):X!==" "?Q(X):$&&W.length!==0?Q(`${X} ${W}`):""}function t6(Z,$,{aColor:Q,aIndicator:X,changeLineTrailingSpaceColor:J,emptyFirstOrLastLinePlaceholder:W}){return F9(Z,$,Q,X,J,W)}function e6(Z,$,{bColor:Q,bIndicator:X,changeLineTrailingSpaceColor:J,emptyFirstOrLastLinePlaceholder:W}){return F9(Z,$,Q,X,J,W)}function Z7(Z,$,{commonColor:Q,commonIndicator:X,commonLineTrailingSpaceColor:J,emptyFirstOrLastLinePlaceholder:W}){return F9(Z,$,Q,X,J,W)}function f6(Z,$,Q,X,{patchColor:J}){return J(`@@ -${Z+1},${$-Z} +${Q+1},${X-Q} @@`)}function BQ(Z,$){let Q=Z.length,X=$.contextLines,J=X+X,W=Q,G=!1,Y=0,z=0;while(z!==Q){let I=z;while(z!==Q&&Z[z][0]===c)z+=1;if(I!==z)if(I===0){if(z>X)W-=z-X,G=!0}else if(z===Q){let C=z-I;if(C>X)W-=C-X,G=!0}else{let C=z-I;if(C>J)W-=C-J,Y+=1}while(z!==Q&&Z[z][0]!==c)z+=1}let B=Y!==0||G;if(Y!==0)W+=Y+1;else if(G)W+=1;let U=W-1,K=[],P=0;if(B)K.push("");let w=0,N=0,H=0,R=0,O=(I)=>{let C=K.length;K.push(Z7(I,C===0||C===U,$)),H+=1,R+=1},M=(I)=>{let C=K.length;K.push(t6(I,C===0||C===U,$)),H+=1},T=(I)=>{let C=K.length;K.push(e6(I,C===0||C===U,$)),R+=1};z=0;while(z!==Q){let I=z;while(z!==Q&&Z[z][0]===c)z+=1;if(I!==z)if(I===0){if(z>X)I=z-X,w=I,N=I,H=w,R=N;for(let C=I;C!==z;C+=1)O(Z[C][1])}else if(z===Q){let C=z-I>X?I+X:z;for(let A=I;A!==C;A+=1)O(Z[A][1])}else{let C=z-I;if(C>J){let A=I+X;for(let S=I;S!==A;S+=1)O(Z[S][1]);K[P]=f6(w,H,N,R,$),P=K.length,K.push("");let L=C-J;w=H+L,N=R+L,H=w,R=N;for(let S=z-X;S!==z;S+=1)O(Z[S][1])}else for(let A=I;A!==z;A+=1)O(Z[A][1])}while(z!==Q&&Z[z][0]===s)M(Z[z][1]),z+=1;while(z!==Q&&Z[z][0]===o)T(Z[z][1]),z+=1}if(B)K[P]=f6(w,H,N,R,$);return K.join(` +`)}function YQ(Z,$){return Z.map((Q,X,J)=>{let W=Q[1],G=X===0||X===J.length-1;switch(Q[0]){case s:return t6(W,G,$);case o:return e6(W,G,$);default:return Z7(W,G,$)}}).join(` +`)}var P9=(Z)=>Z,$7=5,UQ=0;function zQ(){return{aAnnotation:"Expected",aColor:f.green,aIndicator:"-",bAnnotation:"Received",bColor:f.red,bIndicator:"+",changeColor:f.inverse,changeLineTrailingSpaceColor:P9,commonColor:f.dim,commonIndicator:" ",commonLineTrailingSpaceColor:P9,compareKeys:void 0,contextLines:$7,emptyFirstOrLastLinePlaceholder:"",expand:!1,includeChangeCounts:!1,omitAnnotationLines:!1,patchColor:f.yellow,printBasicPrototype:!1,truncateThreshold:UQ,truncateAnnotation:"... Diff result is truncated",truncateAnnotationColor:P9}}function KQ(Z){return Z&&typeof Z==="function"?Z:void 0}function VQ(Z){return typeof Z==="number"&&Number.isSafeInteger(Z)&&Z>=0?Z:$7}function x1(Z={}){return{...zQ(),...Z,compareKeys:KQ(Z.compareKeys),contextLines:VQ(Z.contextLines)}}function d1(Z){return Z.length===1&&Z[0].length===0}function wQ(Z){let $=0,Q=0;return Z.forEach((X)=>{switch(X[0]){case s:$+=1;break;case o:Q+=1;break}}),{a:$,b:Q}}function qQ({aAnnotation:Z,aColor:$,aIndicator:Q,bAnnotation:X,bColor:J,bIndicator:W,includeChangeCounts:G,omitAnnotationLines:Y},z){if(Y)return"";let B="",U="";if(G){let w=String(z.a),N=String(z.b),H=X.length-Z.length,R=" ".repeat(Math.max(0,H)),O=" ".repeat(Math.max(0,-H)),M=N.length-w.length,T=" ".repeat(Math.max(0,M)),I=" ".repeat(Math.max(0,-M));B=`${R} ${Q} ${T}${w}`,U=`${O} ${W} ${I}${N}`}let K=`${Q} ${Z}${B}`,P=`${W} ${X}${U}`;return`${$(K)} +${J(P)} + +`}function M9(Z,$,Q){return qQ(Q,wQ(Z))+(Q.expand?YQ(Z,Q):BQ(Z,Q))+($?Q.truncateAnnotationColor(` +${Q.truncateAnnotation}`):"")}function c0(Z,$,Q){let X=x1(Q),[J,W]=Q7(d1(Z)?[]:Z,d1($)?[]:$,X);return M9(J,W,X)}function HQ(Z,$,Q,X,J){if(d1(Z)&&d1(Q))Z=[],Q=[];if(d1($)&&d1(X))$=[],X=[];if(Z.length!==Q.length||$.length!==X.length)return c0(Z,$,J);let[W,G]=Q7(Q,X,J),Y=0,z=0;return W.forEach((B)=>{switch(B[0]){case s:B[1]=Z[Y],Y+=1;break;case o:B[1]=$[z],z+=1;break;default:B[1]=$[z],Y+=1,z+=1}}),M9(W,G,x1(J))}function Q7(Z,$,Q){let X=Q?.truncateThreshold??!1,J=Math.max(Math.floor(Q?.truncateThreshold??0),0),W=X?Math.min(Z.length,J):Z.length,G=X?Math.min($.length,J):$.length,Y=W!==Z.length||G!==$.length,z=(w,N)=>Z[w]===$[N],B=[],U=0,K=0;s6(W,G,z,(w,N,H)=>{for(;U!==N;U+=1)B.push(new m(s,Z[U]));for(;K!==H;K+=1)B.push(new m(o,$[K]));for(;w!==0;w-=1,U+=1,K+=1)B.push(new m(c,$[K]))});for(;U!==W;U+=1)B.push(new m(s,Z[U]));for(;K!==G;K+=1)B.push(new m(o,$[K]));return[B,Y]}function g6(Z){if(Z===void 0)return"undefined";else if(Z===null)return"null";else if(Array.isArray(Z))return"array";else if(typeof Z==="boolean")return"boolean";else if(typeof Z==="function")return"function";else if(typeof Z==="number")return"number";else if(typeof Z==="string")return"string";else if(typeof Z==="bigint")return"bigint";else if(typeof Z==="object"){if(Z!=null){if(Z.constructor===RegExp)return"regexp";else if(Z.constructor===Map)return"map";else if(Z.constructor===Set)return"set";else if(Z.constructor===Date)return"date"}return"object"}else if(typeof Z==="symbol")return"symbol";throw Error(`value of unknown type: ${Z}`)}function m6(Z){return Z.includes(`\r +`)?`\r +`:` +`}function PQ(Z,$,Q){let X=Q?.truncateThreshold??!1,J=Math.max(Math.floor(Q?.truncateThreshold??0),0),W=Z.length,G=$.length;if(X){let w=Z.includes(` +`),N=$.includes(` +`),H=m6(Z),R=m6($),O=w?`${Z.split(H,J).join(H)} +`:Z,M=N?`${$.split(R,J).join(R)} +`:$;W=O.length,G=M.length}let Y=W!==Z.length||G!==$.length,z=(w,N)=>Z[w]===$[N],B=0,U=0,K=[];if(s6(W,G,z,(w,N,H)=>{if(B!==N)K.push(new m(s,Z.slice(B,N)));if(U!==H)K.push(new m(o,$.slice(U,H)));B=N+w,U=H+w,K.push(new m(c,$.slice(H,U)))}),B!==W)K.push(new m(s,Z.slice(B)));if(U!==G)K.push(new m(o,$.slice(U)));return[K,Y]}function NQ(Z,$,Q){return $.reduce((X,J)=>X+(J[0]===c?J[1]:J[0]===Z&&J[1].length!==0?Q(J[1]):""),"")}class D9{op;line;lines;changeColor;constructor(Z,$){this.op=Z,this.line=[],this.lines=[],this.changeColor=$}pushSubstring(Z){this.pushDiff(new m(this.op,Z))}pushLine(){this.lines.push(this.line.length!==1?new m(this.op,NQ(this.op,this.line,this.changeColor)):this.line[0][0]===this.op?this.line[0]:new m(this.op,this.line[0][1])),this.line.length=0}isLineEmpty(){return this.line.length===0}pushDiff(Z){this.line.push(Z)}align(Z){let $=Z[1];if($.includes(` +`)){let Q=$.split(` +`),X=Q.length-1;Q.forEach((J,W)=>{if(W{if(G===0){let Y=new m($,W);if(this.deleteBuffer.isLineEmpty()&&this.insertBuffer.isLineEmpty())this.flushChangeLines(),this.pushDiffCommonLine(Y);else this.pushDiffChangeLines(Y),this.flushChangeLines()}else if(G{switch(W[0]){case s:Q.align(W);break;case o:X.align(W);break;default:J.align(W)}}),J.getLines()}function RQ(Z,$){if($){let Q=Z.length-1;return Z.some((X,J)=>X[0]===c&&(J!==Q||X[1]!==` +`))}return Z.some((Q)=>Q[0]===c)}function FQ(Z,$,Q){if(Z!==$&&Z.length!==0&&$.length!==0){let X=Z.includes(` +`)||$.includes(` +`),[J,W]=J7(X?`${Z} +`:Z,X?`${$} +`:$,!0,Q);if(RQ(J,X)){let G=x1(Q),Y=DQ(J,G.changeColor);return M9(Y,W,G)}}return c0(Z.split(` +`),$.split(` +`),Q)}function J7(Z,$,Q,X){let[J,W]=PQ(Z,$,X);if(Q)t$(J);return[J,W]}function R9(Z,$){let{commonColor:Q}=x1($);return Q(Z)}var{AsymmetricMatcher:MQ,DOMCollection:OQ,DOMElement:IQ,Immutable:CQ,ReactElement:LQ,ReactTestComponent:TQ}=$0,W7=[TQ,LQ,IQ,OQ,CQ,MQ,$0.Error],m0={maxDepth:20,plugins:W7},G7={callToJSON:!1,maxDepth:8,plugins:W7},X0=(Z,$)=>$;function v1(Z,$,Q,X=X0){if(Object.is(Z,$))return"";let J=g6(Z),W=J,G=!1;if(J==="object"&&typeof Z.asymmetricMatch==="function"){if(Z.$$typeof!==Symbol.for("jest.asymmetricMatcher"))return;if(typeof Z.getExpectedType!=="function")return;W=Z.getExpectedType(),G=W==="string"}if(W!==g6($)){let O=function(I){return I.length<=R?I:`${I.slice(0,R)}...`},{aAnnotation:Y,aColor:z,aIndicator:B,bAnnotation:U,bColor:K,bIndicator:P}=x1(Q),w=d0(G7,Q),N=X1(Z,w),H=X1($,w),R=1e5;N=X("expected",O(N)),H=X("actual",O(H));let M=`${z(`${B} ${Y}:`)} +${N}`,T=`${K(`${P} ${U}:`)} +${H}`;return`${M} + +${T}`}if(G)return;switch(J){case"string":return c0(Z.split(` +`),$.split(` +`),Q);case"boolean":case"number":return kQ(Z,$,Q,X);case"map":return N9(d6(Z),d6($),Q,X);case"set":return N9(c6(Z),c6($),Q,X);default:return N9(Z,$,Q,X)}}function AQ(Z){return($,Q)=>{return Z[$]=Q,Q}}function kQ(Z,$,Q,X=X0){let J=X("expected",X1(Z,m0)),W=X("actual",X1($,m0));return J===W?"":c0(J.split(` +`),W.split(` +`),Q)}function d6(Z){return new Map(Array.from(Z.entries()).sort())}function c6(Z){return new Set(Array.from(Z.values()).sort())}function N9(Z,$,Q,X=X0){let J,W=!1;try{let Y=d0(m0,Q);J=l6(Z,$,Y,Q,X)}catch{W=!0}let G=R9(a6,Q);if(J===void 0||J===G){let Y=d0(G7,Q);if(J=l6(Z,$,Y,Q,X),J!==G&&!W)J=`${R9(QQ,Q)} + +${J}`}return J}function B7(Z){return d0(m0,Z)}function d0(Z,$){let{compareKeys:Q,printBasicPrototype:X,maxDepth:J}=x1($);return{...Z,compareKeys:Q,printBasicPrototype:X,maxDepth:J??Z.maxDepth}}function l6(Z,$,Q,X,J=X0){let W={...Q,indent:0},G=X1(Z,W),Y=X1($,W);if(G===Y)return R9(a6,X);else{let z=J("expected",X1(Z,Q)),B=J("actual",X1($,Q));return HQ(z.split(` +`),B.split(` +`),G.split(` +`),Y.split(` +`),X)}}var p6=20000;function i6(Z){return g1(Z)==="Object"&&typeof Z.asymmetricMatch==="function"}function f0(Z,$){let Q=g1(Z),X=g1($);return Q===X&&(Q==="Object"||Q==="Array")}function l0(Z,$,Q,X){let{aAnnotation:J,bAnnotation:W}=x1(Q);if(typeof $==="string"&&typeof Z==="string"&&$.length>0&&Z.length>0&&$.length<=p6&&Z.length<=p6&&$!==Z){if($.includes(` +`)||Z.includes(` +`))return FQ($,Z,Q);let[P]=J7($,Z,!0),w=P.some((O)=>O[0]===c),N=SQ(J,W),H=N(J)+EQ(n6(P,s,w)),R=N(W)+jQ(n6(P,o,w));return`${H} +${R}`}let G=q9($,{forceWritable:!0}),Y=q9(Z,{forceWritable:!0}),{replacedExpected:z,replacedActual:B}=g0(Y,G),U=X?AQ(X):X0;return v1(z,B,Q,U)}function g0(Z,$,Q=new WeakSet,X=new WeakSet){if(Z instanceof Error&&$ instanceof Error&&typeof Z.cause<"u"&&typeof $.cause>"u")return delete Z.cause,{replacedActual:Z,replacedExpected:$};if(!f0(Z,$))return{replacedActual:Z,replacedExpected:$};if(Q.has(Z)||X.has($))return{replacedActual:Z,replacedExpected:$};return Q.add(Z),X.add($),w9($).forEach((J)=>{let W=$[J],G=Z[J];if(i6(W)){if(W.asymmetricMatch(G))$[J]=G;else if("sample"in W&&W.sample!==void 0&&f0(G,W.sample)){let Y=g0(G,W.sample,Q,X);Z[J]=Y.replacedActual,$[J]=Y.replacedExpected}}else if(i6(G)){if(G.asymmetricMatch(W))Z[J]=W;else if("sample"in G&&G.sample!==void 0&&f0(G.sample,W)){let Y=g0(G.sample,W,Q,X);Z[J]=Y.replacedActual,$[J]=Y.replacedExpected}}else if(f0(G,W)){let Y=g0(G,W,Q,X);Z[J]=Y.replacedActual,$[J]=Y.replacedExpected}}),{replacedActual:Z,replacedExpected:$}}function SQ(...Z){let $=Z.reduce((Q,X)=>X.length>Q?X.length:Q,0);return(Q)=>`${Q}: ${" ".repeat($-Q.length)}`}var _Q="·";function Y7(Z){return Z.replace(/\s+$/gm,($)=>_Q.repeat($.length))}function jQ(Z){return f.red(Y7(a(Z)))}function EQ(Z){return f.green(Y7(a(Z)))}function n6(Z,$,Q){return Z.reduce((X,J)=>X+(J[0]===c?J[1]:J[0]===$?Q?f.inverse(J[1]):J[1]:""),"")}function F1(Z){return typeof Z==="function"&&"_isMockFunction"in Z&&Z._isMockFunction===!0}var xQ=new Set,O9=new Set,U7=new Set,vQ=new FinalizationRegistry((Z)=>{U7.delete(Z)}),z7=new WeakMap,K7=new WeakMap;function I9(Z={}){let{originalImplementation:$,restore:Q,mockImplementation:X,resetToMockImplementation:J,resetToMockName:W}=Z;if(Q)xQ.add(Q);let G=iQ($),Y=nQ(),z=yQ({config:G,state:Y,...Z}),B=(X||$)?.length??0;if(Object.defineProperty(z,"length",{writable:!0,enumerable:!1,value:B,configurable:!0}),W)G.mockName=z.name||"vi.fn()";z7.set(z,G),K7.set(Y,z);let U=new WeakRef(z);U7.add(U),vQ.register(z,U),z._isMockFunction=!0,z.getMockImplementation=()=>{return G.onceMockImplementations[0]||G.mockImplementation};let K=()=>{if(!Z.prototypeMembers?.length)H7(z,G.onceMockImplementations[0]||G.mockImplementation||$)};if(Object.defineProperty(z,"mock",{configurable:!1,enumerable:!0,writable:!1,value:Y}),z.mockImplementation=function(w){return G.mockImplementation=w,K(),z},z.mockImplementationOnce=function(w){return G.onceMockImplementations.push(w),K(),z},z.withImplementation=function(w,N){let{mockImplementation:H,onceMockImplementations:R}=G,O=()=>{G.mockImplementation=H,G.onceMockImplementations=R,K()};G.mockImplementation=w,G.onceMockImplementations=[],K();let M=N();if(typeof M==="object"&&typeof M?.then==="function")return M.then(()=>{return O(),z});else O();return z},z.mockReturnThis=function(){return z.mockImplementation(function(){return this})},z.mockReturnValue=function(w){return z.mockImplementation(function(){if(new.target)c1("mockReturnValue");return w})},z.mockReturnValueOnce=function(w){return z.mockImplementationOnce(function(){if(new.target)c1("mockReturnValueOnce");return w})},z.mockThrow=function(w){return z.mockImplementation(function(){throw w})},z.mockThrowOnce=function(w){return z.mockImplementationOnce(function(){throw w})},z.mockResolvedValue=function(w){return z.mockImplementation(function(){if(new.target)c1("mockResolvedValue");return Promise.resolve(w)})},z.mockResolvedValueOnce=function(w){return z.mockImplementationOnce(function(){if(new.target)c1("mockResolvedValueOnce");return Promise.resolve(w)})},z.mockRejectedValue=function(w){return z.mockImplementation(function(){if(new.target)c1("mockRejectedValue");return Promise.reject(w)})},z.mockRejectedValueOnce=function(w){return z.mockImplementationOnce(function(){if(new.target)c1("mockRejectedValueOnce");return Promise.reject(w)})},z.mockClear=function(){return Y.calls=[],Y.contexts=[],Y.instances=[],Y.invocationCallOrder=[],Y.results=[],Y.settledResults=[],O9.delete(z),z},z.mockReset=function(){return z.mockClear(),G.mockImplementation=J?X:void 0,G.mockName=W?z.name||"vi.fn()":"vi.fn()",G.onceMockImplementations=[],K(),z},z.mockRestore=function(){return z.mockReset(),Q?.()},z.mockName=function(w){if(typeof w==="string")G.mockName=w;return z},z.getMockName=function(){return G.mockName||"vi.fn()"},Symbol.dispose)z[Symbol.dispose]=()=>z.mockRestore();if(X)z.mockImplementation(X);else K();return z}function V7(Z){if(Z!=null&&F1(Z))return Z;return I9({mockImplementation:Z,resetToMockImplementation:!0})}function w7(Z,$,Q){p0(Z!=null,"The vi.spyOn() function could not find an object to spy upon. The first argument must be defined."),p0(typeof Z==="object"||typeof Z==="function","Vitest cannot spy on a primitive value.");let[X,J]=q7(Z,$)||[];p0(J||$ in Z,`The property "${String($)}" is not defined on the ${typeof Z}.`);let W=Q||"value",G=!1;if(W==="value"&&J&&J.value==null&&J.get)W="get",G=!0;let Y;if(J){if(Y=J[W],Y==null&&W==="value")Y=Z[$]}else if(W!=="value")Y=()=>Z[$];else Y=Z[$];let z=G&&Y?Y():Y,B=typeof z;if(p0(B==="function"||W!=="value"&&Y==null,`vi.spyOn() can only spy on a function. Received ${B}.`),F1(z))return z;let U=(w)=>{let{value:N,...H}=J||{configurable:!0,writable:!0};if(W!=="value")delete H.writable;H[W]=w,Object.defineProperty(Z,$,H)},P=I9({restore:()=>{if(X!==Z)Reflect.deleteProperty(Z,$);else if(J&&!Y)Object.defineProperty(Z,$,J);else U(Y)},originalImplementation:z,resetToMockName:!0});try{U(G?()=>P:P)}catch(w){if(w instanceof TypeError&&Symbol.toStringTag&&Z[Symbol.toStringTag]==="Module"&&(w.message.includes("Cannot redefine property")||w.message.includes("Cannot replace module namespace")||w.message.includes("can't redefine non-configurable property")))throw TypeError(`Cannot spy on export "${String($)}". Module namespace is not configurable in ESM. See: https://vitest.dev/guide/mocking/modules#mocking-a-module`,{cause:w});throw w}return P}function q7(Z,$){let Q=Object.getOwnPropertyDescriptor(Z,$);if(Q)return[Z,Q];let X=Object.getPrototypeOf(Z);while(X!==null){let J=Object.getOwnPropertyDescriptor(X,$);if(J)return[X,J];X=Object.getPrototypeOf(X)}}function p0(Z,$){if(!Z)throw Error($)}var hQ=1;function yQ({state:Z,config:$,name:Q,prototypeState:X,prototypeConfig:J,keepMembersImplementation:W,mockImplementation:G,prototypeMembers:Y=[]}){let z=$.mockOriginal,B=G,U=Q||z?.name||"Mock",K=function(){},P=X&&K7.get(X),w={[U]:function(...R){if(O9.add(w[U]),P)O9.add(P);bQ(R,Z,X),uQ(hQ++,Z,X);let O={type:"incomplete",value:void 0},M={type:"incomplete",value:void 0};fQ(O,Z,X),gQ(M,Z,X);let T=new.target?void 0:this,[I,C]=mQ(T,Z,X),[A,L]=dQ(T,Z,X),S=$.onceMockImplementations.shift()||$.mockImplementation||J?.onceMockImplementations.shift()||J?.mockImplementation||z||K,b,t,u=!1;try{if(new.target){if(Y.length===0)H7(N,S===K?void 0:S);b=Reflect.construct(S,R,new.target);for(let v of Y){let e=b[v];if(e!==N.prototype[v])continue;let r=F1(e),g=r?e.mock:void 0,d=r?z7.get(e):void 0;b[v]=I9({originalImplementation:W?d?.mockOriginal:void 0,prototypeState:g,prototypeConfig:d,keepMembersImplementation:W})}}else b=S.apply(this,R)}catch(v){if(t=v,u=!0,v instanceof TypeError&&v.message.includes("is not a constructor"))console.warn(`[vitest] The ${w[U].getMockName()} mock did not use 'function' or 'class' in its implementation, see https://vitest.dev/api/vi#vi-spyon for examples.`);throw v}finally{if(u)O.type="throw",O.value=t,M.type="rejected",M.value=t;else{if(O.type="return",O.value=b,new.target){if(Z.contexts[A-1]=b,Z.instances[I-1]=b,L!=null&&X)X.contexts[L-1]=b;if(C!=null&&X)X.instances[C-1]=b}if(b instanceof Promise)b.then((v)=>{M.type="fulfilled",M.value=v},(v)=>{M.type="rejected",M.value=v});else M.type="fulfilled",M.value=b}}return b}},N=w[U],H=z||B;if(H)cQ(N,H);return N}function H7(Z,$){let Q=Z.prototype;if(Q==null)return;let X=$?.prototype??Object.prototype;if(Q!==X&&Object.getPrototypeOf(Q)!==X)Object.setPrototypeOf(Q,X)}function bQ(Z,$,Q){$.calls.push(Z),Q?.calls.push(Z)}function uQ(Z,$,Q){$.invocationCallOrder.push(Z),Q?.invocationCallOrder.push(Z)}function fQ(Z,$,Q){$.results.push(Z),Q?.results.push(Z)}function gQ(Z,$,Q){$.settledResults.push(Z),Q?.settledResults.push(Z)}function mQ(Z,$,Q){let X=$.instances.push(Z),J=Q?.instances.push(Z);return[X,J]}function dQ(Z,$,Q){let X=$.contexts.push(Z),J=Q?.contexts.push(Z);return[X,J]}function cQ(Z,$){let{properties:Q,descriptors:X}=pQ($);for(let J of Q){let W=X[J];if(q7(Z,J))continue;Object.defineProperty(Z,J,W)}}var lQ=new Set(["length","name","prototype",Symbol.for("nodejs.util.promisify.custom")]);function pQ(Z){let $=new Set,Q={};while(Z&&Z!==Object.prototype&&Z!==Function.prototype){let X=[...Object.getOwnPropertyNames(Z),...Object.getOwnPropertySymbols(Z)];for(let J of X){if(Q[J]||lQ.has(J))continue;$.add(J),Q[J]=Object.getOwnPropertyDescriptor(Z,J)}Z=Object.getPrototypeOf(Z)}return{properties:$,descriptors:Q}}function iQ(Z){return{mockImplementation:void 0,mockOriginal:Z,mockName:"vi.fn()",onceMockImplementations:[]}}function nQ(){let Z={calls:[],contexts:[],instances:[],invocationCallOrder:[],settledResults:[],results:[],get lastCall(){return Z.calls.at(-1)}};return Z}function c1(Z){throw TypeError(`Cannot use \`${Z}\` when called with \`new\`. Use \`mockImplementation\` with a \`class\` keyword instead. See https://vitest.dev/api/mock#class-support for more information.`)}function oQ(Z){return Z&&(Z["@@__IMMUTABLE_ITERABLE__@@"]||Z["@@__IMMUTABLE_RECORD__@@"])}var rQ=Object.getPrototypeOf({});function P7(Z){if(Z instanceof Error)return`: ${Z.message}`;if(typeof Z==="string")return`: ${Z}`;return""}function M1(Z,$=new WeakMap){if(!Z||typeof Z==="string")return Z;if(Z instanceof Error&&"toJSON"in Z&&typeof Z.toJSON==="function"){let Q=Z.toJSON();if(Q&&Q!==Z&&typeof Q==="object"){if(typeof Z.message==="string")J0(()=>Q.message??=N7(Z.message));if(typeof Z.stack==="string")J0(()=>Q.stack??=Z.stack);if(typeof Z.name==="string")J0(()=>Q.name??=Z.name);if(Z.cause!=null)J0(()=>Q.cause??=M1(Z.cause,$))}return M1(Q,$)}if(typeof Z==="function")return`Function<${Z.name||"anonymous"}>`;if(typeof Z==="symbol")return Z.toString();if(typeof Z!=="object")return Z;if(typeof Buffer<"u"&&Z instanceof Buffer)return``;if(typeof Uint8Array<"u"&&Z instanceof Uint8Array)return``;if(oQ(Z))return M1(Z.toJSON(),$);if(Z instanceof Promise||Z.constructor&&Z.constructor.prototype==="AsyncFunction")return"Promise";if(typeof Element<"u"&&Z instanceof Element)return Z.tagName;if(typeof Z.toJSON==="function")return M1(Z.toJSON(),$);if($.has(Z))return $.get(Z);if(Array.isArray(Z)){let Q=Array(Z.length);return $.set(Z,Q),Z.forEach((X,J)=>{try{Q[J]=M1(X,$)}catch(W){Q[J]=P7(W)}}),Q}else{let Q=Object.create(null);$.set(Z,Q);let X=Z;while(X&&X!==rQ)Object.getOwnPropertyNames(X).forEach((J)=>{if(J in Q)return;try{Q[J]=M1(Z[J],$)}catch(W){delete Q[J],Q[J]=P7(W)}}),X=Object.getPrototypeOf(X);if(Z instanceof Error)J0(()=>Q.message=N7(Z.message));return Q}}function J0(Z){try{return Z()}catch{}}function N7(Z){return Z.replace(/\(0\s?,\s?__vite_ssr_import_\d+__.(\w+)\)/g,"$1").replace(/__(vite_ssr_import|vi_import)_\d+__\./g,"").replace(/getByTestId('__vitest_\d+__')/g,"page")}function C9(Z,$,Q=new WeakSet){if(!Z||typeof Z!=="object")return{message:String(Z)};let X=Z;if(X.showDiff||X.showDiff===void 0&&X.expected!==void 0&&X.actual!==void 0){let J={...$,...X.diffOptions};X.diff=l0(X.actual,X.expected,J,X),X.expected=D7(X.expected,J),X.actual=D7(X.actual,J)}try{if(!Q.has(X)&&typeof X.cause==="object")Q.add(X),X.cause=C9(X.cause,$,Q)}catch{}try{return M1(X)}catch(J){return M1(Error(`Failed to fully serialize error: ${J?.message} +Inner error message: ${X?.message}`))}}function D7(Z,$){if(typeof Z!=="string")return X1(Z,B7($));return Z}var i0=Symbol.for("matchers-object"),o0=Symbol.for("$$jest-matchers-object"),A7=Symbol.for("expect-global"),r0=Symbol.for("asymmetric-matchers-object");var n0=f.green,y9=f.red,aQ=f.inverse,sQ=f.bold,k1=f.dim;function tQ(Z,$="received",Q="expected",X={}){let{comment:J="",isDirectExpectCall:W=!1,isNot:G=!1,promise:Y="",secondArgument:z="",expectedColor:B=n0,receivedColor:U=y9,secondArgumentColor:K=n0}=X,P="",w="expect";if(!W&&$!=="")P+=k1(`${w}(`)+U($),w=")";if(Y!=="")P+=k1(`${w}.`)+Y,w="";if(G)P+=`${k1(`${w}.`)}not`,w="";if(Z.includes("."))w+=Z;else P+=k1(`${w}.`)+Z,w="";if(Q==="")w+="()";else{if(P+=k1(`${w}(`)+B(Q),z)P+=k1(", ")+K(z);w=")"}if(J!=="")w+=` // ${J}`;if(w!=="")P+=k1(w);return P}var eQ="·";function k7(Z){return Z.replace(/\s+$/gm,($)=>eQ.repeat($.length))}function ZX(Z){return y9(k7(a(Z)))}function $X(Z){return n0(k7(a(Z)))}function S7(){return{EXPECTED_COLOR:n0,RECEIVED_COLOR:y9,INVERTED_COLOR:aQ,BOLD_WEIGHT:sQ,DIM_COLOR:k1,diff:v1,matcherHint:tQ,printReceived:ZX,printExpected:$X,printDiffOrStringify:l0,printWithType:QX}}function QX(Z,$,Q){let X=g1($),J=X!=="null"&&X!=="undefined"?`${Z} has type: ${X} +`:"",W=`${Z} has value: ${Q($)}`;return J+W}function b9(){return globalThis[o0].customEqualityTesters}function E(Z,$,Q,X){return Q=Q||[],W0(Z,$,[],[],Q,X?_7:WX)}var aX=Function.prototype.toString;function R7(Z){return!!Z&&typeof Z==="object"&&"asymmetricMatch"in Z&&w1("Function",Z.asymmetricMatch)}function XX(Z,$,Q){let X=R7(Z),J=R7($);if(X&&J)return;if(X)return Z.asymmetricMatch($,Q);if(J)return $.asymmetricMatch(Z,Q)}function l1(Z){if(typeof Error.isError==="function")return Error.isError(Z);switch(Object.prototype.toString.call(Z)){case"[object Error]":case"[object Exception]":case"[object DOMException]":return!0;default:return Z instanceof Error}}function W0(Z,$,Q,X,J,W){let G=!0,Y=XX(Z,$,J);if(Y!==void 0)return Y;let z={equals:E};for(let N=0;NObject.getOwnPropertyDescriptor(Z,X).enumerable))}function WX(Z,$){return _7(Z,$)&&Z[$]!==void 0}function _7(Z,$){return Object.hasOwn(Z,$)}function w1(Z,$){return Object.prototype.toString.apply($)===`[object ${Z}]`}function M7(Z){return Z!==null&&typeof Z==="object"&&"nodeType"in Z&&typeof Z.nodeType==="number"&&"nodeName"in Z&&typeof Z.nodeName==="string"&&"isEqualNode"in Z&&typeof Z.isEqualNode==="function"}var j7="@@__IMMUTABLE_KEYED__@@",E7="@@__IMMUTABLE_SET__@@",GX="@@__IMMUTABLE_LIST__@@",a0="@@__IMMUTABLE_ORDERED__@@",BX="@@__IMMUTABLE_RECORD__@@";function YX(Z){return!!(Z&&Z[j7]&&!Z[a0])}function UX(Z){return!!(Z&&Z[E7]&&!Z[a0])}function s0(Z){return Z!=null&&typeof Z==="object"&&!Array.isArray(Z)}function zX(Z){return Boolean(Z&&s0(Z)&&Z[GX])}function KX(Z){return Boolean(Z&&s0(Z)&&Z[j7]&&Z[a0])}function VX(Z){return Boolean(Z&&s0(Z)&&Z[E7]&&Z[a0])}function wX(Z){return Boolean(Z&&s0(Z)&&Z[BX])}var x7=Symbol.iterator;function O7(Z){return!!(Z!=null&&Z[x7])}function W1(Z,$,Q=[],X=[],J=[]){if(typeof Z!=="object"||typeof $!=="object"||Array.isArray(Z)||Array.isArray($)||!O7(Z)||!O7($))return;if(Z.constructor!==$.constructor)return!1;let W=X.length;while(W--)if(X[W]===Z)return J[W]===$;X.push(Z),J.push($);let G=[...Q.filter((B)=>B!==W1),Y];function Y(B,U){return W1(B,U,[...Q],[...X],[...J])}if(Z.size!==void 0){if(Z.size!==$.size)return!1;else if(w1("Set",Z)||UX(Z)){let B=!0;for(let U of Z)if(!$.has(U)){let K=!1;for(let P of $)if(E(U,P,G)===!0)K=!0;if(K===!1){B=!1;break}}return X.pop(),J.pop(),B}else if(w1("Map",Z)||YX(Z)){let B=!0;for(let U of Z)if(!$.has(U[0])||!E(U[1],$.get(U[0]),G)){let K=!1;for(let P of $){let w=E(U[0],P[0],G),N=!1;if(w===!0)N=E(U[1],P[1],G);if(N===!0)K=!0}if(K===!1){B=!1;break}}return X.pop(),J.pop(),B}}let z=$[x7]();for(let B of Z){let U=z.next();if(U.done||!E(B,U.value,G))return!1}if(!z.next().done)return!1;if(!zX(Z)&&!KX(Z)&&!VX(Z)&&!wX(Z)){let B=Object.entries(Z),U=Object.entries($);if(!E(B,U,G))return!1}return X.pop(),J.pop(),!0}function u9(Z,$){if(!Z||typeof Z!=="object"||Z===Object.prototype)return!1;return Object.hasOwn(Z,$)||u9(Object.getPrototypeOf(Z),$)}function qX(Z){return Q0(Z)&&!l1(Z)&&!Array.isArray(Z)&&!(Z instanceof Date)&&!(Z instanceof Set)&&!(Z instanceof Map)}function G0(Z,$,Q=[]){let X=Q.filter((W)=>W!==G0),J=(W=new WeakMap)=>(G,Y)=>{if(!qX(Y))return;return Object.keys(Y).every((z)=>{if(Y[z]!=null&&typeof Y[z]==="object"){if(W.has(Y[z]))return E(G[z],Y[z],X);W.set(Y[z],!0)}let B=G!=null&&u9(G,z)&&E(G[z],Y[z],[...X,J(W)]);return W.delete(Y[z]),B})};return J()(Z,$)}function I7(Z,$){if(Z==null||$==null||Z.constructor===$.constructor)return;return!1}function C7(Z,$){let Q=Z,X=$;if(!(Z instanceof DataView&&$ instanceof DataView)){if(!(Z instanceof ArrayBuffer)||!($ instanceof ArrayBuffer))return;try{Q=new DataView(Z),X=new DataView($)}catch{return}}if(Q.byteLength!==X.byteLength)return!1;for(let J=0;JG!==A9);return E(Z,$,W,!0)&&E(X,J)}function HX(Z,$="#{this}",Q="#{exp}"){let X=`expected ${$} to be ${Q} // Object.is equality`;if(["toStrictEqual","toEqual"].includes(Z))return`${X} + +If it should pass with deep equality, replace "toBe" with "${Z}" + +Expected: ${$} +Received: serializes to the same string +`;return X}function PX(Z,$){return`${$} ${Z}${$===1?"":"s"}`}function L9(Z){return[...Object.keys(Z),...Object.getOwnPropertySymbols(Z).filter(($)=>Object.getOwnPropertyDescriptor(Z,$)?.enumerable)]}function NX(Z,$,Q){let X=0,J=(W=new WeakMap)=>(G,Y)=>{if(Array.isArray(G)){if(Array.isArray(Y)&&Y.length===G.length)return Y.map((z,B)=>J(W)(G[B],z))}else if(G instanceof Date)return G;else if(Q0(G)&&Q0(Y)){if(E(G,Y,[...Q,W1,G0]))return Y;let z={};if(W.set(G,z),typeof G.constructor==="function"&&typeof G.constructor.name==="string")Object.defineProperty(z,"constructor",{enumerable:!1,value:G.constructor});for(let B of L9(G))if(u9(Y,B))z[B]=W.has(G[B])?W.get(G[B]):J(W)(G[B],Y[B]);else if(!W.has(G[B])){if(X+=1,Q0(G[B]))X+=L9(G[B]).length;J(W)(G[B],Y[B])}if(L9(z).length>0)return z}return G};return{subset:J()(Z,$),stripped:X}}function DX(Z){return!!Z&&(typeof Z==="object"||typeof Z==="function")&&Z["~standard"]&&typeof Z["~standard"].validate==="function"}if(!Object.hasOwn(globalThis,i0)){let Z=new WeakMap,$=Object.create(null),Q=[],X=Object.create(null);Object.defineProperty(globalThis,i0,{configurable:!0,get:()=>Z}),Object.defineProperty(globalThis,o0,{configurable:!0,get:()=>({state:Z.get(globalThis[A7]),matchers:$,customEqualityTesters:Q})}),Object.defineProperty(globalThis,r0,{configurable:!0,get:()=>X})}function B0(Z){return globalThis[i0].get(Z)}function t0(Z,$){let Q=globalThis[i0],X=Q.get($),J=Object.defineProperties(X||{},Object.getOwnPropertyDescriptors(Z));if(!X)Q.set($,J)}class q1{sample;inverse;$$typeof=Symbol.for("jest.asymmetricMatcher");constructor(Z,$=!1){this.sample=Z,this.inverse=$}getMatcherContext(Z){return{...B0(Z||globalThis[A7]),equals:E,isNot:this.inverse,customTesters:b9(),utils:{...S7(),diff:v1,stringify:a,iterableEquality:W1,subsetEquality:G0}}}}q1.prototype[Symbol.for("chai/inspect")]=function(Z){let $=a(this,Z.depth,{min:!0});if($.length<=Z.truncate)return $;return`${this.toString()}{…}`};class k9 extends q1{constructor(Z,$=!1){if(!w1("String",Z))throw Error("Expected is not a string");super(Z,$)}asymmetricMatch(Z){let $=w1("String",Z)&&Z.includes(this.sample);return this.inverse?!$:$}toString(){return`String${this.inverse?"Not":""}Containing`}getExpectedType(){return"string"}}class v7 extends q1{asymmetricMatch(Z){return Z!=null}toString(){return"Anything"}toAsymmetricMatcher(){return"Anything"}}class S9 extends q1{constructor(Z,$=!1){super(Z,$)}getPrototype(Z){if(Object.getPrototypeOf)return Object.getPrototypeOf(Z);if(Z.constructor.prototype===Z)return null;return Z.constructor.prototype}hasProperty(Z,$){if(!Z)return!1;if(Object.hasOwn(Z,$))return!0;return this.hasProperty(this.getPrototype(Z),$)}getProperties(Z){return[...Object.keys(Z),...Object.getOwnPropertySymbols(Z).filter(($)=>Object.getOwnPropertyDescriptor(Z,$)?.enumerable)]}asymmetricMatch(Z,$){if(typeof this.sample!=="object")throw TypeError(`You must provide an object to ${this.toString()}, not '${typeof this.sample}'.`);let Q=!0,X=this.getProperties(this.sample);for(let J of X){if(!this.hasProperty(Z,J)){Q=!1;break}let W=this.sample[J],G=Z[J];if(!E(W,G,$)){Q=!1;break}}return this.inverse?!Q:Q}toString(){return`Object${this.inverse?"Not":""}Containing`}getExpectedType(){return"object"}}class _9 extends q1{constructor(Z,$=!1){super(Z,$)}asymmetricMatch(Z,$){if(!Array.isArray(this.sample))throw TypeError(`You must provide an array to ${this.toString()}, not '${typeof this.sample}'.`);let Q=this.sample.length===0||Array.isArray(Z)&&this.sample.every((X)=>Z.some((J)=>E(X,J,$)));return this.inverse?!Q:Q}toString(){return`Array${this.inverse?"Not":""}Containing`}getExpectedType(){return"array"}}class h7 extends q1{constructor(Z){if(typeof Z>"u")throw TypeError("any() expects to be passed a constructor function. Please pass one or use anything() to match any object.");super(Z)}fnNameFor(Z){if(Z.name)return Z.name;let Q=Function.prototype.toString.call(Z).match(/^(?:async)?\s*function\s*(?:\*\s*)?([\w$]+)\s*\(/);return Q?Q[1]:""}asymmetricMatch(Z){if(this.sample===String)return typeof Z=="string"||Z instanceof String;if(this.sample===Number)return typeof Z=="number"||Z instanceof Number;if(this.sample===Function)return typeof Z=="function"||typeof Z==="function";if(this.sample===Boolean)return typeof Z=="boolean"||Z instanceof Boolean;if(this.sample===BigInt)return typeof Z=="bigint"||Z instanceof BigInt;if(this.sample===Symbol)return typeof Z=="symbol"||Z instanceof Symbol;if(this.sample===Object)return typeof Z=="object";return Z instanceof this.sample}toString(){return"Any"}getExpectedType(){if(this.sample===String)return"string";if(this.sample===Number)return"number";if(this.sample===Function)return"function";if(this.sample===Object)return"object";if(this.sample===Boolean)return"boolean";return this.fnNameFor(this.sample)}toAsymmetricMatcher(){return`Any<${this.fnNameFor(this.sample)}>`}}class j9 extends q1{constructor(Z,$=!1){if(!w1("String",Z)&&!w1("RegExp",Z))throw Error("Expected is not a String or a RegExp");super(new RegExp(Z),$)}asymmetricMatch(Z){let $=w1("String",Z)&&this.sample.test(Z);return this.inverse?!$:$}toString(){return`String${this.inverse?"Not":""}Matching`}getExpectedType(){return"string"}}class E9 extends q1{precision;constructor(Z,$=2,Q=!1){if(!w1("Number",Z))throw Error("Expected is not a Number");if(!w1("Number",$))throw Error("Precision is not a Number");super(Z);this.inverse=Q,this.precision=$}asymmetricMatch(Z){if(!w1("Number",Z))return!1;let $=!1;if(Z===Number.POSITIVE_INFINITY&&this.sample===Number.POSITIVE_INFINITY)$=!0;else if(Z===Number.NEGATIVE_INFINITY&&this.sample===Number.NEGATIVE_INFINITY)$=!0;else $=Math.abs(this.sample-Z)<10**-this.precision/2;return this.inverse?!$:$}toString(){return`Number${this.inverse?"Not":""}CloseTo`}getExpectedType(){return"number"}toAsymmetricMatcher(){return[this.toString(),this.sample,`(${PX("digit",this.precision)})`].join(" ")}}class x9 extends q1{result;constructor(Z,$=!1){if(!DX(Z))throw TypeError("SchemaMatching expected to receive a Standard Schema.");super(Z,$)}asymmetricMatch(Z){let $=this.sample["~standard"].validate(Z);if($ instanceof Promise)throw TypeError("Async schema validation is not supported in asymmetric matchers.");this.result=$;let Q=!this.result.issues||this.result.issues.length===0;return this.inverse?!Q:Q}toString(){return`Schema${this.inverse?"Not":""}Matching`}getExpectedType(){return"object"}toAsymmetricMatcher(){let{utils:Z}=this.getMatcherContext();if((this.result?.issues||[]).length>0)return`${this.toString()} ${Z.stringify(this.result,void 0,{printBasicPrototype:!1})}`;return this.toString()}}var y7=(Z,$)=>{$.addMethod(Z.expect,"anything",()=>new v7),$.addMethod(Z.expect,"any",(Q)=>new h7(Q)),$.addMethod(Z.expect,"stringContaining",(Q)=>new k9(Q)),$.addMethod(Z.expect,"objectContaining",(Q)=>new S9(Q)),$.addMethod(Z.expect,"arrayContaining",(Q)=>new _9(Q)),$.addMethod(Z.expect,"stringMatching",(Q)=>new j9(Q)),$.addMethod(Z.expect,"closeTo",(Q,X)=>new E9(Q,X)),$.addMethod(Z.expect,"schemaMatching",(Q)=>new x9(Q)),Z.expect.not={stringContaining:(Q)=>new k9(Q,!0),objectContaining:(Q)=>new S9(Q,!0),arrayContaining:(Q)=>new _9(Q,!0),stringMatching:(Q)=>new j9(Q,!0),closeTo:(Q,X)=>new E9(Q,X,!0),schemaMatching:(Q)=>new x9(Q,!0)}};function L7(Z,$,Q){let X=Z.flag($,"soft")?".soft":"",J=Z.flag($,"negate")?"not.":"",W=`${Z.flag($,"_name")}(${Q?"expected":""})`,G=Z.flag($,"promise"),Y=G?`.${G}`:"";return`expect${X}(actual)${Y}.${J}${W}`}function T7(Z,$,Q,X,J){let W=Z;if(W&&$ instanceof Promise){if($=$.finally(()=>{if(!W.promises)return;let Y=W.promises.indexOf($);if(Y!==-1)W.promises.splice(Y,1)}),!W.promises)W.promises=[];if(J)$=$.then(H9,(Y)=>{v9(W,Y)});W.promises.push($);let G=!1;return W.onFinished??=[],W.onFinished.push(()=>{if(!G){let Y=Error(`Promise returned by \`${Q}\` was not awaited. This assertion is asynchronous and must be awaited; otherwise, it is not guaranteed to complete before the test finishes: + +await ${Q} +`);if(X.stack)Y.stack=X.stack.replace(X.message,Y.message);throw Y}}),{then(Y,z){return G=!0,$.then(Y,z)},catch(Y){return G=!0,$.catch(Y)},finally(Y){return G=!0,$.finally(Y)},[Symbol.toStringTag]:"Promise"}}return $}function v9(Z,$){Z.result||={state:"fail"},Z.result.state="fail",Z.result.errors||=[],Z.result.errors.push(C9($))}function b7(Z,$,Q){return function(...X){if($!=="withTest")Z.flag(this,"_name",$);if(!Z.flag(this,"soft"))try{return Q.apply(this,X)}finally{}let J=Z.flag(this,"vitest-test");if(!J)throw Error("expect.soft() can only be used inside a test");try{let W=Q.apply(this,X);if(W&&typeof W==="object"&&typeof W.then==="function")return W.then(H9,(G)=>{v9(J,G)});return W}catch(W){v9(J,W)}}}var u7=(Z,$)=>{let{AssertionError:Q}=Z,X=b9();function J(B,U){let K=(P)=>{let w=b7($,P,U);$.addMethod(Z.Assertion.prototype,P,w),$.addMethod(globalThis[o0].matchers,P,w)};if(Array.isArray(B))B.forEach((P)=>K(P));else K(B)}["throw","throws","Throw"].forEach((B)=>{$.overwriteMethod(Z.Assertion.prototype,B,(U)=>{return function(...K){let P=$.flag(this,"promise"),w=$.flag(this,"object"),N=$.flag(this,"negate");if(P==="rejects")$.flag(this,"object",()=>{throw w});else if(P==="resolves"&&typeof w!=="function")if(!N){let H=$.flag(this,"message")||"expected promise to throw an error, but it didn't";throw new Q(H,{showDiff:!1},$.flag(this,"ssfi"))}else return;U.apply(this,K)}})}),J("withTest",function(B){return $.flag(this,"vitest-test",B),this}),J("toEqual",function(B){let U=$.flag(this,"object"),K=E(U,B,[...X,W1]);return this.assert(K,"expected #{this} to deeply equal #{exp}","expected #{this} to not deeply equal #{exp}",B,U)}),J("toStrictEqual",function(B){let U=$.flag(this,"object"),K=E(U,B,[...X,W1,I7,A9,C7],!0);return this.assert(K,"expected #{this} to strictly equal #{exp}","expected #{this} to not strictly equal #{exp}",B,U)}),J("toBe",function(B){let U=this._obj,K=Object.is(U,B),P="";if(!K){if(E(U,B,[...X,W1,I7,A9,C7],!0))P="toStrictEqual";else if(E(U,B,[...X,W1]))P="toEqual"}return this.assert(K,HX(P),"expected #{this} not to be #{exp} // Object.is equality",B,U)}),J("toMatchObject",function(B){let U=this._obj,K=E(U,B,[...X,W1,G0]),P=$.flag(this,"negate"),{subset:w,stripped:N}=NX(U,B,X);if(K&&P||!K&&!P){let H=$.getMessage(this,[K,"expected #{this} to match object #{exp}","expected #{this} to not match object #{exp}",B,w,!1]),R=N===0?H:`${H} +(${N} matching ${N===1?"property":"properties"} omitted from actual)`;throw new Q(R,{showDiff:!0,expected:B,actual:w})}}),J("toMatch",function(B){let U=this._obj;if(typeof U!=="string")throw TypeError(`.toMatch() expects to receive a string, but got ${typeof U}`);return this.assert(typeof B==="string"?U.includes(B):U.match(B),"expected #{this} to match #{exp}","expected #{this} not to match #{exp}",B,U)}),J("toContain",function(B){let U=this._obj;if(typeof Node<"u"&&U instanceof Node){if(!(B instanceof Node))throw TypeError(`toContain() expected a DOM node as the argument, but got ${typeof B}`);return this.assert(U.contains(B),"expected #{this} to contain element #{exp}","expected #{this} not to contain element #{exp}",B,U)}if(typeof DOMTokenList<"u"&&U instanceof DOMTokenList){N1(B,"class name",["string"]);let P=$.flag(this,"negate")?U.value.replace(B,"").trim():`${U.value} ${B}`;return this.assert(U.contains(B),`expected "${U.value}" to contain "${B}"`,`expected "${U.value}" not to contain "${B}"`,P,U.value)}if(typeof U==="string"&&typeof B==="string")return this.assert(U.includes(B),"expected #{this} to contain #{exp}","expected #{this} not to contain #{exp}",B,U);if(U!=null&&typeof U!=="string")$.flag(this,"object",Array.from(U));return this.contain(B)}),J("toContainEqual",function(B){let U=$.flag(this,"object"),K=Array.from(U).findIndex((P)=>{return E(P,B,X)});this.assert(K!==-1,"expected #{this} to deep equally contain #{exp}","expected #{this} to not deep equally contain #{exp}",B)}),J("toBeTruthy",function(){let B=$.flag(this,"object");this.assert(Boolean(B),"expected #{this} to be truthy","expected #{this} to not be truthy",!0,B)}),J("toBeFalsy",function(){let B=$.flag(this,"object");this.assert(!B,"expected #{this} to be falsy","expected #{this} to not be falsy",!1,B)}),J("toBeGreaterThan",function(B){let U=this._obj;return N1(U,"actual",["number","bigint"]),N1(B,"expected",["number","bigint"]),this.assert(U>B,`expected ${U} to be greater than ${B}`,`expected ${U} to be not greater than ${B}`,B,U,!1)}),J("toBeGreaterThanOrEqual",function(B){let U=this._obj;return N1(U,"actual",["number","bigint"]),N1(B,"expected",["number","bigint"]),this.assert(U>=B,`expected ${U} to be greater than or equal to ${B}`,`expected ${U} to be not greater than or equal to ${B}`,B,U,!1)}),J("toBeLessThan",function(B){let U=this._obj;return N1(U,"actual",["number","bigint"]),N1(B,"expected",["number","bigint"]),this.assert(UString(M).replace(/([.[\]])/g,"\\$1")).join(".");let U=this._obj;if(U==null)throw TypeError(`.toHaveProperty() expects to receive a valid object, but got ${U}`);let[K,P]=B,w=()=>{if(Object.hasOwn(U,K))return{value:U[K],exists:!0};return $.getPathInfo(U,K)},{value:N,exists:H}=w(),R=H&&(B.length===1||E(P,N,X)),O=B.length===1?"":` with value ${E6(P,{truncate:40})}`;return this.assert(R,`expected #{this} to have property "${K}"${O}`,`expected #{this} to not have property "${K}"${O}`,P,H?N:void 0)}),J("toBeCloseTo",function(B,U=2){let K=this._obj,P=!1,w=0,N=0;if(B===Number.POSITIVE_INFINITY&&K===Number.POSITIVE_INFINITY)P=!0;else if(B===Number.NEGATIVE_INFINITY&&K===Number.NEGATIVE_INFINITY)P=!0;else w=10**-U/2,N=Math.abs(K-B),P=N0,w=$.flag(this,"negate"),N=$.getMessage(this,[P,`expected "${U}" to be called at least once`,`expected "${U}" to not be called at all, but actually been called ${K} times`,!0,P]);if(P&&w)N=T9(B,N);if(P&&w||!P&&!w)throw new Q(N)});function Y(B,U){return B.length===U.length&&B.every((K,P)=>E(K,U[P],[...X,W1]))}J(["toHaveBeenCalledWith","toBeCalledWith"],function(...B){let U=G(this),K=U.getMockName(),P=U.mock.calls.some((N)=>Y(N,B)),w=$.flag(this,"negate");if(P&&w||!P&&!w){let N=$.getMessage(this,[P,`expected "${K}" to be called with arguments: #{exp}`,`expected "${K}" to not be called with arguments: #{exp}`,B]);throw new Q(T9(U,N,B))}}),J("toHaveBeenCalledExactlyOnceWith",function(...B){let U=G(this),K=U.getMockName(),P=U.mock.calls.length,N=U.mock.calls.some((R)=>Y(R,B))&&P===1,H=$.flag(this,"negate");if(N&&H||!N&&!H){let R=$.getMessage(this,[N,`expected "${K}" to be called once with arguments: #{exp}`,`expected "${K}" to not be called once with arguments: #{exp}`,B]);throw new Q(T9(U,R,B))}}),J("toHaveBeenNthCalledWith",function(B,...U){let K=G(this),P=K.getMockName(),w=K.mock.calls[B-1],N=K.mock.calls.length,H=B<=N;this.assert(w&&Y(w,U),`expected ${m1(B)} "${P}" call to have been called with #{exp}${H?"":`, but called only ${N} times`}`,`expected ${m1(B)} "${P}" call to not have been called with #{exp}`,U,w,H)}),J("toHaveBeenLastCalledWith",function(...B){let U=G(this),K=U.getMockName(),P=U.mock.calls.at(-1);this.assert(P&&Y(P,B),`expected last "${K}" call to have been called with #{exp}`,`expected last "${K}" call to not have been called with #{exp}`,B,P)});function z(B,U,K){let P=B.mock.invocationCallOrder,w=U.mock.invocationCallOrder;if(P.length===0)return!K;if(w.length===0)return!1;return P[0]"u"||B instanceof RegExp)return this.throws(B);let U=this._obj,K=$.flag(this,"promise"),P=$.flag(this,"negate"),w=null;if(K==="rejects")w=U;else if(K==="resolves"&&typeof U!=="function")if(!P){let H=$.flag(this,"message")||"expected promise to throw an error, but it didn't";throw new Q(H,{showDiff:!1},$.flag(this,"ssfi"))}else return;else{let H=!1;try{U()}catch(R){H=!0,w=R}if(!H&&!P){let R=$.flag(this,"message")||"expected function to throw an error, but it didn't";throw new Q(R,{showDiff:!1},$.flag(this,"ssfi"))}}if(typeof B==="function"){let H=B.name||B.prototype.constructor.name;return this.assert(w&&w instanceof B,`expected error to be instance of ${H}`,`expected error not to be instance of ${H}`,B,w)}if(l1(B)){let H=E(w,B,[...X,W1]);return this.assert(H,"expected a thrown error to be #{exp}","expected a thrown error not to be #{exp}",B,w)}if(typeof B==="object"&&"asymmetricMatch"in B&&typeof B.asymmetricMatch==="function"){let H=B;return this.assert(w&&H.asymmetricMatch(w),"expected error to match asymmetric matcher","expected error not to match asymmetric matcher",H,w)}let N=E(w,B,[...X,W1]);return this.assert(N,"expected a thrown value to equal #{exp}","expected a thrown value not to equal #{exp}",B,w)}),[{name:"toHaveResolved",condition:(B)=>B.mock.settledResults.length>0&&B.mock.settledResults.some(({type:U})=>U==="fulfilled"),action:"resolved"},{name:["toHaveReturned","toReturn"],condition:(B)=>B.mock.calls.length>0&&B.mock.results.some(({type:U})=>U!=="throw"),action:"called"}].forEach(({name:B,condition:U,action:K})=>{J(B,function(){let P=G(this),w=P.getMockName(),N=U(P);this.assert(N,`expected "${w}" to be successfully ${K} at least once`,`expected "${w}" to not be successfully ${K}`,N,!N,!1)})}),[{name:"toHaveResolvedTimes",condition:(B,U)=>B.mock.settledResults.reduce((K,{type:P})=>P==="fulfilled"?++K:K,0)===U,action:"resolved"},{name:["toHaveReturnedTimes","toReturnTimes"],condition:(B,U)=>B.mock.results.reduce((K,{type:P})=>P==="throw"?K:++K,0)===U,action:"called"}].forEach(({name:B,condition:U,action:K})=>{J(B,function(P){let w=G(this),N=w.getMockName(),H=U(w,P);this.assert(H,`expected "${N}" to be successfully ${K} ${P} times`,`expected "${N}" to not be successfully ${K} ${P} times`,`expected resolved times: ${P}`,`received resolved times: ${H}`,!1)})}),[{name:"toHaveResolvedWith",condition:(B,U)=>B.mock.settledResults.some(({type:K,value:P})=>K==="fulfilled"&&E(U,P)),action:"resolve"},{name:["toHaveReturnedWith","toReturnWith"],condition:(B,U)=>B.mock.results.some(({type:K,value:P})=>K==="return"&&E(U,P)),action:"return"}].forEach(({name:B,condition:U,action:K})=>{J(B,function(P){let w=G(this),N=U(w,P),H=$.flag(this,"negate");if(N&&H||!N&&!H){let R=w.getMockName(),O=$.getMessage(this,[N,`expected "${R}" to ${K} with: #{exp} at least once`,`expected "${R}" to not ${K} with: #{exp}`,P]),M=K==="return"?w.mock.results:w.mock.settledResults;throw new Q(RX(w,M,O,P))}})}),[{name:"toHaveLastResolvedWith",condition:(B,U)=>{let K=B.mock.settledResults.at(-1);return Boolean(K&&K.type==="fulfilled"&&E(K.value,U))},action:"resolve"},{name:"toHaveLastReturnedWith",condition:(B,U)=>{let K=B.mock.results.at(-1);return Boolean(K&&K.type==="return"&&E(K.value,U))},action:"return"}].forEach(({name:B,condition:U,action:K})=>{J(B,function(P){let w=G(this),H=(K==="return"?w.mock.results:w.mock.settledResults).at(-1),R=w.getMockName();this.assert(U(w,P),`expected last "${R}" call to ${K} #{exp}`,`expected last "${R}" call to not ${K} #{exp}`,P,H?.value)})}),[{name:"toHaveNthResolvedWith",condition:(B,U,K)=>{let P=B.mock.settledResults[U-1];return P&&P.type==="fulfilled"&&E(P.value,K)},action:"resolve"},{name:"toHaveNthReturnedWith",condition:(B,U,K)=>{let P=B.mock.results[U-1];return P&&P.type==="return"&&E(P.value,K)},action:"return"}].forEach(({name:B,condition:U,action:K})=>{J(B,function(P,w){let N=G(this),H=N.getMockName(),O=(K==="return"?N.mock.results:N.mock.settledResults)[P-1],M=`${m1(P)} call`;this.assert(U(N,P,w),`expected ${M} "${H}" call to ${K} #{exp}`,`expected ${M} "${H}" call to not ${K} #{exp}`,w,O?.value)})}),J("withContext",function(B){for(let U in B)$.flag(this,U,B[U]);return this}),$.addProperty(Z.Assertion.prototype,"resolves",function(){let U=Error("resolves");$.flag(this,"promise","resolves"),$.flag(this,"error",U);let K=$.flag(this,"vitest-test"),P=$.flag(this,"object");if($.flag(this,"poll"))throw SyntaxError("expect.poll() is not supported in combination with .resolves");if(typeof P?.then!=="function")throw TypeError(`You must provide a Promise to expect() when using .resolves, not '${typeof P}'.`);let w=new Proxy(this,{get:(N,H,R)=>{let O=Reflect.get(N,H,R);if(typeof O!=="function")return O instanceof Z.Assertion?w:O;return(...M)=>{$.flag(this,"_name",H);let T=Promise.resolve(P).then((I)=>{return $.flag(this,"object",I),O.call(this,...M)},(I)=>{let C=new Q(`promise rejected "${$.inspect(I)}" instead of resolving`,{showDiff:!1});throw C.cause=I,C}).catch((I)=>{if(l1(I)&&U.stack)I.stack=U.stack.replace(U.message,I.message);throw I});return T7(K,T,L7($,this,!!M.length),U,$.flag(this,"soft"))}}});return w}),$.addProperty(Z.Assertion.prototype,"rejects",function(){let U=Error("rejects");$.flag(this,"promise","rejects"),$.flag(this,"error",U);let K=$.flag(this,"vitest-test"),P=$.flag(this,"object"),w=typeof P==="function"?P():P;if($.flag(this,"poll"))throw SyntaxError("expect.poll() is not supported in combination with .rejects");if(typeof w?.then!=="function")throw TypeError(`You must provide a Promise to expect() when using .rejects, not '${typeof w}'.`);let N=new Proxy(this,{get:(H,R,O)=>{let M=Reflect.get(H,R,O);if(typeof M!=="function")return M instanceof Z.Assertion?N:M;return(...T)=>{$.flag(this,"_name",R);let I=Promise.resolve(w).then((C)=>{throw new Q(`promise resolved "${$.inspect(C)}" instead of rejecting`,{showDiff:!0,expected:Error("rejected promise"),actual:C})},(C)=>{return $.flag(this,"object",C),M.call(this,...T)}).catch((C)=>{if(l1(C)&&U.stack)C.stack=U.stack.replace(U.message,C.message);throw C});return T7(K,I,L7($,this,!!T.length),U,$.flag(this,"soft"))}}});return N})};function T9(Z,$,Q){if(Z.mock.calls.length)$+=f.gray(` + +Received: + +${Z.mock.calls.map((X,J)=>{let W=f.bold(` ${m1(J+1)} ${Z.getMockName()} call: + +`);if(Q)W+=v1(Q,X,{omitAnnotationLines:!0});else W+=a(X).split(` +`).map((G)=>` ${G}`).join(` +`);return W+=` +`,W}).join(` +`)}`);return $+=f.gray(` + +Number of calls: ${f.bold(Z.mock.calls.length)} +`),$}function RX(Z,$,Q,X){if($.length)Q+=f.gray(` + +Received: + +${$.map((J,W)=>{let G=f.bold(` ${m1(W+1)} ${Z.getMockName()} call return: + +`);if(X)G+=v1(X,J.value,{omitAnnotationLines:!0});else G+=a(J).split(` +`).map((Y)=>` ${Y}`).join(` +`);return G+=` +`,G}).join(` +`)}`);return Q+=f.gray(` + +Number of calls: ${f.bold(Z.mock.calls.length)} +`),Q}function FX(Z,$){let Q=Z._obj,X=K1.flag(Z,"negate"),J=K1.flag(Z,"promise")||"",W=K1.flag(Z,"message"),G={...S7(),diff:v1,stringify:a,iterableEquality:W1,subsetEquality:G0},Y=K1.flag(Z,"vitest-test"),z=Y?.fullTestName??"";if(Y?.type!=="test")Y=void 0;let B={...B0($),currentTestName:z,customTesters:b9(),isNot:X,utils:G,promise:J,equals:E,suppressedErrors:[],soft:K1.flag(Z,"soft"),poll:K1.flag(Z,"poll"),assertion:Z};return Object.assign(B,{task:Y}),{state:B,isNot:X,obj:Q,customMessage:W}}class h9 extends Error{actual;expected;__vitest_error_context__;constructor(Z,$,Q,X){super(Z);this.actual=$,this.expected=Q,this.__vitest_error_context__=X}}function MX(Z,$,Q){return(X,J)=>{Object.entries(Q).forEach(([W,G])=>{function Y(...K){let{state:P,isNot:w,obj:N,customMessage:H}=FX(this,$),R=G.call(P,N,...K);if(R&&typeof R==="object"&&typeof R.then==="function")return R.then(({pass:L,message:S,actual:b,expected:t,meta:u})=>{if(L&&w||!L&&!w){let v=(H?`${H}: `:"")+S();throw new h9(v,b,t,{assertionName:W,meta:u})}});let{pass:O,message:M,actual:T,expected:I,meta:C}=R;if(O&&w||!O&&!w){let A=(H?`${H}: `:"")+M();throw new h9(A,T,I,{assertionName:W,meta:C})}}let z=b7(J,W,Y);if(J.addMethod(globalThis[o0].matchers,W,z),J.addMethod(Z.Assertion.prototype,W,z),G.__vitest_poll_takeover__){let K=Z.Assertion.prototype[W];Object.defineProperty(K,"__vitest_poll_takeover__",{value:!0})}class B extends q1{constructor(K=!1,...P){super(P,K)}asymmetricMatch(K){let{pass:P}=G.call(this.getMatcherContext($),K,...this.sample);return this.inverse?!P:P}toString(){return`${this.inverse?"not.":""}${W}`}getExpectedType(){return"any"}toAsymmetricMatcher(){return`${this.toString()}<${this.sample.map((K)=>a(K)).join(", ")}>`}}let U=(...K)=>new B(!1,...K);Object.defineProperty($,W,{configurable:!0,enumerable:!0,value:U,writable:!0}),Object.defineProperty($.not,W,{configurable:!0,enumerable:!0,value:(...K)=>new B(!0,...K),writable:!0}),Object.defineProperty(globalThis[r0],W,{configurable:!0,enumerable:!0,value:U,writable:!0})})}}var f7=(Z,$)=>{$.addMethod(Z.expect,"extend",(Q,X)=>{T1(MX(Z,Q,X))})};n9();T1(f7);T1(u7);T1(y7);function OX(){let Z=($,Q)=>{let{assertionCalls:X}=B0(Z);return t0({assertionCalls:X+1},Z),L1($,Q)};return Object.assign(Z,L1),Object.assign(Z,globalThis[r0]),Z.getState=()=>B0(Z),Z.setState=($)=>t0($,Z),t0({assertionCalls:0,isExpectingAssertions:!1,isExpectingAssertionsError:null,expectedAssertionsNumber:null,expectedAssertionsNumberErrorGen:null},Z),Z.assert=V,Z.extend=($)=>L1.extend(Z,$),Z.unreachable=($)=>V.fail("expected"+($?' "'+$+'" ':" ")+"not to be reached"),Z}globalThis.__bundles=globalThis.__bundles||{};globalThis.__bundles["vitest-expect"]={createExpect:OX,fn:V7,spyOn:w7,isMockFunction:F1};})(); diff --git a/apps/sim/lib/execution/sandbox/run-task.ts b/apps/sim/lib/execution/sandbox/run-task.ts index 8e0e533c5d5..d28d5175d54 100644 --- a/apps/sim/lib/execution/sandbox/run-task.ts +++ b/apps/sim/lib/execution/sandbox/run-task.ts @@ -20,6 +20,8 @@ export interface RunSandboxTaskOptions { signal?: AbortSignal /** Records canonical workspace files whose bytes were successfully read by a task broker. */ onWorkspaceFileAccess?: SandboxBrokerContext['onWorkspaceFileAccess'] + /** Run id that brokers see as `ctx.requestId`, for callers that keep per-run broker state. */ + requestId?: string } /** @@ -51,7 +53,7 @@ export async function runSandboxTask( options: RunSandboxTaskOptions = {} ): Promise { const task = getSandboxTask(taskId) - const requestId = generateShortId(12) + const requestId = options.requestId ?? generateShortId(12) const brokerContext: SandboxBrokerContext = { workspaceId: input.workspaceId, @@ -78,6 +80,14 @@ export async function runSandboxTask( bootstrap: task.bootstrap, brokers: task.brokers.map((b) => b.name), finalize: task.finalize, + ...(task.userModules + ? { + userModule: { + filename: input.codeFilename ?? `${task.id}.js`, + modules: { ...task.userModules }, + }, + } + : {}), }, } diff --git a/apps/sim/lib/execution/sandbox/types.ts b/apps/sim/lib/execution/sandbox/types.ts index 08f7e1ff7fb..ee7082ac094 100644 --- a/apps/sim/lib/execution/sandbox/types.ts +++ b/apps/sim/lib/execution/sandbox/types.ts @@ -6,7 +6,7 @@ * which host-side brokers to expose, and how to serialize the final result. */ -export type SandboxBundleName = 'pptxgenjs' | 'docx' | 'pdf-lib' +export type SandboxBundleName = 'pptxgenjs' | 'docx' | 'pdf-lib' | 'vitest-expect' export interface SandboxBroker { /** @@ -35,6 +35,8 @@ export interface SandboxBrokerContext { export interface SandboxTaskInput { workspaceId: string code: string + /** Module filename for `code` when the task sets `userModules`; appears in stack traces. */ + codeFilename?: string } export interface SandboxTask { @@ -52,6 +54,11 @@ export interface SandboxTask * helper functions users expect (e.g. `getFileBase64`). */ bootstrap: string + /** + * When set, user code runs as an ES module that may import only these modules + * (specifier -> module source). Each source reads what `bootstrap` installed. + */ + userModules?: Readonly> /** * JS source that, when evaluated inside an async IIFE after user code, must * return a `Uint8Array`. The bytes are transferred out via `ExternalCopy`. diff --git a/apps/sim/lib/logs/get-trigger-options.ts b/apps/sim/lib/logs/get-trigger-options.ts index b5f88ced199..1d84adc6ae4 100644 --- a/apps/sim/lib/logs/get-trigger-options.ts +++ b/apps/sim/lib/logs/get-trigger-options.ts @@ -33,6 +33,7 @@ export function getTriggerOptions(): TriggerOption[] { { value: 'mothership', label: 'Sim agent', color: '#ec4899' }, { value: 'workflow', label: 'Workflow', color: '#0369a1' }, { value: 'custom_block', label: 'Custom block', color: '#0369a1' }, + { value: 'test', label: 'Test', color: '#4b5563' }, ] for (const trigger of triggers) { diff --git a/apps/sim/lib/mothership/agent-cli/resource-effects.ts b/apps/sim/lib/mothership/agent-cli/resource-effects.ts index de1ddb069a1..f38fa1d3603 100644 --- a/apps/sim/lib/mothership/agent-cli/resource-effects.ts +++ b/apps/sim/lib/mothership/agent-cli/resource-effects.ts @@ -15,6 +15,7 @@ import * as tables from '@/lib/api/contracts/v2/tables' import * as workflows from '@/lib/api/contracts/v2/workflows' import { parseFolderPath } from '@/lib/folders/paths' import type { ResourceAddress, ResourceChange } from '@/lib/mothership/generated/resources' +import { findNonTabFileIds } from '@/lib/mothership/resources/file-tabs' import { encodeVfsPathSegments } from '@/lib/vfs/path' interface EffectContext { @@ -339,6 +340,16 @@ function matchParams(pattern: string, pathname: string): Record return params } +/** Only workspace files and chat uploads open as file tabs; other rows open as their owner. */ +async function onlyFileTabs(changes: ResourceChange[]): Promise { + const isFileUpsert = (change: ResourceChange) => + change.op === 'upsert' && change.resource.type === 'file' + const nonTabIds = await findNonTabFileIds( + changes.flatMap((change) => (isFileUpsert(change) ? [change.resource.id] : [])) + ) + return changes.filter((change) => !isFileUpsert(change) || !nonTabIds.has(change.resource.id)) +} + /** The observer adds no request and never reads provider responses or download bodies. */ export function createResourceEffectTransport( endpoint: string, @@ -366,7 +377,8 @@ export function createResourceEffectTransport( const response = await transport(input, init) if (route && response.ok && (!route.readOnly || observeReads)) { const params = matchParams(route.path, url.pathname) - if (params) effects.push(...(await route.project(response, { params, body }))) + if (params) + effects.push(...(await onlyFileTabs(await route.project(response, { params, body })))) } return response } diff --git a/apps/sim/lib/mothership/agent-cli/services.ts b/apps/sim/lib/mothership/agent-cli/services.ts index b783f5a01f1..1b5f856f293 100644 --- a/apps/sim/lib/mothership/agent-cli/services.ts +++ b/apps/sim/lib/mothership/agent-cli/services.ts @@ -39,7 +39,8 @@ export async function executeAgentCliService( const scope = invocation.kind === 'service' && invocation.name === 'settings' ? invocation.input.scope - : invocation.kind === 'service' && invocation.name === 'dashboards' + : invocation.kind === 'service' && + (invocation.name === 'dashboards' || invocation.name === 'tests') ? 'workspace' : 'organization' if ( diff --git a/apps/sim/lib/mothership/application/execute-workflow-test-use-case.ts b/apps/sim/lib/mothership/application/execute-workflow-test-use-case.ts new file mode 100644 index 00000000000..b01ac10e03b --- /dev/null +++ b/apps/sim/lib/mothership/application/execute-workflow-test-use-case.ts @@ -0,0 +1,13 @@ +import { createCopilotApplicationAdapter } from '@/lib/mothership/application/application-adapter' +import { COPILOT_APPLICATION_DELEGATION_TTL_MS } from '@/lib/mothership/auth/application-delegation' +import { workflowTestOperations } from '@/lib/workflow-tests/application/operations' + +export const executeWorkflowTestUseCase = createCopilotApplicationAdapter({ + domain: 'workflow_tests', + operations: workflowTestOperations, + delegation: { + audience: 'sim:workspaces', + ttlMs: COPILOT_APPLICATION_DELEGATION_TTL_MS, + createDelegationId: (context) => `copilot-tool:${context.toolCallId}`, + }, +}) diff --git a/apps/sim/lib/mothership/chat/post.ts b/apps/sim/lib/mothership/chat/post.ts index 4850f93f479..beaf414829e 100644 --- a/apps/sim/lib/mothership/chat/post.ts +++ b/apps/sim/lib/mothership/chat/post.ts @@ -130,6 +130,7 @@ const GENERIC_RESOURCE_TITLE: Record['t integration: 'Integration', file: 'File', dashboard: 'Dashboard', + test: 'Test', knowledgebase: 'Knowledge Base', folder: 'Folder', filefolder: 'File Folder', diff --git a/apps/sim/lib/mothership/entitlements.ts b/apps/sim/lib/mothership/entitlements.ts index e97f98941e4..48cdf2aaaaa 100644 --- a/apps/sim/lib/mothership/entitlements.ts +++ b/apps/sim/lib/mothership/entitlements.ts @@ -1,6 +1,7 @@ import type { Principal } from '@sim/auth/principal' import { readDashboardAvailability } from '@/lib/dashboards/application/availability' import { ENTITLEMENTS, type Entitlement } from '@/lib/mothership/generated/protocol' +import { readWorkflowTestAvailability } from '@/lib/workflow-tests/application/availability' /** The owner of one chat turn: exactly one of a workspace or an organization. */ export interface EntitlementOwner { @@ -48,6 +49,10 @@ const EVALUATORS: Record = { workspace: ({ principal, workspaceId }) => readDashboardAvailability.execute({ principal, input: { workspaceId } }), }, + [ENTITLEMENTS.tests]: { + workspace: ({ principal, workspaceId }) => + readWorkflowTestAvailability.execute({ principal, input: { workspaceId } }), + }, } function evaluate(evaluator: EntitlementEvaluator, owner: EntitlementOwner): Promise { diff --git a/apps/sim/lib/mothership/generated/agent-cli.ts b/apps/sim/lib/mothership/generated/agent-cli.ts index 988ea2fb4ff..27744c0de02 100644 --- a/apps/sim/lib/mothership/generated/agent-cli.ts +++ b/apps/sim/lib/mothership/generated/agent-cli.ts @@ -53,6 +53,7 @@ export const AgentCliServiceInvocation = z.object({ "settings", "search_sources", "dashboards", + "tests", "workspaces", ]), input: z.record(z.string(), z.json()), diff --git a/apps/sim/lib/mothership/generated/protocol.ts b/apps/sim/lib/mothership/generated/protocol.ts index 5b8a7c07b59..ea7d7b563a5 100644 --- a/apps/sim/lib/mothership/generated/protocol.ts +++ b/apps/sim/lib/mothership/generated/protocol.ts @@ -137,7 +137,7 @@ export type DesktopContext = z.infer; * the list travels in a forgeable payload. The wire accepts any name, so Sim can add one * before the worker learns it. */ -export const ENTITLEMENTS = { dashboards: "dashboards" } as const; +export const ENTITLEMENTS = { dashboards: "dashboards", tests: "tests" } as const; export type Entitlement = (typeof ENTITLEMENTS)[keyof typeof ENTITLEMENTS]; export const Entitlements = z.array(z.string().min(1).max(64)).max(32); diff --git a/apps/sim/lib/mothership/generated/resources.ts b/apps/sim/lib/mothership/generated/resources.ts index 33505e8ad5d..06090025bee 100644 --- a/apps/sim/lib/mothership/generated/resources.ts +++ b/apps/sim/lib/mothership/generated/resources.ts @@ -16,6 +16,7 @@ export const ResourceType = z.enum([ "knowledgebase", "file", "dashboard", + "test", "folder", "filefolder", "log", diff --git a/apps/sim/lib/mothership/request/go/file-preview-adapter.ts b/apps/sim/lib/mothership/request/go/file-preview-adapter.ts index 0dedab3b707..3f2984f66e4 100644 --- a/apps/sim/lib/mothership/request/go/file-preview-adapter.ts +++ b/apps/sim/lib/mothership/request/go/file-preview-adapter.ts @@ -22,13 +22,17 @@ import type { StreamEvent, StreamingContext, } from '@/lib/mothership/request/types' +import { findNonTabFileIds } from '@/lib/mothership/resources/file-tabs' import { peekFileIntent } from '@/lib/mothership/tools/server/files/file-intent-store' import { buildFilePreviewText, loadWorkspaceFileTextForPreview, type WorkspaceFilePreviewBase, } from '@/lib/mothership/tools/server/files/file-preview' -import { findWorkspaceFileRecord } from '@/lib/uploads/contexts/workspace/workspace-file-manager' +import { + findWorkspaceFileRecord, + resolveWorkspaceFileReference, +} from '@/lib/uploads/contexts/workspace/workspace-file-manager' import { listAllWorkspaceFiles } from '@/lib/workspace-files/application/list-workspace-files' const logger = createLogger('CopilotFilePreviewAdapter') @@ -135,6 +139,23 @@ async function resolvePreviewTarget(args: { } } +/** + * Whether the preview target may open as a file tab. A file another resource owns streams no + * preview, since the preview would open it as a file tab. + */ +async function isFileTabTarget( + workspaceId: string | undefined, + target: FileIntent['target'] +): Promise { + let fileId = target.fileId + if (!fileId && target.kind === 'path' && target.path && workspaceId) { + fileId = ( + await resolveWorkspaceFileReference(workspaceId, target.path, { includeTestFiles: true }) + )?.id + } + return fileId === undefined || !(await findNonTabFileIds([fileId])).has(fileId) +} + function parseWorkspaceFileArgs(value: unknown): ParsedWorkspaceFileArgs | undefined { const args = asJsonRecord(value) if (!args) { @@ -446,6 +467,10 @@ export async function processFilePreviewStreamEvent(input: { }) const previewTargetKind = toPreviewTargetKind(target.kind) const { fileId, fileName } = target + if (!(await isFileTabTarget(execContext.workspaceId, target))) { + clearIntent() + return + } const isContentOp = isContentOperation(operation) // Per-channel: a re-declared prepare_file_edit just overwrites THIS channel's @@ -526,6 +551,10 @@ export async function processFilePreviewStreamEvent(input: { isContentOperation(workspaceResultIntent.operation) ) { const result = extractWorkspaceFileResult(streamEvent.payload.output) + if (result.fileId && (await findNonTabFileIds([result.fileId])).has(result.fileId)) { + clearIntent() + return + } if (result.fileId && workspaceResultIntent.target.kind === 'path') { const intent: FileIntent = { ...workspaceResultIntent, diff --git a/apps/sim/lib/mothership/request/tools/resources.ts b/apps/sim/lib/mothership/request/tools/resources.ts index c74853cfda4..2a3ded3cf7b 100644 --- a/apps/sim/lib/mothership/request/tools/resources.ts +++ b/apps/sim/lib/mothership/request/tools/resources.ts @@ -9,6 +9,7 @@ import { TraceAttr } from '@/lib/mothership/generated/trace-attributes-v1' import { TraceSpan } from '@/lib/mothership/generated/trace-spans-v1' import { withCopilotSpan } from '@/lib/mothership/request/otel' import type { StreamEvent, ToolCallResult } from '@/lib/mothership/request/types' +import { findNonTabFileIds } from '@/lib/mothership/resources/file-tabs' import { extractDeletedResourcesFromToolResult, extractResourcesFromToolResult, @@ -119,7 +120,7 @@ export async function handleResourceSideEffects( : isResourceToolName(toolName) ? extractResourcesFromToolResult(toolName, params, projectedResult.output) : [] - const resources = + const paired = projectedResources.length === rawResources.length ? rawResources.map((resource, index) => ({ ...projectedResources[index], @@ -130,6 +131,12 @@ export async function handleResourceSideEffects( : {}), })) : [] + const nonTabFileIds = await findNonTabFileIds( + paired.flatMap((resource) => (resource.type === 'file' ? [resource.id] : [])) + ) + const resources = paired.filter( + (resource) => resource.type !== 'file' || !nonTabFileIds.has(resource.id) + ) if (resources.length > 0) { upsertedCount = resources.length diff --git a/apps/sim/lib/mothership/resources/file-tabs.ts b/apps/sim/lib/mothership/resources/file-tabs.ts new file mode 100644 index 00000000000..e7f54a81d01 --- /dev/null +++ b/apps/sim/lib/mothership/resources/file-tabs.ts @@ -0,0 +1,25 @@ +import { db } from '@sim/db' +import { workspaceFiles } from '@sim/db/schema' +import { and, inArray, notInArray } from 'drizzle-orm' + +/** Row contexts that open as a file tab: workspace files and chat uploads. */ +const FILE_TAB_CONTEXTS = ['workspace', 'mothership'] + +/** + * The ids among `fileIds` whose rows must not open as a file tab: any context other than a + * workspace file or chat upload belongs to another resource (a test's source, for one), which + * opens as that resource instead. + */ +export async function findNonTabFileIds(fileIds: string[]): Promise> { + if (fileIds.length === 0) return new Set() + const rows = await db + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) + .where( + and( + inArray(workspaceFiles.id, fileIds), + notInArray(workspaceFiles.context, FILE_TAB_CONTEXTS) + ) + ) + return new Set(rows.map((row) => row.id)) +} diff --git a/apps/sim/lib/mothership/resources/types.ts b/apps/sim/lib/mothership/resources/types.ts index 6e4990a6d31..4cc79d7d243 100644 --- a/apps/sim/lib/mothership/resources/types.ts +++ b/apps/sim/lib/mothership/resources/types.ts @@ -6,6 +6,7 @@ export const MothershipResourceType = { table: 'table', file: 'file', dashboard: 'dashboard', + test: 'test', workflow: 'workflow', knowledgebase: 'knowledgebase', folder: 'folder', @@ -112,6 +113,7 @@ const RESOURCE_POLICY: Record = { table: { persisted: true }, file: { persisted: true }, dashboard: { persisted: true }, + test: { persisted: true }, workflow: { persisted: true }, knowledgebase: { persisted: true }, folder: { persisted: true }, diff --git a/apps/sim/lib/mothership/tools/server/files/workspace-file.ts b/apps/sim/lib/mothership/tools/server/files/workspace-file.ts index 6e1bb97588e..4dc211befd2 100644 --- a/apps/sim/lib/mothership/tools/server/files/workspace-file.ts +++ b/apps/sim/lib/mothership/tools/server/files/workspace-file.ts @@ -21,6 +21,7 @@ import { } from '@/lib/mothership/tools/server/base-tool' import { DocCompileUserError } from '@/lib/mothership/tools/server/files/doc-compile-error' import type { WorkspaceFileRecord } from '@/lib/uploads/contexts/workspace/workspace-file-manager' +import { parseTestFileReference } from '@/lib/workflow-tests/paths' import { admitCreateWorkspaceFile, createWorkspaceFile, @@ -350,6 +351,12 @@ export const workspaceFileServerTool: BaseServerTool = { [dashboardsServerTool.name]: dashboardsServerTool, + [testsServerTool.name]: testsServerTool, [searchDocsServerTool.name]: searchDocsServerTool, [searchWorkspaceServerTool.name]: searchWorkspaceServerTool, [listWorkspacesServerTool.name]: listWorkspacesServerTool, diff --git a/apps/sim/lib/mothership/tools/server/tests.ts b/apps/sim/lib/mothership/tools/server/tests.ts new file mode 100644 index 00000000000..f34517fbc55 --- /dev/null +++ b/apps/sim/lib/mothership/tools/server/tests.ts @@ -0,0 +1,171 @@ +import { sleep } from '@sim/utils/helpers' +import { mothershipTestsInputSchema } from '@/lib/api/contracts/mothership-tests' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { executeWorkflowTestUseCase } from '@/lib/mothership/application/execute-workflow-test-use-case' +import { requireTrustedCopilotExecutionContext } from '@/lib/mothership/auth/application-delegation' +import type { ResourceChange } from '@/lib/mothership/generated/resources' +import { + assertServerToolNotAborted, + type BaseServerTool, + type ServerToolContext, +} from '@/lib/mothership/tools/server/base-tool' +import { startWorkflowTestRuns } from '@/lib/workflow-tests/application/run-tests' +import { + createWorkflowTest, + deleteWorkflowTest, + getWorkflowTestDetail, + listWorkflowTests, + readWorkflowTestRuns, + updateWorkflowTest, +} from '@/lib/workflow-tests/application/tests' +import type { TestReport } from '@/lib/workflow-tests/protocol' + +const RUN_POLL_MS = 2_000 + +function workspaceFor(input: { workspaceId?: string }, context?: ServerToolContext): string { + const trusted = requireTrustedCopilotExecutionContext(context) + if (input.workspaceId && input.workspaceId !== trusted.workspaceId) + throw new OrchestrationError('not_found', 'Workspace not found in this invocation') + assertServerToolNotAborted(context) + return trusted.workspaceId +} + +/** Each failing case once: where it failed and why, in vitest's own words or the judge's. */ +function failures(report: TestReport | null) { + if (!report) return [] + return report.tests + .filter((test) => test.status === 'fail') + .map((test) => { + const check = test.checks.find((candidate) => candidate.status === 'fail') + return { + test: test.path.join(' > '), + line: check?.line ?? test.error?.line, + message: check?.judge ?? check?.message ?? test.error?.message ?? 'Failed', + executions: test.executions.map((execution) => execution.executionId), + } + }) +} + +export const testsServerTool: BaseServerTool = { + name: 'tests', + inputSchema: mothershipTestsInputSchema, + async execute(raw, context) { + const input = mothershipTestsInputSchema.parse(raw) + const workspaceId = workspaceFor(input, context) + switch (input.action) { + case 'create': { + const { test } = await executeWorkflowTestUseCase(context, createWorkflowTest, { + workspaceId, + name: input.name, + title: input.title, + description: input.description, + }) + const resources: ResourceChange[] = [ + { + op: 'upsert', + resource: { type: 'test', workspaceId, id: test.name, title: test.title }, + }, + ] + return { test: { name: test.name, title: test.title }, document: test.path, resources } + } + case 'update': { + const { test } = await executeWorkflowTestUseCase(context, updateWorkflowTest, { + workspaceId, + name: input.name, + title: input.title, + description: input.description, + }) + return { test: { name: test.name, title: test.title, description: test.description } } + } + case 'list': { + const { tests } = await executeWorkflowTestUseCase(context, listWorkflowTests, { + workspaceId, + version: input.version, + }) + return { + tests: tests.map((test) => ({ + name: test.name, + title: test.title, + document: test.path, + cases: test.caseCount, + status: test.status, + })), + } + } + case 'get': { + const detail = await executeWorkflowTestUseCase(context, getWorkflowTestDetail, { + workspaceId, + name: input.name, + version: input.version, + }) + return { + test: { + name: detail.test.name, + title: detail.test.title, + description: detail.test.description, + document: detail.test.path, + status: detail.test.status, + cases: detail.test.cases.map((testCase) => testCase.path.join(' > ')), + }, + latestRun: detail.latestRun + ? { + status: detail.latestRun.status, + upToDate: detail.latestRun.current, + ranAgainst: detail.latestRun.ranAgainst.map((workflow) => ({ + workflow: workflow.name, + version: workflow.draft ? 'draft' : workflow.version, + redeployedSince: workflow.stale, + })), + passed: detail.latestRun.passed, + failed: detail.latestRun.failed, + error: detail.latestRun.error, + failures: failures(detail.latestRun.report), + } + : null, + } + } + case 'run': { + const { runs } = await executeWorkflowTestUseCase(context, startWorkflowTestRuns, { + workspaceId, + version: input.version, + names: input.names, + only: input.only, + }) + const runIds = runs.map((run) => run.runId) + for (;;) { + assertServerToolNotAborted(context) + const { runs: current } = await executeWorkflowTestUseCase( + context, + readWorkflowTestRuns, + { + workspaceId, + runIds, + } + ) + if (current.every((run) => run.status !== 'running')) { + return { + version: input.version, + results: current.map((run) => ({ + name: run.name, + status: run.status, + passed: run.passed, + failed: run.failed, + skipped: run.skipped, + ...(run.error ? { error: run.error } : {}), + failures: failures(run.report), + })), + } + } + await sleep(RUN_POLL_MS) + } + } + case 'delete': { + await executeWorkflowTestUseCase(context, deleteWorkflowTest, { + workspaceId, + name: input.name, + }) + return { deleted: input.name } + } + } + }, +} diff --git a/apps/sim/lib/mothership/vfs/path-utils.ts b/apps/sim/lib/mothership/vfs/path-utils.ts index 730c4a41fa9..f4ae4a0b4ca 100644 --- a/apps/sim/lib/mothership/vfs/path-utils.ts +++ b/apps/sim/lib/mothership/vfs/path-utils.ts @@ -37,13 +37,13 @@ export function canonicalizeVfsPath(path: string): string { } /** - * Canonical, per-segment-encoded VFS path of a workspace file. `uploads` is the - * chat-upload namespace, which has no folders. + * Canonical, per-segment-encoded VFS path of a workspace file. `uploads` (chat uploads) + * and `tests` (test files) are namespaces without folders. */ export function canonicalWorkspaceFilePath(parts: { folderPath?: string | null name: string - prefix?: 'files' | 'recently-deleted/files' | 'uploads' + prefix?: 'files' | 'recently-deleted/files' | 'uploads' | 'tests' }): string { const prefix = parts.prefix ?? 'files' const folderSegments = parts.folderPath diff --git a/apps/sim/lib/uploads/config.ts b/apps/sim/lib/uploads/config.ts index ab49401e8c3..0d456fb23a3 100644 --- a/apps/sim/lib/uploads/config.ts +++ b/apps/sim/lib/uploads/config.ts @@ -220,6 +220,7 @@ function getS3Config(context: StorageContext): StorageConfig { } case 'mothership': case 'workspace': + case 'test': case 'table-import': case 'organization-logos': return { @@ -284,6 +285,7 @@ function getBlobConfig(context: StorageContext): StorageConfig { } case 'mothership': case 'workspace': + case 'test': case 'table-import': case 'organization-logos': return { @@ -345,6 +347,7 @@ function getGcsConfig(context: StorageContext): StorageConfig { return { bucket: GCS_EXECUTION_FILES_CONFIG.bucket || GCS_CONFIG.bucket } case 'mothership': case 'workspace': + case 'test': case 'table-import': case 'organization-logos': return { bucket: GCS_CONFIG.bucket } diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts index 57161c2058c..e31b139f458 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts @@ -48,7 +48,7 @@ import { asOrchestrationError, OrchestrationError } from '@/lib/core/orchestrati import { generateRequestId } from '@/lib/core/utils/request' import { generateRestoreName } from '@/lib/core/utils/restore-name' import { isPayloadSizeLimitError } from '@/lib/core/utils/stream-limits' -import type { DbOrTx } from '@/lib/db/types' +import type { DbOrTx, DbTransaction } from '@/lib/db/types' import { acquireFolderMutationLock } from '@/lib/folders/locks' import { parseFolderPath } from '@/lib/folders/paths' import { @@ -101,11 +101,17 @@ import { getWorkspaceFileSize, MAX_WORKSPACE_FILE_SIZE } from '@/lib/uploads/sha import { isMarkdownFile } from '@/lib/uploads/utils/file-utils' import type { ServableFile } from '@/lib/uploads/utils/file-utils.server' import { displaySegmentPattern } from '@/lib/vfs/path' +import { parseTestFileReference, TEST_FILE_SUFFIX } from '@/lib/workflow-tests/paths' +import { + getLiveWorkflowTestByBodyFileId, + getLiveWorkflowTestByName, +} from '@/lib/workflow-tests/repository' import { SIM_PAGE_CONTENT_TYPE } from '@/lib/workspace-files/page-compile' import { MAX_SIM_PAGE_UPLOAD_SNIFF_BYTES, restoreSimPageSourceBuffer, } from '@/lib/workspace-files/page-source-embed' +import { contentWritableWorkspaceFileContextCondition } from '@/lib/workspace-files/query-scope' import { getWorkspaceWithOwner } from '@/lib/workspaces/permissions/utils' import { isUuid } from '@/executor/constants' import type { UserFile } from '@/executor/types' @@ -168,7 +174,7 @@ export interface WorkspaceFileRecord { * Set on chat uploads (`context = 'mothership'`), which the VFS addresses as * `uploads/` rather than `files/…`; `name` then carries the upload's display name. */ - vfsNamespace?: 'uploads' + vfsNamespace?: 'uploads' | 'tests' /** Public share state, attached at the API boundary. `null` when never shared. */ share?: ShareRecord | null } @@ -188,6 +194,8 @@ export interface UploadedWorkspaceFileRecord extends WorkspaceFileRecord { export interface ActiveWorkspaceFileContext { fileId: string + /** `workspace_files.context`: `'test'` for a test file, which follows its test's policy. */ + fileContext: string workspaceId: string workspaceOrganizationId: string | null allowPersonalApiKeys: boolean @@ -281,6 +289,7 @@ interface WorkspaceFileMetadataInsert { originalName: string contentType: string size: number + context?: 'workspace' | 'test' } /** @@ -295,9 +304,9 @@ async function insertWorkspaceFileMetadataInTx( const [inserted] = await tx .insert(workspaceFiles) .values({ - ...omit(metadata, ['size']), + ...omit(metadata, ['size', 'context']), sizeBytes: metadata.size, - context: 'workspace', + context: metadata.context ?? 'workspace', displayName: metadata.originalName, deletedAt: null, uploadedAt: new Date(), @@ -612,6 +621,72 @@ export async function uploadWorkspaceFile( throw new FileConflictError(fileName) } +/** + * Creates a file another resource owns: a test file's source (`context = 'test'`). + * `insertOwner` runs in the file's insert transaction, so the owner row and its file commit together or not at all. + */ +export async function createOwnedWorkspaceFile(params: { + context: 'test' + workspaceId: string + userId: string + content: string + contentType: string + fileName: (fileId: string) => string + insertOwner: (tx: DbTransaction, fileId: string) => Promise +}): Promise<{ fileId: string; owner: T }> { + const fileId = `wf_${generateShortId()}` + const name = params.fileName(fileId) + const buffer = Buffer.from(params.content, 'utf-8') + const storageKey = generateWorkspaceFileKey(params.workspaceId, name) + const storageBillingContext = await resolveStorageBillingContext(params.workspaceId) + const uploadResult = await uploadFile({ + file: buffer, + fileName: storageKey, + contentType: params.contentType, + context: 'workspace', + preserveKey: true, + customKey: storageKey, + metadata: { + originalName: name, + uploadedAt: new Date().toISOString(), + purpose: 'workspace', + userId: params.userId, + workspaceId: params.workspaceId, + }, + persistMetadata: false, + }) + try { + const { owner, updatedUsage } = await db.transaction(async (tx) => { + const inserted = await insertWorkspaceFileMetadataInTx(tx, { + id: fileId, + key: uploadResult.key, + userId: params.userId, + workspaceId: params.workspaceId, + folderId: null, + originalName: name, + contentType: params.contentType, + size: buffer.length, + context: params.context, + }) + if (!inserted) throw new Error(`Owned ${params.context} file ${fileId} was not inserted`) + const updatedUsage = await incrementStorageUsageForBillingContextInTx( + tx, + storageBillingContext, + buffer.length + ) + return { owner: await params.insertOwner(tx, fileId), updatedUsage } + }) + void maybeNotifyStorageLimitForBillingContext(storageBillingContext, updatedUsage) + return { fileId, owner } + } catch (error) { + await cleanupWorkspaceStorageObject( + uploadResult.key, + `${params.context} file owner insert failure` + ) + throw error + } +} + /** * Finalize a workspace file that was uploaded through a transfer session * (signed PUT or completed multipart). Verifies the object exists, @@ -1217,6 +1292,7 @@ async function mapSingleWorkspaceFileRecord( if (file.context === 'mothership') { return mapChatUploadRecord(file, workspaceId) } + if (file.context === 'test') return mapTestFileRecord(file, workspaceId) if (!file.folderId) { return mapWorkspaceFileRecord(file, workspaceId, new Map()) } @@ -1231,6 +1307,17 @@ async function mapSingleWorkspaceFileRecord( ) } +/** A test file reads as `tests/.test.js`, the path Sim addresses it by. */ +async function mapTestFileRecord( + file: WorkspaceFileRow, + workspaceId: string +): Promise { + const record = mapWorkspaceFileRecord(file, workspaceId, new Map()) + const owner = await getLiveWorkflowTestByBodyFileId(file.id) + if (!owner) return record + return { ...record, name: `${owner.name}${TEST_FILE_SUFFIX}`, vfsNamespace: 'tests' } +} + /** * Store an image file's intrinsic pixel dimensions (a pure rendering hint used to reserve layout space * before the image loads). The client reports the browser's own EXIF-corrected `naturalWidth/Height`, and @@ -1299,27 +1386,34 @@ export async function getWorkspaceFileByName( */ export interface WorkspaceFileLookupOptions { includeChatUploads?: boolean + /** + * Admit test files (`context = 'test'`), by id or as `tests/.test.js`. Only content + * reads and writes set this, and their use cases apply the test policy to whatever they admit. + */ + includeTestFiles?: boolean /** Internal Mothership scope for uploads/; ordinary API lookup remains workspace-wide. */ chatId?: string } -/** Row context a single-file lookup admits: workspace files, plus chat uploads on opt-in. */ -function workspaceFileContextCondition(includeChatUploads?: boolean) { - return includeChatUploads - ? inArray(workspaceFiles.context, ['workspace', 'mothership']) - : eq(workspaceFiles.context, 'workspace') +/** Row context a single-file lookup admits: workspace files, plus chat uploads and test files on opt-in. */ +function workspaceFileContextCondition(options?: WorkspaceFileLookupOptions) { + const contexts = [ + 'workspace', + ...(options?.includeChatUploads ? ['mothership'] : []), + ...(options?.includeTestFiles ? ['test'] : []), + ] + return contexts.length === 1 + ? eq(workspaceFiles.context, 'workspace') + : inArray(workspaceFiles.context, contexts) } /** Workspace-file rows for one scope: live, Recently Deleted, or both. */ function workspaceFileScopeCondition( workspaceId: string, scope: WorkspaceFileScope, - includeChatUploads?: boolean + lookup?: WorkspaceFileLookupOptions ) { - const base = [ - eq(workspaceFiles.workspaceId, workspaceId), - workspaceFileContextCondition(includeChatUploads), - ] + const base = [eq(workspaceFiles.workspaceId, workspaceId), workspaceFileContextCondition(lookup)] if (scope === 'all') return and(...base) return scope === 'archived' ? and(...base, isNotNull(workspaceFiles.deletedAt)) @@ -1662,12 +1756,24 @@ async function getWorkspaceFileByExactReference( * records without one rather than pairing a row with a version a second query read later. * With `includeChatUploads`, an `uploads/` path (or a chat upload's own id) reaches * the chat upload it names; chat uploads are never found through the listing fallback. + * With `includeTestFiles`, `tests/.test.js` (or the file's own id) reaches that test file. */ export async function resolveWorkspaceFileReference( workspaceId: string, fileReference: string, options?: WorkspaceFileLookupOptions ): Promise { + if (options?.includeTestFiles) { + const testName = parseTestFileReference(fileReference) + if (testName !== null) { + const owner = await getLiveWorkflowTestByName(workspaceId, testName) + return owner + ? getWorkspaceFileWithCurrentVersion(workspaceId, owner.bodyFileId, { + includeTestFiles: true, + }) + : null + } + } const includeChatUploads = options?.includeChatUploads === true if (includeChatUploads) { const uploadName = parseChatUploadReference(fileReference) @@ -1682,6 +1788,7 @@ export async function resolveWorkspaceFileReference( if (normalizedReference.startsWith('wf_') || isUuid(normalizedReference)) { const file = await getWorkspaceFileWithCurrentVersion(workspaceId, normalizedReference, { includeChatUploads, + includeTestFiles: options?.includeTestFiles, }) if (file) return file } @@ -1705,6 +1812,7 @@ export async function loadActiveWorkspaceFileContext( const [context] = await db .select({ fileId: workspaceFiles.id, + fileContext: workspaceFiles.context, workspaceId: workspace.id, workspaceOrganizationId: workspace.organizationId, allowPersonalApiKeys: workspace.allowPersonalApiKeys, @@ -1715,7 +1823,7 @@ export async function loadActiveWorkspaceFileContext( .where( and( eq(workspaceFiles.id, fileId), - workspaceFileContextCondition(options?.includeChatUploads), + workspaceFileContextCondition(options), ...(options?.includeDeleted ? [] : [isNull(workspaceFiles.deletedAt)]), isNull(workspace.archivedAt) ) @@ -1736,6 +1844,7 @@ export async function loadWorkspaceFileLifecycleContext( const [context] = await db .select({ fileId: workspaceFiles.id, + fileContext: workspaceFiles.context, workspaceId: workspace.id, workspaceOrganizationId: workspace.organizationId, allowPersonalApiKeys: workspace.allowPersonalApiKeys, @@ -1798,7 +1907,7 @@ export async function getWorkspaceFile( and( eq(workspaceFiles.id, fileId), eq(workspaceFiles.workspaceId, workspaceId), - workspaceFileContextCondition(options?.includeChatUploads), + workspaceFileContextCondition(options), ...(includeDeleted ? [] : [isNull(workspaceFiles.deletedAt)]) ) ) @@ -1835,7 +1944,7 @@ export async function getWorkspaceFileWithCurrentVersion( workspaceFileScopeCondition( workspaceId, options?.includeDeleted ? 'all' : 'active', - options?.includeChatUploads + options ) ) ) @@ -1998,7 +2107,9 @@ export async function updateWorkspaceFileContent( } logger.info(`Updating workspace file content: ${fileId} for workspace ${workspaceId}`) - const fileRecord = await getWorkspaceFile(workspaceId, fileId) + const fileRecord = await getWorkspaceFile(workspaceId, fileId, { + includeTestFiles: true, + }) if (!fileRecord) { throw new OrchestrationError('not_found', 'File not found') } @@ -2046,7 +2157,7 @@ export async function updateWorkspaceFileContent( and( eq(workspaceFiles.id, fileId), eq(workspaceFiles.workspaceId, workspaceId), - eq(workspaceFiles.context, 'workspace'), + contentWritableWorkspaceFileContextCondition, isNull(workspaceFiles.deletedAt) ) ) @@ -2115,7 +2226,7 @@ export async function updateWorkspaceFileContent( and( eq(workspaceFiles.id, fileId), eq(workspaceFiles.workspaceId, workspaceId), - eq(workspaceFiles.context, 'workspace'), + contentWritableWorkspaceFileContextCondition, isNull(workspaceFiles.deletedAt) ) ) diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-secret-provenance.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-secret-provenance.ts index 69be5efc158..0fd5ee48452 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-secret-provenance.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-secret-provenance.ts @@ -438,7 +438,7 @@ async function markWorkspaceFileSecretProvenanceTrackedInTx( eq(workspaceFiles.id, fileId), gte(workspaceFiles.contentUpdatedAt, contentUpdatedAt), lt(workspaceFiles.contentUpdatedAt, nextContentMillisecond), - inArray(workspaceFiles.context, ['workspace', 'mothership', 'execution']), + inArray(workspaceFiles.context, ['workspace', 'mothership', 'execution', 'test']), or( isNull(workspaceFiles.secretProvenanceVersion), eq(workspaceFiles.secretProvenanceVersion, 1) diff --git a/apps/sim/lib/uploads/shared/types.ts b/apps/sim/lib/uploads/shared/types.ts index ed67609ef2b..4e636f9bef4 100644 --- a/apps/sim/lib/uploads/shared/types.ts +++ b/apps/sim/lib/uploads/shared/types.ts @@ -69,6 +69,7 @@ export type StorageContext = | 'mothership' | 'execution' | 'workspace' + | 'test' | 'table-import' | 'profile-pictures' | 'og-images' diff --git a/apps/sim/lib/workflow-tests/application/availability.ts b/apps/sim/lib/workflow-tests/application/availability.ts new file mode 100644 index 00000000000..26d9381bd50 --- /dev/null +++ b/apps/sim/lib/workflow-tests/application/availability.ts @@ -0,0 +1,28 @@ +import { defineAuthorizedWorkspaceUseCase, defineWorkspaceOperation } from '@/lib/core/application' +import { isWorkflowTestsEnabled } from '@/lib/workflow-tests/feature-flag' +import { resolveActiveWorkspaceApplicationContext } from '@/lib/workspaces/application/workspace-context' + +/** permission-group-exempt: reports rollout availability only; test operations enforce files.use. */ +const availabilityOperation = defineWorkspaceOperation({ + id: 'workflow_tests.availability', + minimumRole: 'read', + oauthScope: 'api:read', + workspaceApiKey: 'allow', + capability: 'none', + principalKinds: [ + 'session', + 'personal_api_key', + 'workspace_api_key', + 'oauth_access_token', + 'delegated', + ], + delegatedServices: ['copilot'], +}) + +export const readWorkflowTestAvailability = defineAuthorizedWorkspaceUseCase({ + operation: availabilityOperation, + resolveContext: ({ input }: { input: { workspaceId: string } }) => + resolveActiveWorkspaceApplicationContext(input.workspaceId), + authorizationOptions: { delegation: { audience: 'sim:workspaces', isWithinScope: () => true } }, + execute: ({ context }) => isWorkflowTestsEnabled(context.workspaceOrganizationId), +}) diff --git a/apps/sim/lib/workflow-tests/application/operations.ts b/apps/sim/lib/workflow-tests/application/operations.ts new file mode 100644 index 00000000000..09ffaf3ef0c --- /dev/null +++ b/apps/sim/lib/workflow-tests/application/operations.ts @@ -0,0 +1,47 @@ +import { defineWorkspaceOperation } from '@/lib/core/application/workspace-operation' + +/** Workspace API keys are refused: a test run's workflow runs are attributed to the acting user. */ +const policy = { + workspaceApiKey: 'deny', + principalKinds: ['session', 'personal_api_key', 'oauth_access_token', 'delegated'], + delegatedServices: ['copilot'], +} as const + +/** Test files keep the access policy of their file-backed source, like dashboards. */ +export const workflowTestOperations = { + read: defineWorkspaceOperation({ + ...policy, + oauthScope: 'api:read', + capability: 'files.use', + id: 'workflow_tests.read', + minimumRole: 'read', + }), + create: defineWorkspaceOperation({ + ...policy, + oauthScope: 'api:write', + capability: 'files.use', + id: 'workflow_tests.create', + minimumRole: 'write', + }), + update: defineWorkspaceOperation({ + ...policy, + oauthScope: 'api:write', + capability: 'files.use', + id: 'workflow_tests.update', + minimumRole: 'write', + }), + run: defineWorkspaceOperation({ + ...policy, + oauthScope: 'api:write', + capability: 'files.use', + id: 'workflow_tests.run', + minimumRole: 'write', + }), + delete: defineWorkspaceOperation({ + ...policy, + oauthScope: 'api:write', + capability: 'files.use', + id: 'workflow_tests.delete', + minimumRole: 'write', + }), +} as const diff --git a/apps/sim/lib/workflow-tests/application/run-tests.ts b/apps/sim/lib/workflow-tests/application/run-tests.ts new file mode 100644 index 00000000000..11568a3c411 --- /dev/null +++ b/apps/sim/lib/workflow-tests/application/run-tests.ts @@ -0,0 +1,139 @@ +import { + type Principal, + resolvePrincipalSubjectUserId, + toPrincipalActor, +} from '@sim/auth/principal' +import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' +import { generateId } from '@sim/utils/id' +import { defineAuthorizedWorkspaceUseCase } from '@/lib/core/application' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { + fetchWorkspaceFileBuffer, + getWorkspaceFile, +} from '@/lib/uploads/contexts/workspace/workspace-file-manager' +import { workflowTestOperations } from '@/lib/workflow-tests/application/operations' +import { testSourceHash } from '@/lib/workflow-tests/definition' +import { requireWorkflowTestsEnabled } from '@/lib/workflow-tests/feature-flag' +import { testFilePath } from '@/lib/workflow-tests/paths' +import { + completeWorkflowTestRun, + failWorkflowTestRun, + insertWorkflowTestRun, + listLiveWorkflowTests, + readExecutedDeployments, + recordWorkflowTestProgress, + type WorkflowTestRow, +} from '@/lib/workflow-tests/repository' +import { runWorkflowTestFile } from '@/lib/workflow-tests/run-test-file' +import { MAX_TEST_SOURCE_BYTES } from '@/lib/workflow-tests/source-write' +import type { WorkflowTestVersion } from '@/lib/workflows/application/run-workflow-for-test' +import { resolveActiveWorkspaceApplicationContext } from '@/lib/workspaces/application/workspace-context' + +const logger = createLogger('WorkflowTestRuns') + +const authorizationOptions = { + delegation: { audience: 'sim:workspaces', isWithinScope: () => true }, +} as const + +async function executeTestRun(params: { + runId: string + test: WorkflowTestRow + principal: Principal + version: WorkflowTestVersion + only: string[] | null +}): Promise { + const { runId, test, principal, version } = params + let sourceHash: string | null = null + try { + const file = await getWorkspaceFile(test.workspaceId, test.bodyFileId, { + includeTestFiles: true, + }) + if (!file) throw new Error(`${testFilePath(test.name)} has no source file`) + const buffer = await fetchWorkspaceFileBuffer(file, { maxBytes: MAX_TEST_SOURCE_BYTES }) + const source = buffer.toString('utf-8') + sourceHash = testSourceHash(source) + const report = await runWorkflowTestFile({ + principal, + workspaceId: test.workspaceId, + source, + filePath: testFilePath(test.name), + version, + only: params.only, + onProgress: (path, status) => recordWorkflowTestProgress(runId, path.join(' > '), status), + }) + const executionIds = report.tests.flatMap((result) => + result.executions.map((execution) => execution.executionId) + ) + const ranAgainst = await readExecutedDeployments(executionIds, test.workspaceId) + await completeWorkflowTestRun(runId, report, sourceHash, ranAgainst) + } catch (error) { + logger.warn('Workflow test run failed', { + runId, + testId: test.id, + error: getErrorMessage(error), + }) + await failWorkflowTestRun(runId, getErrorMessage(error), sourceHash) + } +} + +export interface RunWorkflowTestsInput { + workspaceId: string + version: WorkflowTestVersion + /** Test file names to run; omitted runs every test file in the workspace. */ + names?: string[] + /** With exactly one name: the `describe > it` paths to run inside it. */ + only?: string[] +} + +/** + * Starts one run per test file and returns their ids at once; the runs finish in this + * process and record their reports on `workflow_test_run`. Files run one after another so + * a large suite does not occupy every sandbox slot. + */ +export const startWorkflowTestRuns = defineAuthorizedWorkspaceUseCase({ + operation: workflowTestOperations.run, + resolveContext: ({ input }: { input: RunWorkflowTestsInput }) => + resolveActiveWorkspaceApplicationContext(input.workspaceId), + authorizationOptions, + authorizeResource: ({ context }) => requireWorkflowTestsEnabled(context.workspaceOrganizationId), + async execute({ principal, input, context }) { + if (input.only && input.names?.length !== 1) { + throw new OrchestrationError('validation', 'only needs exactly one test file name') + } + const all = await listLiveWorkflowTests(context.workspaceId) + const tests = input.names + ? input.names.map((name) => { + const test = all.find((row) => row.name === name) + if (!test) throw new OrchestrationError('not_found', `No test file ${testFilePath(name)}`) + return test + }) + : all + if (tests.length === 0) throw new OrchestrationError('validation', 'There are no tests to run') + + const actor = toPrincipalActor(principal) + const runs = tests.map((test) => ({ runId: generateId(), test })) + for (const { runId, test } of runs) { + await insertWorkflowTestRun({ + id: runId, + testId: test.id, + workspaceId: context.workspaceId, + version: input.version, + triggeredByActor: actor, + triggeredByUserId: resolvePrincipalSubjectUserId(principal) ?? null, + }) + } + void (async () => { + for (const { runId, test } of runs) { + await executeTestRun({ + runId, + test, + principal, + version: input.version, + only: input.only ?? null, + }) + } + })() + return { runs: runs.map(({ runId, test }) => ({ runId, name: test.name })) } + }, +}) diff --git a/apps/sim/lib/workflow-tests/application/tests.ts b/apps/sim/lib/workflow-tests/application/tests.ts new file mode 100644 index 00000000000..9e514d798b0 --- /dev/null +++ b/apps/sim/lib/workflow-tests/application/tests.ts @@ -0,0 +1,383 @@ +import { requirePrincipalSubjectUserId } from '@sim/auth/principal' +import { getPostgresErrorCode } from '@sim/utils/errors' +import { generateId } from '@sim/utils/id' +import { defineAuthorizedWorkspaceUseCase } from '@/lib/core/application' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { createOwnedWorkspaceFile } from '@/lib/uploads/contexts/workspace/workspace-file-manager' +import { workflowTestOperations } from '@/lib/workflow-tests/application/operations' +import { starterTestSource, testSourceHash } from '@/lib/workflow-tests/definition' +import { requireWorkflowTestsEnabled } from '@/lib/workflow-tests/feature-flag' +import { assertTestName, testFilePath } from '@/lib/workflow-tests/paths' +import type { TestCaseStatus, TestReport } from '@/lib/workflow-tests/protocol' +import { + type DeploymentFacts, + getLatestWorkflowTestRun, + getLiveWorkflowTestByName, + getWorkflowTestRunById, + insertWorkflowTestInTx, + listLiveWorkflowTests, + listRecentWorkflowTestRuns, + listWorkflowTestRunsById, + type RanAgainstEntry, + RECENT_RUNS_PER_TEST, + readDeploymentFacts, + softDeleteWorkflowTest, + updateWorkflowTestMetadata, + type WorkflowTestCases, + type WorkflowTestRow, + type WorkflowTestRunRow, +} from '@/lib/workflow-tests/repository' +import type { WorkflowTestVersion } from '@/lib/workflows/application/run-workflow-for-test' +import { resolveActiveWorkspaceApplicationContext } from '@/lib/workspaces/application/workspace-context' + +const authorizationOptions = { + delegation: { audience: 'sim:workspaces', isWithinScope: () => true }, +} as const + +/** A run that has said nothing for this long died with its process. */ +const ABANDONED_RUN_MS = 30 * 60_000 + +type WorkflowTestStatus = 'passing' | 'failing' | 'error' | 'running' | 'changed' | 'not_run' + +export interface WorkflowTestRunSummary { + id: string + status: 'running' | 'passed' | 'failed' | 'error' + passed: number + failed: number + skipped: number + error: string | null + triggeredByUserId: string | null + version: WorkflowTestVersion + /** The test file changed after this run. */ + sourceChanged: boolean + ranAgainst: WorkflowTestRanAgainst[] + /** Whether the run still describes the test: same source, and no workflow redeployed since. */ + current: boolean + startedAt: string + completedAt: string | null +} + +interface WorkflowTestRanAgainst { + workflowId: string + /** `null` when the workflow has since been deleted. */ + name: string | null + /** `null` for a draft run, or a deployment since removed. */ + version: number | null + draft: boolean + liveVersion: number | null + /** The workflow was redeployed or deleted after this run. */ + stale: boolean +} + +type RunRow = Omit + +/** Reads what every run's workflows look like now, in three queries for any number of runs. */ +async function loadDeploymentFacts(rows: RunRow[]): Promise { + const entries = rows.flatMap(ranAgainstEntries) + return readDeploymentFacts( + [...new Set(entries.map((entry) => entry.workflowId))], + [...new Set(entries.flatMap((entry) => entry.deploymentVersionId ?? []))] + ) +} + +function ranAgainstEntries(row: RunRow): RanAgainstEntry[] { + return (row.ranAgainst ?? []) as RanAgainstEntry[] +} + +function presentRanAgainst(row: RunRow, facts: DeploymentFacts): WorkflowTestRanAgainst[] { + return ranAgainstEntries(row).map((entry) => { + const name = facts.names.get(entry.workflowId) ?? null + const live = facts.active.get(entry.workflowId) + const deploymentId = entry.deploymentVersionId + return { + workflowId: entry.workflowId, + name, + version: deploymentId === null ? null : (facts.versions.get(deploymentId) ?? null), + draft: deploymentId === null, + liveVersion: live?.version ?? null, + stale: name === null || (deploymentId !== null && live?.id !== deploymentId), + } + }) +} + +function runStatus(row: Omit): WorkflowTestRunSummary['status'] { + if (row.status !== 'running') return row.status as WorkflowTestRunSummary['status'] + return Date.now() - row.startedAt.getTime() > ABANDONED_RUN_MS ? 'error' : 'running' +} + +function presentRun( + row: RunRow, + testSourceHash: string, + facts: DeploymentFacts +): WorkflowTestRunSummary { + const status = runStatus(row) + const sourceChanged = row.sourceHash !== testSourceHash + const ranAgainst = presentRanAgainst(row, facts) + return { + id: row.id, + status, + passed: row.passed, + failed: row.failed, + skipped: row.skipped, + error: + status === 'error' && row.status === 'running' ? 'The run stopped responding' : row.error, + triggeredByUserId: row.triggeredByUserId, + version: row.version as WorkflowTestVersion, + sourceChanged, + ranAgainst, + current: !sourceChanged && !ranAgainst.some((workflow) => workflow.stale), + startedAt: row.startedAt.toISOString(), + completedAt: row.completedAt?.toISOString() ?? null, + } +} + +function testStatus(latest: WorkflowTestRunSummary | undefined): WorkflowTestStatus { + if (!latest) return 'not_run' + if (latest.status !== 'running' && !latest.current) return 'changed' + if (latest.status === 'passed') return 'passing' + if (latest.status === 'failed') return 'failing' + return latest.status +} + +function presentTest(row: WorkflowTestRow, runs: WorkflowTestRunSummary[]) { + const cases = row.cases as WorkflowTestCases + return { + id: row.id, + name: row.name, + title: row.title, + description: row.description, + path: testFilePath(row.name), + fileId: row.bodyFileId, + caseCount: cases.length, + createdByUserId: row.createdByUserId, + createdAt: row.createdAt.toISOString(), + status: testStatus(runs[0]), + recentRuns: runs, + updatedAt: row.updatedAt.toISOString(), + } +} + +export const listWorkflowTests = defineAuthorizedWorkspaceUseCase({ + operation: workflowTestOperations.read, + resolveContext: ({ input }: { input: { workspaceId: string; version?: WorkflowTestVersion } }) => + resolveActiveWorkspaceApplicationContext(input.workspaceId), + authorizationOptions, + authorizeResource: ({ context }) => requireWorkflowTestsEnabled(context.workspaceOrganizationId), + async execute({ input, context }) { + const rows = await listLiveWorkflowTests(context.workspaceId) + const runs = await listRecentWorkflowTestRuns( + rows.map((row) => row.id), + input.version ?? null, + RECENT_RUNS_PER_TEST + ) + const facts = await loadDeploymentFacts(runs) + const sourceHashByTest = new Map(rows.map((row) => [row.id, row.sourceHash])) + const runsByTest = new Map() + for (const run of runs) { + const sourceHash = sourceHashByTest.get(run.testId) + if (sourceHash === undefined) throw new Error(`Run ${run.id} belongs to no listed test`) + const list = runsByTest.get(run.testId) ?? [] + list.push(presentRun(run, sourceHash, facts)) + runsByTest.set(run.testId, list) + } + return { tests: rows.map((row) => presentTest(row, runsByTest.get(row.id) ?? [])) } + }, +}) + +interface WorkflowTestInput { + workspaceId: string + name: string +} + +async function resolveTestContext({ input }: { input: WorkflowTestInput }) { + const context = await resolveActiveWorkspaceApplicationContext(input.workspaceId) + const test = await getLiveWorkflowTestByName(context.workspaceId, input.name) + if (!test) throw new OrchestrationError('not_found', `No test file ${testFilePath(input.name)}`) + return { ...context, test } +} + +export const getWorkflowTestDetail = defineAuthorizedWorkspaceUseCase({ + operation: workflowTestOperations.read, + resolveContext: ({ input }: { input: WorkflowTestInput & { version?: WorkflowTestVersion } }) => + resolveTestContext({ input }), + authorizationOptions, + authorizeResource: ({ context }) => requireWorkflowTestsEnabled(context.workspaceOrganizationId), + async execute({ input, context }) { + const [latest, recent] = await Promise.all([ + getLatestWorkflowTestRun(context.test.id, input.version ?? null), + listRecentWorkflowTestRuns([context.test.id], input.version ?? null, 20), + ]) + const facts = await loadDeploymentFacts(latest ? [latest, ...recent] : recent) + const history = recent.map((run) => presentRun(run, context.test.sourceHash, facts)) + return { + test: { + ...presentTest(context.test, history.slice(0, RECENT_RUNS_PER_TEST)), + cases: context.test.cases as WorkflowTestCases, + }, + latestRun: latest ? presentRunDetail(latest, context.test.sourceHash, facts) : null, + history, + } + }, +}) + +function presentRunDetail(row: WorkflowTestRunRow, testSourceHash: string, facts: DeploymentFacts) { + return { + ...presentRun(row, testSourceHash, facts), + report: row.report as TestReport | null, + progress: row.progress as Record | null, + } +} + +/** One run of a test with its report, for reading an earlier run's results. */ +export const getWorkflowTestRunDetail = defineAuthorizedWorkspaceUseCase({ + operation: workflowTestOperations.read, + resolveContext: ({ input }: { input: WorkflowTestInput & { runId: string } }) => + resolveTestContext({ input }), + authorizationOptions, + authorizeResource: ({ context }) => requireWorkflowTestsEnabled(context.workspaceOrganizationId), + async execute({ input, context }) { + const run = await getWorkflowTestRunById(context.test.id, input.runId) + if (!run) throw new OrchestrationError('not_found', 'That run was not found') + const facts = await loadDeploymentFacts([run]) + return { run: presentRunDetail(run, context.test.sourceHash, facts) } + }, +}) + +const MAX_TITLE_LENGTH = 200 +const MAX_DESCRIPTION_LENGTH = 2000 + +function cleanTitle(title: string): string { + const trimmed = title.trim() + if (!trimmed || trimmed.length > MAX_TITLE_LENGTH) { + throw new OrchestrationError( + 'validation', + `A test title is 1 to ${MAX_TITLE_LENGTH} characters: the concern the file tests` + ) + } + return trimmed +} + +function cleanDescription(description: string | null | undefined): string | null { + const trimmed = description?.trim() ?? '' + if (trimmed.length > MAX_DESCRIPTION_LENGTH) { + throw new OrchestrationError( + 'validation', + `A test description is at most ${MAX_DESCRIPTION_LENGTH} characters` + ) + } + return trimmed || null +} + +export interface CreateWorkflowTestInput { + workspaceId: string + name: string + title: string + description?: string +} + +/** + * Creates a test: its metadata, and `tests/.test.js` holding an empty describe. The + * cases are written into that file afterwards with the file tools, and every write is checked. + */ +export const createWorkflowTest = defineAuthorizedWorkspaceUseCase({ + operation: workflowTestOperations.create, + resolveContext: ({ input }: { input: CreateWorkflowTestInput }) => + resolveActiveWorkspaceApplicationContext(input.workspaceId), + authorizationOptions, + authorizeResource: ({ context }) => requireWorkflowTestsEnabled(context.workspaceOrganizationId), + async execute({ principal, input, context }) { + assertTestName(input.name) + const title = cleanTitle(input.title) + const description = cleanDescription(input.description) + const path = testFilePath(input.name) + if (await getLiveWorkflowTestByName(context.workspaceId, input.name)) { + throw new OrchestrationError('conflict', `${path} already exists`) + } + const userId = requirePrincipalSubjectUserId(principal) + const source = starterTestSource(title) + try { + const { owner } = await createOwnedWorkspaceFile({ + context: 'test', + workspaceId: context.workspaceId, + userId, + content: source, + contentType: 'text/javascript', + fileName: () => `${input.name}.test.js`, + insertOwner: (tx, bodyFileId) => + insertWorkflowTestInTx(tx, { + id: generateId(), + workspaceId: context.workspaceId, + name: input.name, + title, + description, + bodyFileId, + cases: [], + sourceHash: testSourceHash(source), + createdByUserId: userId, + }), + }) + return { test: presentTest(owner, []) } + } catch (error) { + if (getPostgresErrorCode(error) === '23505') { + throw new OrchestrationError('conflict', `${path} already exists`) + } + throw error + } + }, +}) + +export interface UpdateWorkflowTestInput extends WorkflowTestInput { + title?: string + description?: string | null +} + +/** Changes a test's metadata; its cases change by editing the file. */ +export const updateWorkflowTest = defineAuthorizedWorkspaceUseCase({ + operation: workflowTestOperations.update, + resolveContext: ({ input }: { input: UpdateWorkflowTestInput }) => resolveTestContext({ input }), + authorizationOptions, + authorizeResource: ({ context }) => requireWorkflowTestsEnabled(context.workspaceOrganizationId), + async execute({ input, context }) { + if (input.title === undefined && input.description === undefined) { + throw new OrchestrationError('validation', 'Pass a title or a description to change') + } + const row = await updateWorkflowTestMetadata(context.test.id, { + ...(input.title !== undefined ? { title: cleanTitle(input.title) } : {}), + ...(input.description !== undefined + ? { description: cleanDescription(input.description) } + : {}), + }) + return { test: presentTest(row, []) } + }, +}) + +export const deleteWorkflowTest = defineAuthorizedWorkspaceUseCase({ + operation: workflowTestOperations.delete, + resolveContext: resolveTestContext, + authorizationOptions, + authorizeResource: ({ context }) => requireWorkflowTestsEnabled(context.workspaceOrganizationId), + async execute({ context }) { + await softDeleteWorkflowTest(context.test.id) + return { deleted: true } + }, +}) + +/** Runs by id with their reports, for a caller waiting on runs it started. */ +export const readWorkflowTestRuns = defineAuthorizedWorkspaceUseCase({ + operation: workflowTestOperations.read, + resolveContext: ({ input }: { input: { workspaceId: string; runIds: string[] } }) => + resolveActiveWorkspaceApplicationContext(input.workspaceId), + authorizationOptions, + authorizeResource: ({ context }) => requireWorkflowTestsEnabled(context.workspaceOrganizationId), + async execute({ input, context }) { + const rows = await listWorkflowTestRunsById(input.runIds, context.workspaceId) + const facts = await loadDeploymentFacts(rows) + return { + runs: rows.map((row) => ({ + ...presentRun(row, row.testSourceHash, facts), + name: row.testName, + report: row.report as TestReport | null, + })), + } + }, +}) diff --git a/apps/sim/lib/workflow-tests/brokers.ts b/apps/sim/lib/workflow-tests/brokers.ts new file mode 100644 index 00000000000..1e877c04845 --- /dev/null +++ b/apps/sim/lib/workflow-tests/brokers.ts @@ -0,0 +1,80 @@ +import type { SandboxBroker, SandboxBrokerContext } from '@/lib/execution/sandbox/types' +import { + testCancelArgsSchema, + testJudgeArgsSchema, + testNextArgsSchema, + testProgressArgsSchema, + testReplyArgsSchema, + testStartArgsSchema, +} from '@/lib/workflow-tests/protocol' +import type { WorkflowTestSession } from '@/lib/workflow-tests/session' +import { requireWorkflowTestSession } from '@/lib/workflow-tests/session-registry' + +/** The session this sandbox run belongs to, scoped to the broker context's workspace. */ +function sessionFor(ctx: SandboxBrokerContext): WorkflowTestSession { + const session = requireWorkflowTestSession(ctx.requestId) + if (session.config.workspaceId !== ctx.workspaceId) { + throw new Error('This workflow test run belongs to another workspace') + } + return session +} + +const testPlan: SandboxBroker = { + name: 'testPlan', + async handle(ctx) { + return { only: sessionFor(ctx).config.only } + }, +} + +const testStart: SandboxBroker = { + name: 'testStart', + handle: (ctx, args) => sessionFor(ctx).start(testStartArgsSchema.parse(args)), +} + +const testNext: SandboxBroker = { + name: 'testNext', + handle: (ctx, args) => sessionFor(ctx).next(testNextArgsSchema.parse(args).runId), +} + +const testReply: SandboxBroker = { + name: 'testReply', + handle(ctx, args) { + const reply = testReplyArgsSchema.parse(args) + return sessionFor(ctx).reply( + reply.runId, + reply.callId, + 'error' in reply ? { error: reply.error } : { output: reply.output } + ) + }, +} + +const testCancel: SandboxBroker = { + name: 'testCancel', + async handle(ctx, args) { + sessionFor(ctx).cancel(testCancelArgsSchema.parse(args).runIds) + return null + }, +} + +const testJudge: SandboxBroker = { + name: 'testJudge', + handle: (ctx, args) => sessionFor(ctx).judge(testJudgeArgsSchema.parse(args)), +} + +const testProgress: SandboxBroker = { + name: 'testProgress', + async handle(ctx, args) { + await sessionFor(ctx).progress(testProgressArgsSchema.parse(args)) + return null + }, +} + +export const workflowTestBrokers: ReadonlyArray = [ + testPlan, + testStart, + testNext, + testReply, + testCancel, + testJudge, + testProgress, +] diff --git a/apps/sim/lib/workflow-tests/collect.ts b/apps/sim/lib/workflow-tests/collect.ts new file mode 100644 index 00000000000..a35741c7785 --- /dev/null +++ b/apps/sim/lib/workflow-tests/collect.ts @@ -0,0 +1,16 @@ +import { runSandboxTask } from '@/lib/execution/sandbox/run-task' +import { type TestCollection, testCollectionSchema } from '@/lib/workflow-tests/protocol' + +/** Evaluates a test file without running it: its top-level blocks and every test name. */ +export async function collectWorkflowTestFile(input: { + workspaceId: string + source: string + filePath: string +}): Promise { + const bytes = await runSandboxTask( + 'workflow-test-collect', + { workspaceId: input.workspaceId, code: input.source, codeFilename: input.filePath }, + { ownerKey: `workspace:${input.workspaceId}` } + ) + return testCollectionSchema.parse(JSON.parse(Buffer.from(bytes).toString('utf-8'))) +} diff --git a/apps/sim/lib/workflow-tests/definition.ts b/apps/sim/lib/workflow-tests/definition.ts new file mode 100644 index 00000000000..1b51032b905 --- /dev/null +++ b/apps/sim/lib/workflow-tests/definition.ts @@ -0,0 +1,68 @@ +import { sha256Hex } from '@sim/security/hash' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { SandboxUserCodeError } from '@/lib/execution/sandbox/run-task' +import { collectWorkflowTestFile } from '@/lib/workflow-tests/collect' +import { testFilePath } from '@/lib/workflow-tests/paths' +import type { WorkflowTestCases } from '@/lib/workflow-tests/repository' + +/** + * Evaluates a test file's source and returns the cases it declares. A file is one concern: + * exactly one top-level `describe`, holding every test. Anything else is refused with a message + * the writer can act on, so an invalid file is never saved. + */ +export async function readWorkflowTestCases(input: { + workspaceId: string + name: string + source: string +}): Promise { + const filePath = testFilePath(input.name) + let collection: Awaited> + try { + collection = await collectWorkflowTestFile({ + workspaceId: input.workspaceId, + source: input.source, + filePath, + }) + } catch (error) { + if (error instanceof SandboxUserCodeError) { + throw new OrchestrationError('validation', `${filePath} does not load: ${error.message}`) + } + throw error + } + + const [top, ...rest] = collection.topLevel + if (!top || top.kind !== 'suite' || rest.length > 0) { + throw new OrchestrationError( + 'validation', + `${filePath} must contain exactly one top-level describe(...) with every test inside it` + ) + } + const seen = new Set() + for (const testCase of collection.tests) { + const key = testCase.path.join(' > ') + if (seen.has(key)) { + throw new OrchestrationError( + 'validation', + `${filePath} has two tests named "${key}"; give each test its own name` + ) + } + seen.add(key) + } + return collection.tests +} + +/** Identifies the exact source a test's cases and runs were read from. */ +export function testSourceHash(source: string): string { + return sha256Hex(source) +} + +/** The source `tests create` starts a test file with: one empty describe, ready for cases. */ +export function starterTestSource(title: string): string { + return [ + "import { describe, expect, it } from 'vitest'", + "import { mockBlock, runWorkflow, spyOnBlock } from 'sim:test'", + '', + `describe(${JSON.stringify(title)}, () => {})`, + '', + ].join('\n') +} diff --git a/apps/sim/lib/workflow-tests/feature-flag.ts b/apps/sim/lib/workflow-tests/feature-flag.ts new file mode 100644 index 00000000000..ff728f679f0 --- /dev/null +++ b/apps/sim/lib/workflow-tests/feature-flag.ts @@ -0,0 +1,13 @@ +import { isFeatureEnabled } from '@/lib/core/config/feature-flags' +import { OrchestrationError } from '@/lib/core/orchestration/types' + +/** Test rollout follows the canonical organization; personal workspaces use the global switch. */ +export function isWorkflowTestsEnabled(orgId?: string | null): Promise { + return isFeatureEnabled('workflow-tests', orgId ? { orgId } : {}) +} + +export async function requireWorkflowTestsEnabled(orgId?: string | null): Promise { + if (!(await isWorkflowTestsEnabled(orgId))) { + throw new OrchestrationError('forbidden', 'Workflow tests are not enabled') + } +} diff --git a/apps/sim/lib/workflow-tests/judge.ts b/apps/sim/lib/workflow-tests/judge.ts new file mode 100644 index 00000000000..81306c576a7 --- /dev/null +++ b/apps/sim/lib/workflow-tests/judge.ts @@ -0,0 +1,104 @@ +import { createLogger } from '@sim/logger' +import { generateShortId } from '@sim/utils/id' +import { z } from 'zod' +import { + checkAttributedUsageLimits, + resolveBillingAttribution, + toBillingContext, +} from '@/lib/billing/core/billing-attribution' +import { recordUsage } from '@/lib/billing/core/usage-log' +import { checkAndBillPayerOverageThreshold } from '@/lib/billing/threshold-billing' +import { validateModelProvider } from '@/ee/access-control/utils/permission-check' +import { EVALUATOR } from '@/executor/constants' +import { executeProviderRequest } from '@/providers' +import { resolveProxiedModelCost } from '@/providers/cost-policy' +import { getProviderFromModel } from '@/providers/utils' + +const logger = createLogger('WorkflowTestJudge') + +const JUDGE_MODEL = EVALUATOR.DEFAULT_MODEL + +const JUDGE_SYSTEM_PROMPT = [ + 'You check one output against one requirement for an automated test.', + 'Judge only what the requirement states, and pass only if every part of it holds.', + 'In reason, quote the part of the output your verdict rests on, in one or two sentences.', +].join(' ') + +const verdictSchema = z.object({ pass: z.boolean(), reason: z.string().trim().min(1) }) +export type JudgeVerdict = z.infer + +export interface JudgeRubricInput { + workspaceId: string + actorUserId: string + value: string + rubric: string +} + +/** One pass/fail model call for `toMatchRubric`, billed to the workspace like a wand call. */ +export async function judgeRubric(input: JudgeRubricInput): Promise { + const billingAttribution = await resolveBillingAttribution({ + actorUserId: input.actorUserId, + workspaceId: input.workspaceId, + }) + const usage = await checkAttributedUsageLimits(billingAttribution) + if (usage.isExceeded) throw new Error(usage.message || 'Usage limit exceeded') + await validateModelProvider(input.actorUserId, input.workspaceId, JUDGE_MODEL) + + const response = await executeProviderRequest(getProviderFromModel(JUDGE_MODEL), { + model: JUDGE_MODEL, + systemPrompt: JUDGE_SYSTEM_PROMPT, + messages: [ + { + role: 'user', + content: `Requirement:\n${input.rubric}\n\nOutput:\n${input.value}`, + }, + ], + responseFormat: { + name: 'test_verdict', + schema: { + type: 'object', + properties: { pass: { type: 'boolean' }, reason: { type: 'string' } }, + required: ['pass', 'reason'], + additionalProperties: false, + }, + strict: true, + }, + temperature: 0, + workspaceId: input.workspaceId, + }) + if (!('content' in response) || typeof response.content !== 'string') { + throw new Error('The judge did not return a verdict') + } + + const cost = resolveProxiedModelCost(response.cost) + await recordUsage({ + userId: billingAttribution.actorUserId, + workspaceId: input.workspaceId, + ...toBillingContext(billingAttribution), + entries: [ + { + category: 'model', + source: 'workflow-test', + description: JUDGE_MODEL, + cost: cost.total, + sourceReference: `workflow-test-judge:${generateShortId()}`, + metadata: { + inputTokens: response.tokens?.input ?? 0, + outputTokens: response.tokens?.output ?? 0, + }, + }, + ], + }) + await checkAndBillPayerOverageThreshold(billingAttribution.billingEntity) + + let parsed: unknown + try { + parsed = JSON.parse(response.content) + } catch { + logger.warn('Judge returned non-JSON content', { length: response.content.length }) + throw new Error('The judge returned an unreadable verdict') + } + const verdict = verdictSchema.safeParse(parsed) + if (!verdict.success) throw new Error('The judge returned an unreadable verdict') + return verdict.data +} diff --git a/apps/sim/lib/workflow-tests/mock-channel.ts b/apps/sim/lib/workflow-tests/mock-channel.ts new file mode 100644 index 00000000000..704a6a3912c --- /dev/null +++ b/apps/sim/lib/workflow-tests/mock-channel.ts @@ -0,0 +1,120 @@ +import { toError } from '@sim/utils/errors' +import { generateId } from '@sim/utils/id' +import type { + ExecutionTestHooks, + MockedBlockCall, + MockedToolCall, +} from '@/executor/execution/types' + +type PendingMockCall = + | { kind: 'block'; call: MockedBlockCall } + | { kind: 'tool'; call: MockedToolCall } + +/** A mocked block or Agent tool call waiting for the test's answer. */ +export type ParkedMockCall = PendingMockCall & { callId: string } + +type MockReply = { output: Record } | { error: string } + +/** + * The server half of a test run's mocks. The executor parks each mocked block and mocked Agent + * tool call on `hooks`; the test sandbox pulls parked calls with `nextCall` and settles them + * with `reply`. + */ +export interface MockChannel { + readonly hooks: ExecutionTestHooks + /** The next parked call, in arrival order. */ + nextCall(): Promise + reply(callId: string, reply: MockReply): void + /** Rejects every parked call and waiter; later calls reject immediately. */ + close(reason: Error): void +} + +interface ParkedEntry { + resolve: (output: Record) => void + reject: (error: Error) => void + detach: () => void +} + +interface Waiter { + resolve: (call: ParkedMockCall) => void + reject: (error: Error) => void +} + +/** The test's side of the hooks: which blocks and tools it answers or records. */ +export type MockMatcher = Pick< + ExecutionTestHooks, + 'enterWorkflow' | 'mocksBlock' | 'mocksTool' | 'spiesBlock' | 'recordSpy' +> + +export function createMockChannel(matcher: MockMatcher): MockChannel { + const parked = new Map() + const undelivered: ParkedMockCall[] = [] + const waiters: Waiter[] = [] + let closedWith: Error | undefined + + const deliver = (call: ParkedMockCall) => { + const waiter = waiters.shift() + if (waiter) waiter.resolve(call) + else undelivered.push(call) + } + + const settle = (callId: string): ParkedEntry => { + const entry = parked.get(callId) + if (!entry) throw new Error(`No parked mock call "${callId}"`) + parked.delete(callId) + entry.detach() + const queued = undelivered.findIndex((call) => call.callId === callId) + if (queued !== -1) undelivered.splice(queued, 1) + return entry + } + + const park = ( + pending: PendingMockCall, + abortSignal?: AbortSignal + ): Promise> => { + if (closedWith) return Promise.reject(closedWith) + if (abortSignal?.aborted) return Promise.reject(toError(abortSignal.reason)) + const callId = generateId() + return new Promise((resolve, reject) => { + const onAbort = () => settle(callId).reject(toError(abortSignal?.reason)) + abortSignal?.addEventListener('abort', onAbort, { once: true }) + parked.set(callId, { + resolve, + reject, + detach: () => abortSignal?.removeEventListener('abort', onAbort), + }) + deliver({ callId, ...pending }) + }) + } + + const hooks: ExecutionTestHooks = { + enterWorkflow: (workflow) => matcher.enterWorkflow(workflow), + mocksBlock: (blockId) => matcher.mocksBlock(blockId), + resolveMock: (call, abortSignal) => park({ kind: 'block', call }, abortSignal), + mocksTool: (blockId, toolId) => matcher.mocksTool(blockId, toolId), + resolveToolMock: (call, abortSignal) => park({ kind: 'tool', call }, abortSignal), + spiesBlock: (blockId) => matcher.spiesBlock(blockId), + recordSpy: (call) => matcher.recordSpy(call), + } + + return { + hooks, + nextCall() { + if (closedWith) return Promise.reject(closedWith) + const queued = undelivered.shift() + if (queued) return Promise.resolve(queued) + return new Promise((resolve, reject) => waiters.push({ resolve, reject })) + }, + reply(callId, reply) { + const entry = settle(callId) + if ('error' in reply) entry.reject(new Error(reply.error)) + else entry.resolve(reply.output) + }, + close(reason) { + if (closedWith) return + closedWith = reason + for (const callId of [...parked.keys()]) settle(callId).reject(reason) + for (const waiter of waiters.splice(0)) waiter.reject(reason) + }, + } +} diff --git a/apps/sim/lib/workflow-tests/paths.ts b/apps/sim/lib/workflow-tests/paths.ts new file mode 100644 index 00000000000..37db40bf021 --- /dev/null +++ b/apps/sim/lib/workflow-tests/paths.ts @@ -0,0 +1,31 @@ +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { decodeVfsPathSegments } from '@/lib/mothership/vfs/path-utils' + +/** Test files live flat in this namespace as `tests/.test.js`. */ +const TEST_FILE_PREFIX = 'tests/' +export const TEST_FILE_SUFFIX = '.test.js' + +const TEST_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,79}$/ + +export function testFilePath(name: string): string { + return `${TEST_FILE_PREFIX}${name}${TEST_FILE_SUFFIX}` +} + +/** The test name in a `tests/.test.js` reference; null for any other reference. */ +export function parseTestFileReference(reference: string): string | null { + const trimmed = reference.trim().replace(/^\/+/, '') + if (!trimmed.startsWith(TEST_FILE_PREFIX)) return null + const segments = decodeVfsPathSegments(trimmed) + if (segments.length !== 2 || !segments[1].endsWith(TEST_FILE_SUFFIX)) return null + return segments[1].slice(0, -TEST_FILE_SUFFIX.length) +} + +/** Throws a message that tells the writer what a valid test file path looks like. */ +export function assertTestName(name: string): void { + if (!TEST_NAME_PATTERN.test(name)) { + throw new OrchestrationError( + 'validation', + `"${testFilePath(name)}" is not a valid test file. Use tests/.test.js with a lowercase name of letters, numbers, and dashes, like tests/billing-routing.test.js` + ) + } +} diff --git a/apps/sim/lib/workflow-tests/protocol.ts b/apps/sim/lib/workflow-tests/protocol.ts new file mode 100644 index 00000000000..aa7d0b65509 --- /dev/null +++ b/apps/sim/lib/workflow-tests/protocol.ts @@ -0,0 +1,131 @@ +import { z } from 'zod' + +/** + * The wire between the workflow-test harness (inside the isolate) and the host brokers. + * Everything the isolate sends is untrusted test-author code, so every payload is parsed. + */ + +const nameSchema = z.string().trim().min(1).max(200) +const MAX_TARGETS = 200 + +const keySchema = z.string().min(1).max(500) + +const testTargetSchema = z.discriminatedUnion('kind', [ + z.object({ + key: keySchema, + kind: z.enum(['mock', 'spy']), + workflow: nameSchema.nullable(), + block: nameSchema, + }), + z.object({ + key: keySchema, + kind: z.literal('tool'), + workflow: nameSchema.nullable(), + /** The calling Agent block; null answers the tool for every Agent. */ + block: nameSchema.nullable(), + tool: nameSchema, + }), +]) +export type TestTarget = z.infer + +export const testStartArgsSchema = z.object({ + workflow: nameSchema, + /** The trigger block that starts the run, by name; needed when the workflow has several. */ + trigger: nameSchema.nullable(), + input: z.unknown(), + targets: z.array(testTargetSchema).max(MAX_TARGETS), +}) + +export const testReplyArgsSchema = z.union([ + z.object({ runId: z.string().min(1), callId: z.string().min(1), output: z.unknown() }), + z.object({ runId: z.string().min(1), callId: z.string().min(1), error: z.string().max(10_000) }), +]) + +export const testNextArgsSchema = z.object({ runId: z.string().min(1) }) + +export const testCaseStatusSchema = z.enum(['running', 'pass', 'fail', 'skip']) +export type TestCaseStatus = z.infer + +export const testProgressArgsSchema = z.object({ + path: z.array(z.string().min(1).max(500)).min(1).max(20), + status: testCaseStatusSchema, +}) +export const testCancelArgsSchema = z.object({ runIds: z.array(z.string().min(1)).max(1000) }) +export const testJudgeArgsSchema = z.object({ + value: z.string().max(200_000), + rubric: z.string().trim().min(1).max(4_000), +}) + +export type TestEvent = + | { + kind: 'mock' + runId: string + callId: string + key: string + /** The mocked block, or the Agent block that made a mocked tool call. */ + block: string + /** Set when an Agent's tool call is being answered. */ + tool?: string + input: Record + /** A placeholder output shaped like the block's or tool's real one, for `mockSampleOutput`. */ + sample: Record + branchIndex?: number + } + | { kind: 'waiting'; runId: string } + | { + kind: 'done' + runId: string + executionId: string + output: unknown + error?: string + spyCalls: Array<{ key: string; input: unknown; output: unknown }> + /** Mock and spy keys that named a block in this run's workflow. */ + matchedKeys: string[] + } + +const checkSchema = z.object({ + status: z.enum(['pass', 'fail', 'skip']), + line: z.number().int().positive().optional(), + column: z.number().int().positive().optional(), + message: z.string().optional(), + actual: z.unknown().optional(), + expected: z.unknown().optional(), + judge: z.string().optional(), +}) + +export const testReportSchema = z.object({ + topLevel: z.array(z.object({ kind: z.enum(['suite', 'test']), name: z.string() })), + tests: z.array( + z.object({ + path: z.array(z.string()).min(1), + status: z.enum(['pass', 'fail', 'skip']), + durationMs: z.number().nonnegative(), + checks: z.array(checkSchema), + executions: z.array(z.object({ workflow: z.string(), executionId: z.string() })), + logs: z.array(z.string()), + error: z + .object({ + message: z.string(), + line: z.number().int().positive().optional(), + column: z.number().int().positive().optional(), + actual: z.unknown().optional(), + expected: z.unknown().optional(), + }) + .optional(), + }) + ), +}) +export type TestReport = z.infer + +export const testCollectionSchema = z.object({ + topLevel: z.array(z.object({ kind: z.enum(['suite', 'test']), name: z.string() })), + tests: z.array( + z.object({ + path: z.array(z.string()).min(1), + mode: z.enum(['run', 'only', 'skip']), + line: z.number().int().positive(), + endLine: z.number().int().positive(), + }) + ), +}) +export type TestCollection = z.infer diff --git a/apps/sim/lib/workflow-tests/repository.ts b/apps/sim/lib/workflow-tests/repository.ts new file mode 100644 index 00000000000..d95fe948719 --- /dev/null +++ b/apps/sim/lib/workflow-tests/repository.ts @@ -0,0 +1,328 @@ +import { db } from '@sim/db' +import { + workflow, + workflowDeploymentVersion, + workflowExecutionLogs, + workflowTest, + workflowTestRun, +} from '@sim/db/schema' +import { and, desc, eq, inArray, isNull, sql } from 'drizzle-orm' +import type { DbOrTx, DbTransaction } from '@/lib/db/types' +import type { TestCaseStatus, TestCollection, TestReport } from '@/lib/workflow-tests/protocol' +import type { WorkflowTestVersion } from '@/lib/workflows/application/run-workflow-for-test' + +export type WorkflowTestRow = typeof workflowTest.$inferSelect +export type WorkflowTestRunRow = typeof workflowTestRun.$inferSelect +export type WorkflowTestCases = TestCollection['tests'] + +/** Runs kept per test and version for the list's history dots. */ +export const RECENT_RUNS_PER_TEST = 5 + +export async function insertWorkflowTestInTx( + tx: DbTransaction, + values: typeof workflowTest.$inferInsert +): Promise { + const [row] = await tx.insert(workflowTest).values(values).returning() + return row +} + +export async function getLiveWorkflowTestByName( + workspaceId: string, + name: string, + executor: DbOrTx = db +): Promise { + const [row] = await executor + .select() + .from(workflowTest) + .where( + and( + eq(workflowTest.workspaceId, workspaceId), + eq(workflowTest.name, name), + isNull(workflowTest.deletedAt) + ) + ) + .limit(1) + return row ?? null +} + +export async function getLiveWorkflowTestByBodyFileId( + bodyFileId: string, + executor: DbOrTx = db +): Promise { + const [row] = await executor + .select() + .from(workflowTest) + .where(and(eq(workflowTest.bodyFileId, bodyFileId), isNull(workflowTest.deletedAt))) + .limit(1) + return row ?? null +} + +export async function updateWorkflowTestCases( + testId: string, + cases: WorkflowTestCases, + sourceHash: string, + executor: DbOrTx = db +): Promise { + await executor + .update(workflowTest) + .set({ cases, sourceHash, updatedAt: new Date() }) + .where(eq(workflowTest.id, testId)) +} + +export async function updateWorkflowTestMetadata( + testId: string, + metadata: { title?: string; description?: string | null } +): Promise { + const [row] = await db + .update(workflowTest) + .set({ ...metadata, updatedAt: new Date() }) + .where(eq(workflowTest.id, testId)) + .returning() + return row +} + +export async function listLiveWorkflowTests(workspaceId: string): Promise { + return db + .select() + .from(workflowTest) + .where(and(eq(workflowTest.workspaceId, workspaceId), isNull(workflowTest.deletedAt))) + .orderBy(workflowTest.title) +} + +export async function softDeleteWorkflowTest(testId: string): Promise { + await db + .update(workflowTest) + .set({ deletedAt: new Date(), updatedAt: new Date() }) + .where(eq(workflowTest.id, testId)) +} + +export async function insertWorkflowTestRun(values: { + id: string + testId: string + workspaceId: string + version: WorkflowTestVersion + triggeredByActor: unknown + triggeredByUserId: string | null +}): Promise { + await db.insert(workflowTestRun).values({ ...values, status: 'running' }) +} + +/** A workflow a run executed and the deployment it ran; `null` is the draft. */ +export interface RanAgainstEntry { + workflowId: string + deploymentVersionId: string | null +} + +/** The workflows and deployments these executions ran, from their execution logs. */ +export async function readExecutedDeployments( + executionIds: string[], + workspaceId: string +): Promise { + if (executionIds.length === 0) return [] + const rows = await db + .selectDistinct({ + workflowId: workflowExecutionLogs.workflowId, + deploymentVersionId: workflowExecutionLogs.deploymentVersionId, + }) + .from(workflowExecutionLogs) + .where( + and( + inArray(workflowExecutionLogs.executionId, executionIds), + eq(workflowExecutionLogs.workspaceId, workspaceId) + ) + ) + return rows.flatMap((row) => + row.workflowId + ? [{ workflowId: row.workflowId, deploymentVersionId: row.deploymentVersionId }] + : [] + ) +} + +export interface DeploymentFacts { + names: Map + /** Active deployment per workflow. */ + active: Map + /** Version number of every deployment asked about. */ + versions: Map +} + +/** Names, live deployments, and version numbers needed to tell whether a run is out of date. */ +export async function readDeploymentFacts( + workflowIds: string[], + deploymentVersionIds: string[] +): Promise { + const facts: DeploymentFacts = { names: new Map(), active: new Map(), versions: new Map() } + if (workflowIds.length === 0) return facts + const [workflows, active, versions] = await Promise.all([ + db + .select({ id: workflow.id, name: workflow.name }) + .from(workflow) + .where(and(inArray(workflow.id, workflowIds), isNull(workflow.archivedAt))), + db + .select({ + workflowId: workflowDeploymentVersion.workflowId, + id: workflowDeploymentVersion.id, + version: workflowDeploymentVersion.version, + }) + .from(workflowDeploymentVersion) + .where( + and( + inArray(workflowDeploymentVersion.workflowId, workflowIds), + eq(workflowDeploymentVersion.isActive, true) + ) + ), + deploymentVersionIds.length === 0 + ? Promise.resolve([]) + : db + .select({ id: workflowDeploymentVersion.id, version: workflowDeploymentVersion.version }) + .from(workflowDeploymentVersion) + .where(inArray(workflowDeploymentVersion.id, deploymentVersionIds)), + ]) + for (const row of workflows) facts.names.set(row.id, row.name) + for (const row of active) facts.active.set(row.workflowId, { id: row.id, version: row.version }) + for (const row of versions) facts.versions.set(row.id, row.version) + return facts +} + +export async function completeWorkflowTestRun( + runId: string, + report: TestReport, + sourceHash: string, + ranAgainst: RanAgainstEntry[] +): Promise { + const count = (status: 'pass' | 'fail' | 'skip') => + report.tests.filter((test) => test.status === status).length + const failed = count('fail') + await db + .update(workflowTestRun) + .set({ + status: failed > 0 ? 'failed' : 'passed', + passed: count('pass'), + failed, + skipped: count('skip'), + report, + sourceHash, + ranAgainst, + completedAt: new Date(), + }) + .where(eq(workflowTestRun.id, runId)) +} + +/** Records one case starting or finishing while its run is still going. */ +export async function recordWorkflowTestProgress( + runId: string, + caseKey: string, + status: TestCaseStatus +): Promise { + await db + .update(workflowTestRun) + .set({ + progress: sql`coalesce(${workflowTestRun.progress}, '{}'::jsonb) || jsonb_build_object(${caseKey}::text, ${status}::text)`, + }) + .where(and(eq(workflowTestRun.id, runId), eq(workflowTestRun.status, 'running'))) +} + +export async function failWorkflowTestRun( + runId: string, + error: string, + sourceHash: string | null +): Promise { + await db + .update(workflowTestRun) + .set({ status: 'error', error, sourceHash, completedAt: new Date() }) + .where(eq(workflowTestRun.id, runId)) +} + +/** The newest runs of each test, at one version or (null) both, newest first, without reports. */ +export async function listRecentWorkflowTestRuns( + testIds: string[], + version: WorkflowTestVersion | null, + perTest: number +): Promise>> { + if (testIds.length === 0) return [] + const ranked = db + .select({ + id: workflowTestRun.id, + rank: sql`row_number() over (partition by ${workflowTestRun.testId} order by ${workflowTestRun.startedAt} desc)`.as( + 'rank' + ), + }) + .from(workflowTestRun) + .where( + and( + inArray(workflowTestRun.testId, testIds), + version ? eq(workflowTestRun.version, version) : undefined + ) + ) + .as('ranked') + return db + .select({ + id: workflowTestRun.id, + testId: workflowTestRun.testId, + workspaceId: workflowTestRun.workspaceId, + version: workflowTestRun.version, + status: workflowTestRun.status, + passed: workflowTestRun.passed, + failed: workflowTestRun.failed, + skipped: workflowTestRun.skipped, + error: workflowTestRun.error, + triggeredByActor: workflowTestRun.triggeredByActor, + triggeredByUserId: workflowTestRun.triggeredByUserId, + sourceHash: workflowTestRun.sourceHash, + ranAgainst: workflowTestRun.ranAgainst, + startedAt: workflowTestRun.startedAt, + completedAt: workflowTestRun.completedAt, + }) + .from(workflowTestRun) + .innerJoin(ranked, eq(ranked.id, workflowTestRun.id)) + .where(sql`${ranked.rank} <= ${perTest}`) + .orderBy(desc(workflowTestRun.startedAt)) +} + +/** The newest run of one test, at one version or (null) either, with its report. */ +export async function getLatestWorkflowTestRun( + testId: string, + version: WorkflowTestVersion | null +): Promise { + const [row] = await db + .select() + .from(workflowTestRun) + .where( + and( + eq(workflowTestRun.testId, testId), + version ? eq(workflowTestRun.version, version) : undefined + ) + ) + .orderBy(desc(workflowTestRun.startedAt)) + .limit(1) + return row ?? null +} + +export async function getWorkflowTestRunById( + testId: string, + runId: string +): Promise { + const [row] = await db + .select() + .from(workflowTestRun) + .where(and(eq(workflowTestRun.id, runId), eq(workflowTestRun.testId, testId))) + .limit(1) + return row ?? null +} + +export async function listWorkflowTestRunsById( + runIds: string[], + workspaceId: string +): Promise> { + if (runIds.length === 0) return [] + const rows = await db + .select({ + run: workflowTestRun, + testName: workflowTest.name, + testSourceHash: workflowTest.sourceHash, + }) + .from(workflowTestRun) + .innerJoin(workflowTest, eq(workflowTest.id, workflowTestRun.testId)) + .where(and(inArray(workflowTestRun.id, runIds), eq(workflowTestRun.workspaceId, workspaceId))) + return rows.map(({ run, testName, testSourceHash }) => ({ ...run, testName, testSourceHash })) +} diff --git a/apps/sim/lib/workflow-tests/run-test-file.ts b/apps/sim/lib/workflow-tests/run-test-file.ts new file mode 100644 index 00000000000..4ba22b65cd0 --- /dev/null +++ b/apps/sim/lib/workflow-tests/run-test-file.ts @@ -0,0 +1,55 @@ +import type { Principal } from '@sim/auth/principal' +import { generateShortId } from '@sim/utils/id' +import { runSandboxTask } from '@/lib/execution/sandbox/run-task' +import { + type TestCaseStatus, + type TestReport, + testReportSchema, +} from '@/lib/workflow-tests/protocol' +import { WorkflowTestSession } from '@/lib/workflow-tests/session' +import { + closeWorkflowTestSession, + openWorkflowTestSession, +} from '@/lib/workflow-tests/session-registry' +import type { WorkflowTestVersion } from '@/lib/workflows/application/run-workflow-for-test' + +export interface RunWorkflowTestFileInput { + principal: Principal + workspaceId: string + /** The test file's source and its path, which stack traces and check lines refer to. */ + source: string + filePath: string + version: WorkflowTestVersion + /** Full test names (`describe > it`) to run; null runs the whole file. */ + only: string[] | null + onProgress: (path: string[], status: TestCaseStatus) => Promise + signal?: AbortSignal +} + +/** + * Runs one test file in the sandbox. Each `runWorkflow()` it makes becomes a real workflow + * run whose mocked blocks are answered by the file's own mocks. + */ +export async function runWorkflowTestFile(input: RunWorkflowTestFileInput): Promise { + const requestId = generateShortId(12) + openWorkflowTestSession( + requestId, + new WorkflowTestSession({ + workspaceId: input.workspaceId, + principal: input.principal, + version: input.version, + only: input.only, + onProgress: input.onProgress, + }) + ) + try { + const bytes = await runSandboxTask( + 'workflow-test-run', + { workspaceId: input.workspaceId, code: input.source, codeFilename: input.filePath }, + { requestId, ownerKey: `workspace:${input.workspaceId}`, signal: input.signal } + ) + return testReportSchema.parse(JSON.parse(Buffer.from(bytes).toString('utf-8'))) + } finally { + closeWorkflowTestSession(requestId) + } +} diff --git a/apps/sim/lib/workflow-tests/session-registry.ts b/apps/sim/lib/workflow-tests/session-registry.ts new file mode 100644 index 00000000000..5dfb4402b28 --- /dev/null +++ b/apps/sim/lib/workflow-tests/session-registry.ts @@ -0,0 +1,19 @@ +import type { WorkflowTestSession } from '@/lib/workflow-tests/session' + +/** Live sessions by sandbox request id; each entry is removed when its sandbox run ends. */ +const sessions = new Map() + +export function openWorkflowTestSession(requestId: string, session: WorkflowTestSession): void { + sessions.set(requestId, session) +} + +export function closeWorkflowTestSession(requestId: string): void { + sessions.get(requestId)?.close() + sessions.delete(requestId) +} + +export function requireWorkflowTestSession(requestId: string): WorkflowTestSession { + const session = sessions.get(requestId) + if (!session) throw new Error('This workflow test run is no longer active') + return session +} diff --git a/apps/sim/lib/workflow-tests/session.ts b/apps/sim/lib/workflow-tests/session.ts new file mode 100644 index 00000000000..131c8e67ed0 --- /dev/null +++ b/apps/sim/lib/workflow-tests/session.ts @@ -0,0 +1,505 @@ +import { type Principal, requirePrincipalSubjectUserId } from '@sim/auth/principal' +import { getErrorMessage } from '@sim/utils/errors' +import { generateId } from '@sim/utils/id' +import { isRecordLike } from '@sim/utils/object' +import type { BlockState } from '@sim/workflow-types/workflow' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { type JudgeVerdict, judgeRubric } from '@/lib/workflow-tests/judge' +import { + createMockChannel, + type MockChannel, + type MockMatcher, + type ParkedMockCall, +} from '@/lib/workflow-tests/mock-channel' +import type { TestCaseStatus, TestEvent, TestTarget } from '@/lib/workflow-tests/protocol' +import { + runWorkflowForTest, + type WorkflowTestRunResult, + type WorkflowTestVersion, +} from '@/lib/workflows/application/run-workflow-for-test' +import { getEffectiveBlockOutputs } from '@/lib/workflows/blocks/block-outputs' +import { + loadDeployedWorkflowState, + loadWorkflowFromNormalizedTables, +} from '@/lib/workflows/persistence/utils' +import { getActiveWorkflowName, listActiveWorkflowsNamed } from '@/lib/workflows/queries' +import { generateMockPayloadFromOutputsDefinition } from '@/lib/workflows/triggers/mock-payload' +import { resolveTriggerRunOptions } from '@/lib/workflows/triggers/run-options' +import { normalizeName } from '@/executor/constants' +import type { TestWorkflowBlock } from '@/executor/execution/types' +import type { ExecutionResult } from '@/executor/types' +import { hasExecutionResult, readAttemptedExecutionId } from '@/executor/utils/errors' +import { getToolOutputsMetadata } from '@/tools/metadata-outputs' +import { hasToolId } from '@/tools/tool-ids' + +/** How long one `testNext` broker call waits before answering `waiting`, well under the broker timeout. */ +const EVENT_WAIT_MS = 25_000 + +export interface WorkflowTestSessionConfig { + workspaceId: string + principal: Principal + version: WorkflowTestVersion + /** Full test names (`describe > it`) to run; null runs every test. */ + only: string[] | null + /** Records a case starting or finishing while the file runs. */ + onProgress: (path: string[], status: TestCaseStatus) => Promise +} + +interface RunOutcome { + executionId: string + output: unknown + error?: string + logs: NonNullable +} + +function cancellableDelay(ms: number): { promise: Promise<'timeout'>; cancel: () => void } { + let timer: ReturnType | undefined + const promise = new Promise<'timeout'>((resolve) => { + timer = setTimeout(() => resolve('timeout'), ms) + }) + return { promise, cancel: () => clearTimeout(timer) } +} + +async function resolveWorkflowId(workspaceId: string, name: string): Promise { + const matches = await listActiveWorkflowsNamed(workspaceId, name) + if (matches.length === 0) throw new OrchestrationError('not_found', `No workflow named "${name}"`) + if (matches.length > 1) { + throw new OrchestrationError( + 'validation', + `More than one workflow is named "${name}"; rename one` + ) + } + return matches[0].id +} + +async function loadWorkflowBlocks( + workflowId: string, + workspaceId: string, + version: WorkflowTestVersion +): Promise> { + const state = + version === 'draft' + ? await loadWorkflowFromNormalizedTables(workflowId) + : await loadDeployedWorkflowState(workflowId, workspaceId) + if (!state?.blocks) { + throw new OrchestrationError('validation', `The workflow has no ${version} version to test`) + } + return state.blocks +} + +/** + * The trigger block a run starts from: the one the test names, or the workflow's only one. + * A workflow with several triggers must be told which, by name. + */ +function chooseTrigger( + workflow: string, + blocks: Record, + trigger: string | null +): string | undefined { + const options = resolveTriggerRunOptions(blocks) + if (trigger !== null) { + const chosen = options.find( + (option) => normalizeName(option.blockName) === normalizeName(trigger) + ) + if (!chosen) { + throw new OrchestrationError( + 'validation', + `${workflow} has no trigger named "${trigger}". Its triggers: ${options.map((option) => option.blockName).join(', ')}` + ) + } + return chosen.triggerBlockId + } + if (options.length > 1) { + throw new OrchestrationError( + 'validation', + `${workflow} has ${options.length} triggers; choose one with runWorkflow(name, input, { trigger: '…' }): ${options.map((option) => option.blockName).join(', ')}` + ) + } + return undefined +} + +/** Tools test files may name: catalog tools, and MCP and custom tools by their prefixed ids. */ +function assertKnownTool(toolId: string): void { + if (hasToolId(toolId) || toolId.startsWith('mcp-') || toolId.startsWith('custom_')) return + throw new OrchestrationError('validation', `mockTool("${toolId}"): there is no tool with that id`) +} + +/** Placeholder output shaped like a block's real one, for its configured operation. */ +function sampleBlockOutput(block: TestWorkflowBlock | undefined): Record { + if (!block) return {} + const subBlocks: Record = {} + for (const [key, value] of Object.entries(block.params)) subBlocks[key] = { value } + return generateMockPayloadFromOutputsDefinition(getEffectiveBlockOutputs(block.type, subBlocks)) +} + +function sampleToolOutput(toolId: string): Record { + const outputs = getToolOutputsMetadata(toolId) + return outputs ? generateMockPayloadFromOutputsDefinition(outputs) : {} +} + +interface ToolMock { + /** The Agent block it answers for; null answers every Agent. */ + blockId: string | null + toolId: string + key: string +} + +/** The child a workflow block calls, when it names one by a fixed id rather than a reference. */ +function childWorkflowId(id: unknown): string | undefined { + return typeof id === 'string' && id.trim() !== '' && !id.includes('<') ? id : undefined +} + +/** The mock answering this Agent's call to a tool: one named for that Agent wins over a general one. */ +function toolMockFor(mocks: ToolMock[], blockId: string, toolId: string): ToolMock | undefined { + return ( + mocks.find((mock) => mock.toolId === toolId && mock.blockId === blockId) ?? + mocks.find((mock) => mock.toolId === toolId && mock.blockId === null) + ) +} + +/** + * Matches one `runWorkflow()` call's mocks and spies to blocks by name, as each workflow it + * executes (the tested one and every child) announces its blocks. A target naming a workflow + * only reaches that workflow's blocks; an unnamed one reaches every workflow in the run. + */ +class RunMatcher implements MockMatcher { + private readonly blocks = new Map() + private readonly mockKeyByBlockId = new Map() + private readonly spyKeyByBlockId = new Map() + private readonly toolMocks: ToolMock[] = [] + private readonly workflowNames = new Map() + /** Keys whose target named a block in some workflow this run executed. */ + readonly matchedKeys = new Set() + readonly spyCalls: Array<{ key: string; input: unknown; output: unknown }> = [] + private readonly targets: TestTarget[] + + /** Workflows whose block names are known: every one that ran, and the children they call. */ + private readonly knownWorkflows = new Set() + + constructor( + targets: TestTarget[], + private readonly loadChildBlocks: (workflowId: string) => Promise> + ) { + for (const target of targets) if (target.kind === 'tool') assertKnownTool(target.tool) + this.targets = [...targets].sort( + (a, b) => Number(a.workflow === null) - Number(b.workflow === null) + ) + } + + async enterWorkflow({ + workflowId, + blocks, + }: { + workflowId: string + blocks: TestWorkflowBlock[] + }): Promise { + const workflowName = await this.workflowName(workflowId) + for (const block of blocks) this.blocks.set(block.id, block) + await this.learnWorkflow( + workflowId, + blocks.map((block) => ({ + name: block.name, + childWorkflowId: childWorkflowId(block.params.workflowId), + })) + ) + for (const target of this.targets) { + if (target.workflow !== null && normalizeName(target.workflow) !== workflowName) continue + if (target.kind === 'tool' && target.block === null) { + if (!this.toolMocks.some((mock) => mock.key === target.key)) { + this.toolMocks.push({ blockId: null, toolId: target.tool, key: target.key }) + } + this.matchedKeys.add(target.key) + continue + } + const targetName = normalizeName(target.block ?? '') + for (const block of blocks) { + if (normalizeName(block.name) !== targetName) continue + this.matchedKeys.add(target.key) + if (target.kind === 'tool') { + if ( + !this.toolMocks.some((mock) => mock.key === target.key && mock.blockId === block.id) + ) { + this.toolMocks.push({ blockId: block.id, toolId: target.tool, key: target.key }) + } + } else if (target.kind === 'mock') { + if (!this.mockKeyByBlockId.has(block.id)) this.mockKeyByBlockId.set(block.id, target.key) + this.spyKeyByBlockId.delete(block.id) + } else if (!this.mockKeyByBlockId.has(block.id) && !this.spyKeyByBlockId.has(block.id)) { + this.spyKeyByBlockId.set(block.id, target.key) + } + } + } + } + + /** + * Marks the targets naming a block of this workflow as real, then does the same for each + * child it calls by a fixed id. A mock of a child the run never reached is set up, not a typo. + */ + private async learnWorkflow( + workflowId: string, + blocks: Array<{ name: string; childWorkflowId: string | undefined }> + ): Promise { + if (this.knownWorkflows.has(workflowId)) return + this.knownWorkflows.add(workflowId) + const workflowName = await this.workflowName(workflowId) + const names = new Set(blocks.map((block) => normalizeName(block.name))) + for (const target of this.targets) { + if (target.workflow !== null && normalizeName(target.workflow) !== workflowName) continue + if (target.block === null || names.has(normalizeName(target.block))) { + this.matchedKeys.add(target.key) + } + } + for (const block of blocks) { + if (!block.childWorkflowId || this.knownWorkflows.has(block.childWorkflowId)) continue + const childBlocks = await this.loadChildBlocks(block.childWorkflowId) + await this.learnWorkflow( + block.childWorkflowId, + Object.values(childBlocks).map((child) => ({ + name: child.name, + childWorkflowId: childWorkflowId(child.subBlocks?.workflowId?.value), + })) + ) + } + } + + private async workflowName(workflowId: string): Promise { + const known = this.workflowNames.get(workflowId) + if (known !== undefined) return known + const name = normalizeName((await getActiveWorkflowName(workflowId)) ?? workflowId) + this.workflowNames.set(workflowId, name) + return name + } + + mocksBlock(blockId: string): boolean { + return this.mockKeyByBlockId.has(blockId) + } + + mocksTool(blockId: string, toolId: string): boolean { + return toolMockFor(this.toolMocks, blockId, toolId) !== undefined + } + + spiesBlock(blockId: string): boolean { + return this.spyKeyByBlockId.has(blockId) + } + + recordSpy(call: { blockId: string; input: unknown; output: unknown }): void { + const key = this.spyKeyByBlockId.get(call.blockId) + if (key) this.spyCalls.push({ key, input: call.input, output: call.output }) + } + + mockKey(blockId: string): string | undefined { + return this.mockKeyByBlockId.get(blockId) + } + + toolMockKey(blockId: string, toolId: string): string | undefined { + return toolMockFor(this.toolMocks, blockId, toolId)?.key + } + + block(blockId: string): TestWorkflowBlock | undefined { + return this.blocks.get(blockId) + } +} + +/** One `runWorkflow()` call: an executor run whose mocked blocks park on `channel`. */ +class TestWorkflowRun { + readonly id = generateId() + private pendingCall: Promise | undefined + private readonly outcome: Promise + + constructor( + private readonly channel: MockChannel, + execution: Promise, + private readonly abort: AbortController, + private readonly matcher: RunMatcher + ) { + this.outcome = execution.then( + ({ executionId, result }) => this.toOutcome(executionId, result), + (error: unknown) => { + const executionId = readAttemptedExecutionId(error) ?? '' + if (hasExecutionResult(error)) return this.toOutcome(executionId, error.executionResult) + return { executionId, output: null, error: getErrorMessage(error), logs: [] } + } + ) + void this.outcome.finally(() => this.channel.close(new Error('The workflow run finished'))) + } + + private toOutcome(executionId: string, result: ExecutionResult): RunOutcome { + const error = + result.status === 'paused' + ? 'The workflow paused for human input. Mock the human-in-the-loop block to test past it.' + : result.success + ? undefined + : (result.error ?? 'The workflow run failed') + return { + executionId, + output: result.output, + ...(error ? { error } : {}), + logs: result.logs ?? [], + } + } + + async next(): Promise { + this.pendingCall ??= this.channel.nextCall() + this.pendingCall.catch(() => {}) + const delay = cancellableDelay(EVENT_WAIT_MS) + try { + const winner = await Promise.race([ + this.pendingCall.then((call) => ({ type: 'call' as const, call })), + this.outcome.then((outcome) => ({ type: 'done' as const, outcome })), + delay.promise.then(() => ({ type: 'waiting' as const })), + ]) + if (winner.type === 'waiting') return { kind: 'waiting', runId: this.id } + if (winner.type === 'done') return this.doneEvent(winner.outcome) + this.pendingCall = undefined + return this.mockEvent(winner.call) + } finally { + delay.cancel() + } + } + + private mockEvent(parked: ParkedMockCall): TestEvent { + if (parked.kind === 'tool') { + const { blockId, toolId, input } = parked.call + const key = this.matcher.toolMockKey(blockId, toolId) + if (!key) throw new Error(`Tool ${toolId} was parked without a mock`) + return { + kind: 'mock', + runId: this.id, + callId: parked.callId, + key, + block: this.matcher.block(blockId)?.name ?? blockId, + tool: toolId, + input, + sample: sampleToolOutput(toolId), + } + } + const { blockId, blockName, input, branchIndex } = parked.call + const key = this.matcher.mockKey(blockId) + if (!key) throw new Error(`Block ${blockName} was parked without a mock`) + return { + kind: 'mock', + runId: this.id, + callId: parked.callId, + key, + block: blockName, + input, + sample: sampleBlockOutput(this.matcher.block(blockId)), + ...(branchIndex !== undefined ? { branchIndex } : {}), + } + } + + private doneEvent(outcome: RunOutcome): TestEvent { + return { + kind: 'done', + runId: this.id, + executionId: outcome.executionId, + output: outcome.output, + ...(outcome.error ? { error: outcome.error } : {}), + spyCalls: this.matcher.spyCalls, + matchedKeys: [...this.matcher.matchedKeys], + } + } + + reply(callId: string, reply: { output: unknown } | { error: string }): void { + if ('error' in reply) { + this.channel.reply(callId, { error: reply.error }) + return + } + if (!isRecordLike(reply.output)) { + this.channel.reply(callId, { + error: 'A mocked block must return an object, like { ok: true }', + }) + return + } + this.channel.reply(callId, { output: reply.output }) + } + + cancel(): void { + this.abort.abort(new Error('The test finished')) + this.channel.close(new Error('The test finished')) + } +} + +/** + * Host state for one test file's sandbox run: the workflow runs its tests started, keyed by + * run id. The brokers reach it through the sandbox request id. + */ +export class WorkflowTestSession { + private readonly runs = new Map() + + constructor(readonly config: WorkflowTestSessionConfig) {} + + async start(args: { + workflow: string + trigger: string | null + input: unknown + targets: TestTarget[] + }): Promise { + const { workspaceId, principal, version } = this.config + const workflowId = await resolveWorkflowId(workspaceId, args.workflow) + const blocks = await loadWorkflowBlocks(workflowId, workspaceId, version) + const triggerBlockId = chooseTrigger(args.workflow, blocks, args.trigger) + const matcher = new RunMatcher(args.targets, (childId) => + loadWorkflowBlocks(childId, workspaceId, version) + ) + const channel = createMockChannel(matcher) + const abort = new AbortController() + const execution = runWorkflowForTest({ + principal, + workflowId, + workspaceId, + version, + workflowInput: args.input, + triggerBlockId, + testHooks: channel.hooks, + abortSignal: abort.signal, + }) + const run = new TestWorkflowRun(channel, execution, abort, matcher) + this.runs.set(run.id, run) + return run.next() + } + + progress(args: { path: string[]; status: TestCaseStatus }): Promise { + return this.config.onProgress(args.path, args.status) + } + + next(runId: string): Promise { + return this.requireRun(runId).next() + } + + reply( + runId: string, + callId: string, + reply: { output: unknown } | { error: string } + ): Promise { + const run = this.requireRun(runId) + run.reply(callId, reply) + return run.next() + } + + cancel(runIds: string[]): void { + for (const runId of runIds) { + this.runs.get(runId)?.cancel() + this.runs.delete(runId) + } + } + + close(): void { + this.cancel([...this.runs.keys()]) + } + + judge(args: { value: string; rubric: string }): Promise { + return judgeRubric({ + workspaceId: this.config.workspaceId, + actorUserId: requirePrincipalSubjectUserId(this.config.principal), + value: args.value, + rubric: args.rubric, + }) + } + + private requireRun(runId: string): TestWorkflowRun { + const run = this.runs.get(runId) + if (!run) throw new Error(`Unknown workflow run "${runId}"`) + return run + } +} diff --git a/apps/sim/lib/workflow-tests/source-write.ts b/apps/sim/lib/workflow-tests/source-write.ts new file mode 100644 index 00000000000..097fcd2463d --- /dev/null +++ b/apps/sim/lib/workflow-tests/source-write.ts @@ -0,0 +1,38 @@ +import { isUtf8 } from 'node:buffer' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { readWorkflowTestCases, testSourceHash } from '@/lib/workflow-tests/definition' +import { + getLiveWorkflowTestByBodyFileId, + updateWorkflowTestCases, +} from '@/lib/workflow-tests/repository' + +/** Test files are source code; anything larger is not a test file. */ +export const MAX_TEST_SOURCE_BYTES = 512 * 1024 + +function decodeTestSource(content: Buffer): string { + if (content.length > MAX_TEST_SOURCE_BYTES) { + throw new OrchestrationError( + 'payload_too_large', + `A test file can be at most ${MAX_TEST_SOURCE_BYTES / 1024}KB` + ) + } + if (!isUtf8(content)) throw new OrchestrationError('validation', 'A test file must be UTF-8 text') + return content.toString('utf-8') +} + +/** Validates new source for an existing test file; the returned step records its cases. */ +export async function prepareWorkflowTestSourceWrite(input: { + fileId: string + workspaceId: string + content: Buffer +}): Promise<() => Promise> { + const test = await getLiveWorkflowTestByBodyFileId(input.fileId) + if (!test) throw new OrchestrationError('not_found', 'File not found') + const source = decodeTestSource(input.content) + const cases = await readWorkflowTestCases({ + workspaceId: input.workspaceId, + name: test.name, + source, + }) + return () => updateWorkflowTestCases(test.id, cases, testSourceHash(source)) +} diff --git a/apps/sim/lib/workflows/application/run-workflow-for-test.ts b/apps/sim/lib/workflows/application/run-workflow-for-test.ts new file mode 100644 index 00000000000..d766f4d34bc --- /dev/null +++ b/apps/sim/lib/workflows/application/run-workflow-for-test.ts @@ -0,0 +1,103 @@ +import { type Principal, requirePrincipalSubjectUserId } from '@sim/auth/principal' +import { generateId } from '@sim/utils/id' +import { resolveBillingAttribution } from '@/lib/billing/core/billing-attribution' +import { checkExecutionUsageLimits } from '@/lib/billing/core/usage-gate-cache' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { generateRequestId } from '@/lib/core/utils/request' +import { resolveActiveWorkflowApplicationContext } from '@/lib/workflows/application/context' +import { resolveTriggerExecution } from '@/lib/workflows/application/run-workflow-from-copilot' +import { executeWorkflow } from '@/lib/workflows/executor/execute-workflow' +import type { ExecutionTestHooks } from '@/executor/execution/types' +import type { ExecutionResult } from '@/executor/types' +import { attachAttemptedExecutionId } from '@/executor/utils/errors' + +export type WorkflowTestVersion = 'draft' | 'deployed' + +export interface WorkflowTestRunResult { + executionId: string + result: ExecutionResult +} + +export interface RunWorkflowForTestInput { + principal: Principal + workflowId: string + /** The test's workspace; a workflow from any other workspace is not found. */ + workspaceId: string + version: WorkflowTestVersion + workflowInput: unknown + /** The trigger to start from; omitted, the workflow's only runnable trigger. */ + triggerBlockId?: string + testHooks: ExecutionTestHooks + abortSignal: AbortSignal +} + +/** + * One workflow run inside a workflow test: the workflow's single runnable trigger, the + * test's input, and its mocks installed as executor test hooks. Billed and logged like any + * run, under the `test` trigger. + * + * A step of the `workflow_tests.run` operation, which authorized the caller's write access to + * the workspace before any test ran; nothing else may call it. Each nested run is not + * authorized again, so a delegated caller's token cannot lapse halfway through a suite. + */ +export async function runWorkflowForTest( + input: RunWorkflowForTestInput +): Promise { + const context = await resolveActiveWorkflowApplicationContext({ + workflowId: input.workflowId, + assertedWorkspaceId: input.workspaceId, + }) + const principal = input.principal + const useDraftState = input.version === 'draft' + const prepared = await resolveTriggerExecution({ + input: { + workflowId: input.workflowId, + useDraftState, + workflowInput: input.workflowInput, + hasWorkflowInput: true, + ...(input.triggerBlockId ? { triggerBlockId: input.triggerBlockId } : {}), + useMockPayload: false, + }, + workspaceId: context.workspaceId, + }) + // actorless-unsupported: tests run only for session, personal-key, OAuth, or delegated principals; workflow_tests.run admits no executor or workspace-key caller. + const actorUserId = requirePrincipalSubjectUserId(principal) + const executionId = generateId() + const billingAttribution = await resolveBillingAttribution({ + actorUserId, + workspaceId: context.workspaceId, + }) + const usage = await checkExecutionUsageLimits(billingAttribution) + if (usage.isExceeded) { + throw new OrchestrationError('forbidden', usage.message ?? 'Usage limit exceeded') + } + try { + const result = await executeWorkflow( + { + id: context.workflowId, + userId: context.workflow.userId, + workspaceId: context.workspaceId, + variables: context.workflow.variables || {}, + }, + generateRequestId(), + prepared.input, + actorUserId, + { + enabled: true, + principal, + useDraftState, + workflowTriggerType: 'test', + enforceCredentialAccess: true, + triggerBlockId: prepared.triggerBlockId, + abortSignal: input.abortSignal, + billingAttribution, + testHooks: input.testHooks, + }, + executionId + ) + return { executionId, result } + } catch (error) { + attachAttemptedExecutionId(error, executionId) + throw error + } +} diff --git a/apps/sim/lib/workflows/application/run-workflow-from-copilot.ts b/apps/sim/lib/workflows/application/run-workflow-from-copilot.ts index 97300d598ef..56bb9b37fc8 100644 --- a/apps/sim/lib/workflows/application/run-workflow-from-copilot.ts +++ b/apps/sim/lib/workflows/application/run-workflow-from-copilot.ts @@ -94,7 +94,10 @@ interface SnapshotCopilotRunInput extends BaseCopilotRunInput { export interface RunFromBlockFromCopilotInput extends SnapshotCopilotRunInput {} export interface RunBlockFromCopilotInput extends SnapshotCopilotRunInput {} -async function loadDefinition(input: BaseCopilotRunInput, workspaceId: string) { +async function loadDefinition( + input: Pick, + workspaceId: string +) { if (input.useDraftState) return loadWorkflowFromNormalizedTables(input.workflowId) try { return await loadDeployedWorkflowState(input.workflowId, workspaceId) @@ -104,8 +107,20 @@ async function loadDefinition(input: BaseCopilotRunInput, workspaceId: string) { } } -async function resolveTriggerExecution(params: { - input: TriggerCopilotRunInput +export type TriggerExecutionRequest = Pick< + TriggerCopilotRunInput, + | 'workflowId' + | 'useDraftState' + | 'triggerBlockId' + | 'workflowInput' + | 'hasWorkflowInput' + | 'useMockPayload' + | 'inputFromExecutionId' +> + +/** Picks the workflow's runnable trigger and validates the run input against it. */ +export async function resolveTriggerExecution(params: { + input: TriggerExecutionRequest workspaceId: string }): Promise<{ triggerBlockId: string; input: unknown }> { const state = await loadDefinition(params.input, params.workspaceId) diff --git a/apps/sim/lib/workflows/executor/execute-workflow.ts b/apps/sim/lib/workflows/executor/execute-workflow.ts index 01c14e76eb8..10167c86562 100644 --- a/apps/sim/lib/workflows/executor/execute-workflow.ts +++ b/apps/sim/lib/workflows/executor/execute-workflow.ts @@ -16,6 +16,7 @@ import { ExecutionSnapshot } from '@/executor/execution/snapshot' import type { BlockCompletionCallbackData, ExecutionMetadata, + ExecutionTestHooks, SerializableExecutionState, } from '@/executor/execution/types' import type { ExecutionResult, StreamingExecution } from '@/executor/types' @@ -61,6 +62,8 @@ export interface ExecuteWorkflowOptions { workflowStateOverride?: NonNullable /** Stop execution after this block completes. Used for "run until block" feature. */ stopAfterBlockId?: string + /** Workflow test run: mocked blocks await these hooks instead of running. */ + testHooks?: ExecutionTestHooks /** Run-from-block configuration using a prior execution snapshot. */ runFromBlock?: { startBlockId: string @@ -226,6 +229,7 @@ export async function executeWorkflow( base64MaxBytes: streamConfig?.base64MaxBytes, abortSignal: streamConfig?.abortSignal, stopAfterBlockId: streamConfig?.stopAfterBlockId, + testHooks: streamConfig?.testHooks, trustedInitialResolvedSecretTraceProvenance: streamConfig?.trustedInitialResolvedSecretTraceProvenance, runFromBlock: streamConfig?.runFromBlock, diff --git a/apps/sim/lib/workflows/executor/execution-core.ts b/apps/sim/lib/workflows/executor/execution-core.ts index d7c05f420f9..7cda1cd294e 100644 --- a/apps/sim/lib/workflows/executor/execution-core.ts +++ b/apps/sim/lib/workflows/executor/execution-core.ts @@ -55,6 +55,7 @@ import type { ChildWorkflowContext, ContextExtensions, ExecutionCallbacks, + ExecutionTestHooks, IterationContext, SerializableExecutionState, } from '@/executor/execution/types' @@ -140,6 +141,7 @@ export interface ExecuteWorkflowCoreOptions { includeFileBase64?: boolean base64MaxBytes?: number stopAfterBlockId?: string + testHooks?: ExecutionTestHooks /** Trusted encrypted provenance captured by a server-only pre-execution boundary. */ trustedInitialResolvedSecretTraceProvenance?: ResolvedSecretTraceProvenanceV1 /** Immutable deployment admitted by the durable parent log for a resumed execution. */ @@ -632,6 +634,7 @@ async function executeWorkflowCoreImpl( includeFileBase64, base64MaxBytes, stopAfterBlockId, + testHooks, runFromBlock, resumeDeploymentVersionId, draftState, @@ -1242,6 +1245,7 @@ async function executeWorkflowCoreImpl( includeFileBase64, base64MaxBytes, stopAfterBlockId: resolvedStopAfterBlockId, + ...(testHooks ? { testHooks } : {}), onChildWorkflowInstanceReady, callChain: metadata.callChain, // The live block stream has a single known, authenticated Sim viewer only on diff --git a/apps/sim/lib/workflows/queries.ts b/apps/sim/lib/workflows/queries.ts index f989b3e4d49..6a2c33f16bd 100644 --- a/apps/sim/lib/workflows/queries.ts +++ b/apps/sim/lib/workflows/queries.ts @@ -119,6 +119,31 @@ export async function listWorkspaceWorkflows(input: ListWorkspaceWorkflowsInput) return keysetPage(keys, rows, input.limit) } +/** Active workflows in a workspace with exactly this name; names are not unique, so callers decide. */ +export async function listActiveWorkflowsNamed(workspaceId: string, name: string) { + return db + .select({ id: workflow.id, name: workflow.name }) + .from(workflow) + .where( + and( + eq(workflow.workspaceId, workspaceId), + isNull(workflow.archivedAt), + eq(workflow.name, name) + ) + ) + .limit(2) +} + +/** An active workflow's name by id, or null when it is archived or gone. */ +export async function getActiveWorkflowName(workflowId: string): Promise { + const [row] = await db + .select({ name: workflow.name }) + .from(workflow) + .where(and(eq(workflow.id, workflowId), isNull(workflow.archivedAt))) + .limit(1) + return row?.name ?? null +} + /** * Loads one consistent workflow record + normalized definition snapshot. Both * the editor route and public metadata route derive their own response from diff --git a/apps/sim/lib/workspace-files/application/edit-workspace-file-content.ts b/apps/sim/lib/workspace-files/application/edit-workspace-file-content.ts index 451900fa920..f22c58728c6 100644 --- a/apps/sim/lib/workspace-files/application/edit-workspace-file-content.ts +++ b/apps/sim/lib/workspace-files/application/edit-workspace-file-content.ts @@ -1,5 +1,6 @@ import { isUtf8 } from 'node:buffer' import { AuditAction, AuditResourceType } from '@sim/audit' +import type { Principal } from '@sim/auth/principal' import { resolvePrincipalAttribution } from '@sim/auth/principal' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' @@ -19,6 +20,7 @@ import { defineAuthorizedWorkspaceFileUseCase } from '@/lib/workspace-files/appl import { parseWorkspaceFileRevision } from '@/lib/workspace-files/application/file-revision' import { resolveWorkspaceFileVersionWrite } from '@/lib/workspace-files/application/file-version-write' import { fileOperations } from '@/lib/workspace-files/application/operations' +import { prepareOwnedFileContentWrite } from '@/lib/workspace-files/application/owned-file-content' import { resolveActiveWorkspaceFileContext } from '@/lib/workspace-files/application/workspace-file-context' import { applyWorkspaceFileContentEdit, @@ -73,8 +75,13 @@ export interface EditWorkspaceFileContentResult { */ export const editWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.updateContent, - resolveContext: ({ input }: { input: EditWorkspaceFileContentInput }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: EditWorkspaceFileContentInput + }) => resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ principal, input, context }): Promise { const lockKey = `file-edit:${context.workspaceId}:${context.fileId}` const lockValue = `${Date.now()}-${generateShortId()}` @@ -92,7 +99,9 @@ export const editWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ } try { - const file = await getWorkspaceFileWithCurrentVersion(context.workspaceId, context.fileId) + const file = await getWorkspaceFileWithCurrentVersion(context.workspaceId, context.fileId, { + includeTestFiles: true, + }) if (!file) throw new OrchestrationError('not_found', 'File not found') if (!file.contentUpdatedAt) { throw new OrchestrationError( @@ -174,6 +183,7 @@ export const editWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ ) } + const commitOwner = await prepareOwnedFileContentWrite(context, content) const attribution = resolvePrincipalAttribution(principal, { workspaceBillingOwnerUserId: context.billedAccountUserId, }) @@ -212,6 +222,7 @@ export const editWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ throw error } + await commitOwner() logger.info('Edited workspace file content', { workspaceId: context.workspaceId, fileId: context.fileId, diff --git a/apps/sim/lib/workspace-files/application/export-workspace-file-snapshot.test.ts b/apps/sim/lib/workspace-files/application/export-workspace-file-snapshot.test.ts index 844f2108b24..8509d915365 100644 --- a/apps/sim/lib/workspace-files/application/export-workspace-file-snapshot.test.ts +++ b/apps/sim/lib/workspace-files/application/export-workspace-file-snapshot.test.ts @@ -109,6 +109,7 @@ describe('exportWorkspaceFileSnapshot', () => { expect(mocks.getFile).toHaveBeenCalledWith(WORKSPACE_ID, 'image-1', { throwOnError: true, includeChatUploads: true, + includeTestFiles: true, }) expect(mocks.resolvePermission).toHaveBeenCalledTimes(3) expect(mocks.audit).toHaveBeenCalledOnce() diff --git a/apps/sim/lib/workspace-files/application/export-workspace-file-snapshot.ts b/apps/sim/lib/workspace-files/application/export-workspace-file-snapshot.ts index 21043a036cf..32f5f4be6e1 100644 --- a/apps/sim/lib/workspace-files/application/export-workspace-file-snapshot.ts +++ b/apps/sim/lib/workspace-files/application/export-workspace-file-snapshot.ts @@ -1,4 +1,5 @@ import { AuditAction, AuditResourceType } from '@sim/audit' +import type { Principal } from '@sim/auth/principal' import { PASTE_LIMITS, utf8ByteLength } from '@sim/utils/paste' import { asOrchestrationError, OrchestrationError } from '@/lib/core/orchestration/types' import { MATERIALIZE_CONCURRENCY, mapWithConcurrency } from '@/lib/core/utils/concurrency' @@ -21,10 +22,18 @@ export interface ExportWorkspaceFileSnapshotInput { /** Exports the visible snapshot without writing or advancing the collaborative document. */ export const exportWorkspaceFileSnapshot = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.download, - resolveContext: ({ input }: { input: ExportWorkspaceFileSnapshotInput }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: ExportWorkspaceFileSnapshotInput + }) => resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ principal, input, context }) { - const file = await getWorkspaceFile(context.workspaceId, context.fileId, { throwOnError: true }) + const file = await getWorkspaceFile(context.workspaceId, context.fileId, { + throwOnError: true, + includeTestFiles: true, + }) if (!file) throw new OrchestrationError('not_found', 'File not found') if (!isMarkdownFile(file) && file.type !== 'text/x-markdown') { throw new OrchestrationError('validation', 'Only Markdown files support snapshot export') diff --git a/apps/sim/lib/workspace-files/application/file-versions.ts b/apps/sim/lib/workspace-files/application/file-versions.ts index 1b7cd7da919..e2ab6748dd4 100644 --- a/apps/sim/lib/workspace-files/application/file-versions.ts +++ b/apps/sim/lib/workspace-files/application/file-versions.ts @@ -41,6 +41,7 @@ import { import { parseWorkspaceFileRevision } from '@/lib/workspace-files/application/file-revision' import { resolveWorkspaceFileVersionWrite } from '@/lib/workspace-files/application/file-version-write' import { fileOperations } from '@/lib/workspace-files/application/operations' +import { prepareOwnedFileContentWrite } from '@/lib/workspace-files/application/owned-file-content' import { extractWorkspaceFileRecordText, type ReadWorkspaceFileTextInput, @@ -105,7 +106,10 @@ export interface RevertWorkspaceFileVersionResult { } async function loadActiveFile(context: ActiveWorkspaceFileContext): Promise { - const file = await getWorkspaceFile(context.workspaceId, context.fileId, { throwOnError: true }) + const file = await getWorkspaceFile(context.workspaceId, context.fileId, { + throwOnError: true, + includeTestFiles: true, + }) if (!file) throw new OrchestrationError('not_found', 'File not found') return file } @@ -172,8 +176,13 @@ function recordAtVersion( export const listWorkspaceFileVersions = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.listVersions, - resolveContext: ({ input }: { input: ListWorkspaceFileVersionsInput }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: ListWorkspaceFileVersionsInput + }) => resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ input, context }): Promise { const file = await loadActiveFile(context) const { versions, nextKeys } = await queryWorkspaceFileVersions(file, { @@ -187,8 +196,8 @@ export const listWorkspaceFileVersions = defineAuthorizedWorkspaceFileUseCase({ export const readWorkspaceFileVersion = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.readVersion, - resolveContext: ({ input }: { input: FileVersionRef }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ principal, input }: { principal: Principal; input: FileVersionRef }) => + resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ input, context }): Promise { const file = await loadActiveFile(context) return { version: await loadVersion(file, input.version) } @@ -197,8 +206,13 @@ export const readWorkspaceFileVersion = defineAuthorizedWorkspaceFileUseCase({ export const readWorkspaceFileVersionText = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.readVersionContent, - resolveContext: ({ input }: { input: ReadWorkspaceFileVersionTextInput }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: ReadWorkspaceFileVersionTextInput + }) => resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ input, context, @@ -224,8 +238,8 @@ export const readWorkspaceFileVersionText = defineAuthorizedWorkspaceFileUseCase export const downloadWorkspaceFileVersion = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.downloadVersion, - resolveContext: ({ input }: { input: FileVersionRef }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ principal, input }: { principal: Principal; input: FileVersionRef }) => + resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ input, context, principal }): Promise { const file = await loadActiveFile(context) const version = await loadVersion(file, input.version) @@ -309,6 +323,7 @@ async function executeRevertWorkspaceFileVersion({ const attribution = resolvePrincipalAttribution(principal, { workspaceBillingOwnerUserId: context.billedAccountUserId, }) + const commitOwner = await prepareOwnedFileContentWrite(context, content) let updated: Awaited> try { updated = await updateWorkspaceFileContent( @@ -333,6 +348,7 @@ async function executeRevertWorkspaceFileVersion({ throw error } + await commitOwner() logger.info('Reverted workspace file to a previous version', { workspaceId: context.workspaceId, fileId: context.fileId, @@ -354,8 +370,13 @@ async function executeRevertWorkspaceFileVersion({ */ export const revertWorkspaceFileVersion = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.revertVersion, - resolveContext: ({ input }: { input: RevertWorkspaceFileVersionInput }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: RevertWorkspaceFileVersionInput + }) => resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), execute: executeRevertWorkspaceFileVersion, projectAudit: ({ input, result }) => result.reverted @@ -388,8 +409,8 @@ export interface DeleteWorkspaceFileVersionResult { */ export const deleteWorkspaceFileVersion = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.deleteVersion, - resolveContext: ({ input }: { input: FileVersionRef }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ principal, input }: { principal: Principal; input: FileVersionRef }) => + resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ input, context }): Promise { const file = await loadActiveFile(context) const target = await loadVersion(file, input.version) diff --git a/apps/sim/lib/workspace-files/application/owned-file-content.ts b/apps/sim/lib/workspace-files/application/owned-file-content.ts new file mode 100644 index 00000000000..b5d8b9c525a --- /dev/null +++ b/apps/sim/lib/workspace-files/application/owned-file-content.ts @@ -0,0 +1,19 @@ +import type { ActiveWorkspaceFileContext } from '@/lib/uploads/contexts/workspace/workspace-file-manager' + +/** + * Checks content headed for a file another resource owns before it is stored, and returns + * what to run once it is. A test file's source must load and declare one concern, and its + * test then records the new title and cases; every other file passes unchanged. + */ +export async function prepareOwnedFileContentWrite( + context: Pick, + content: Buffer +): Promise<() => Promise> { + if (context.fileContext !== 'test') return async () => {} + const { prepareWorkflowTestSourceWrite } = await import('@/lib/workflow-tests/source-write') + return prepareWorkflowTestSourceWrite({ + fileId: context.fileId, + workspaceId: context.workspaceId, + content, + }) +} diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-artifact.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-artifact.ts index 9956025ed5b..6df08cb0f17 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-artifact.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-artifact.ts @@ -25,7 +25,11 @@ export const readWorkspaceFileArtifact = defineAuthorizedWorkspaceFileUseCase({ }: { principal: Principal input: ReadWorkspaceFileArtifactInput - }) => resolveReferencedWorkspaceFileContext(principal, input, { includeChatUploads: true }), + }) => + resolveReferencedWorkspaceFileContext(principal, input, { + includeChatUploads: true, + includeTestFiles: true, + }), async execute({ input, context, principal }) { const file = context.file const safe = await isOpaqueWorkspaceFileEgressSafe(context.workspaceId, { diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-content-by-key.test.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-content-by-key.test.ts index b885e7d1728..1b41c6e4a8d 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-content-by-key.test.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-content-by-key.test.ts @@ -78,6 +78,7 @@ describe('readWorkspaceFileContentByKey', () => { expect(mocks.getFile).toHaveBeenCalledWith(file.workspaceId, file.id, { throwOnError: true, + includeTestFiles: true, }) expect(mocks.fetchContent).toHaveBeenCalledWith(file, { maxBytes: MAX_BUFFERED_TRANSFER_BYTES, @@ -104,11 +105,12 @@ describe('readWorkspaceFileContentByKey', () => { }) ).resolves.toEqual({ file }) - expect(mocks.getMetadata).toHaveBeenCalledWith(file.key, 'workspace') - expect(mocks.loadContext).toHaveBeenCalledWith(file.id) + expect(mocks.getMetadata).toHaveBeenCalledWith(file.key) + expect(mocks.loadContext).toHaveBeenCalledWith(file.id, undefined) expect(mocks.resolvePermission).not.toHaveBeenCalled() expect(mocks.getFile).toHaveBeenCalledWith(file.workspaceId, file.id, { throwOnError: true, + includeTestFiles: true, }) expect(mocks.fetchContent).not.toHaveBeenCalled() }) diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-content-by-key.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-content-by-key.ts index 066c49022a2..a0da80874b5 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-content-by-key.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-content-by-key.ts @@ -1,3 +1,4 @@ +import type { Principal } from '@sim/auth/principal' import type { AuthorizedWorkspaceUseCaseContext } from '@/lib/core/application' import { OrchestrationError } from '@/lib/core/orchestration/types' import { @@ -11,6 +12,7 @@ import { getFileMetadataByKey } from '@/lib/uploads/server/metadata' import { MAX_BUFFERED_TRANSFER_BYTES } from '@/lib/uploads/shared/types' import { defineAuthorizedWorkspaceFileUseCase } from '@/lib/workspace-files/application/authorized-workspace-file-use-case' import { fileOperations } from '@/lib/workspace-files/application/operations' +import { assertOwnedFileAccess } from '@/lib/workspace-files/application/workspace-file-context' export interface ReadWorkspaceFileByKeyInput { key: string @@ -32,6 +34,7 @@ async function loadCurrentWorkspaceFileByKey( ): Promise { const file = await getWorkspaceFile(context.workspaceId, context.fileId, { throwOnError: true, + includeTestFiles: true, }) if (!file || file.key !== input.key) throw new OrchestrationError('not_found', 'File not found') return file @@ -52,22 +55,32 @@ async function executeReadWorkspaceFileContentByKey({ } } +/** Workspace files and test files share the `workspace/` key prefix; the row says which. */ +const KEY_READABLE_CONTEXTS = new Set(['workspace', 'test']) + async function resolveWorkspaceFileByKeyContext({ + principal, input, }: { + principal: Principal input: ReadWorkspaceFileByKeyInput }): Promise { - const metadata = await getFileMetadataByKey(input.key, 'workspace') + const metadata = await getFileMetadataByKey(input.key) if ( !metadata?.workspaceId || + !KEY_READABLE_CONTEXTS.has(metadata.context) || (input.assertedWorkspaceId !== undefined && input.assertedWorkspaceId !== metadata.workspaceId) ) { throw new OrchestrationError('not_found', 'File not found') } - const canonical = await loadActiveWorkspaceFileContext(metadata.id) + const canonical = await loadActiveWorkspaceFileContext( + metadata.id, + metadata.context === 'test' ? { includeTestFiles: true } : undefined + ) if (!canonical || canonical.workspaceId !== metadata.workspaceId) { throw new OrchestrationError('not_found', 'File not found') } + await assertOwnedFileAccess(principal, canonical) return canonical } diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-content.test.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-content.test.ts index 6d9157cd793..a02bea328c1 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-content.test.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-content.test.ts @@ -77,11 +77,13 @@ describe('readWorkspaceFileContent', () => { expect(mocks.loadContext).toHaveBeenCalledWith('file-1', { includeDeleted: true, includeChatUploads: true, + includeTestFiles: true, }) expect(mocks.getFile).toHaveBeenCalledWith('workspace-1', 'file-1', { includeDeleted: true, throwOnError: true, includeChatUploads: true, + includeTestFiles: true, }) expect(mocks.fetchBuffer).toHaveBeenCalledWith(file, { maxBytes: 512 }) expect(mocks.getSecretProvenance).not.toHaveBeenCalled() diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-content.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-content.ts index e4216a7951f..38909345960 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-content.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-content.ts @@ -43,6 +43,7 @@ async function executeReadWorkspaceFileContent({ includeDeleted: input.includeDeleted, throwOnError: true, includeChatUploads: true, + includeTestFiles: true, }) if (!file) throw new OrchestrationError('not_found', 'File not found') const content = await fetchWorkspaceFileBuffer(file, { @@ -69,7 +70,11 @@ async function executeReadWorkspaceFileContent({ */ export const readWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.readContent, - resolveContext: ({ input }) => - resolveActiveWorkspaceFileContext({ ...input, includeChatUploads: true }), + resolveContext: ({ principal, input }) => + resolveActiveWorkspaceFileContext({ + ...input, + includeChatUploads: true, + ownedFilePrincipal: principal, + }), execute: executeReadWorkspaceFileContent, }) diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-metadata.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-metadata.ts index f4c7bf8cb2f..e36226450f9 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-metadata.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-metadata.ts @@ -1,3 +1,4 @@ +import type { Principal } from '@sim/auth/principal' import type { ShareRecord } from '@/lib/api/contracts/public-shares' import type { AuthorizedWorkspaceUseCaseContext } from '@/lib/core/application' import { OrchestrationError } from '@/lib/core/orchestration/types' @@ -47,6 +48,7 @@ async function executeReadWorkspaceFileMetadata({ getWorkspaceFile(context.workspaceId, context.fileId, { includeDeleted: input.includeDeleted, throwOnError: true, + includeTestFiles: true, }), getShareForResource('file', context.fileId), ]) @@ -56,7 +58,8 @@ async function executeReadWorkspaceFileMetadata({ export const readWorkspaceFileMetadata = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.readMetadata, - resolveContext: ({ input }) => resolveActiveWorkspaceFileContext(input), + resolveContext: ({ principal, input }) => + resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), execute: executeReadWorkspaceFileMetadata, }) @@ -66,12 +69,18 @@ export const readWorkspaceFileMetadata = defineAuthorizedWorkspaceFileUseCase({ */ export const readWorkspaceFileMetadataWithVersion = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.readMetadata, - resolveContext: ({ input }: { input: ReadWorkspaceFileMetadataInput }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: ReadWorkspaceFileMetadataInput + }) => resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ input, context }): Promise { const [file, share] = await Promise.all([ getWorkspaceFileWithCurrentVersion(context.workspaceId, context.fileId, { includeDeleted: input.includeDeleted, + includeTestFiles: true, }), getShareForResource('file', context.fileId), ]) diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-record.test.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-record.test.ts index dae87caa171..d821e89fffd 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-record.test.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-record.test.ts @@ -59,10 +59,12 @@ describe('workspace file record reads', () => { expect(mocks.loadContext).toHaveBeenCalledWith('file-1', { includeDeleted: undefined, includeChatUploads: true, + includeTestFiles: true, }) expect(mocks.getFile).toHaveBeenCalledWith('workspace-1', 'file-1', { throwOnError: true, includeChatUploads: true, + includeTestFiles: true, }) } ) diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-record.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-record.ts index 9ffde1a241c..e7ad76f6a5f 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-record.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-record.ts @@ -1,3 +1,4 @@ +import type { Principal } from '@sim/auth/principal' import type { AuthorizedWorkspaceUseCaseContext, WorkspaceOperation } from '@/lib/core/application' import { OrchestrationError } from '@/lib/core/orchestration/types' import { @@ -26,8 +27,18 @@ export interface ReadWorkspaceFileRecordResult { function createReadWorkspaceFileRecord(operation: O) { return defineAuthorizedWorkspaceFileUseCase({ operation, - resolveContext: ({ input }: { input: ReadWorkspaceFileRecordInput }) => - resolveActiveWorkspaceFileContext({ ...input, includeChatUploads: true }), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: ReadWorkspaceFileRecordInput + }) => + resolveActiveWorkspaceFileContext({ + ...input, + includeChatUploads: true, + ownedFilePrincipal: principal, + }), async execute({ context, }: AuthorizedWorkspaceUseCaseContext< @@ -38,6 +49,7 @@ function createReadWorkspaceFileRecord(opera const file = await getWorkspaceFile(context.workspaceId, context.fileId, { throwOnError: true, includeChatUploads: true, + includeTestFiles: true, }) if (!file) throw new OrchestrationError('not_found', 'File not found') return { file } diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-secret-provenance.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-secret-provenance.ts index 27c8a695108..85ead6dbfdb 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-secret-provenance.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-secret-provenance.ts @@ -1,3 +1,4 @@ +import type { Principal } from '@sim/auth/principal' import { OrchestrationError } from '@/lib/core/orchestration/types' import { getWorkspaceFile } from '@/lib/uploads/contexts/workspace/workspace-file-manager' import { @@ -17,13 +18,21 @@ export interface ReadWorkspaceFileSecretProvenanceInput { export const readWorkspaceFileSecretProvenance = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.readContent, - resolveContext: ({ input }: { input: ReadWorkspaceFileSecretProvenanceInput }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: ReadWorkspaceFileSecretProvenanceInput + }) => resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ input, context }): Promise<{ provenance: WorkspaceFileSecretProvenance ownerUserId: string }> { - const file = await getWorkspaceFile(context.workspaceId, context.fileId, { throwOnError: true }) + const file = await getWorkspaceFile(context.workspaceId, context.fileId, { + throwOnError: true, + includeTestFiles: true, + }) if (!file) throw new OrchestrationError('not_found', 'File not found') return { provenance: await getBoundWorkspaceFileSecretProvenance(context.workspaceId, { diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-text.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-text.ts index 07c66477ab5..5b972e57e5c 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-text.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-text.ts @@ -255,6 +255,9 @@ export async function parseFileText( export const readWorkspaceFileText = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.readContent, resolveContext: ({ principal, input }) => - resolveReferencedWorkspaceFileContext(principal, input, { includeChatUploads: true }), + resolveReferencedWorkspaceFileContext(principal, input, { + includeChatUploads: true, + includeTestFiles: true, + }), execute: executeReadWorkspaceFileText, }) diff --git a/apps/sim/lib/workspace-files/application/resolve-workspace-file-reference.test.ts b/apps/sim/lib/workspace-files/application/resolve-workspace-file-reference.test.ts index 1ab920d6de0..a179181cd2d 100644 --- a/apps/sim/lib/workspace-files/application/resolve-workspace-file-reference.test.ts +++ b/apps/sim/lib/workspace-files/application/resolve-workspace-file-reference.test.ts @@ -58,14 +58,30 @@ describe('workspace file reference application service', () => { expect(mocks.resolveStoredReference).toHaveBeenCalledWith( file.workspaceId, 'uploads/source.txt', - { includeChatUploads: true, chatId: 'current-chat' } + { + includeChatUploads: true, + includeTestFiles: true, + chatId: 'current-chat', + } ) expect(mocks.resolvePermission).toHaveBeenCalled() }) + it('admits a test file but never a chat upload for files.update_content', async () => { + await resolveWorkspaceFileReference({ + principal, + operation: fileOperations.updateContent, + workspaceId: 'workspace-1', + reference: 'uploads/photo.png', + }) + + expect(mocks.resolveStoredReference).toHaveBeenCalledWith('workspace-1', 'uploads/photo.png', { + includeTestFiles: true, + }) + }) + it.each([ fileOperations.rename, - fileOperations.updateContent, fileOperations.move, fileOperations.delete, fileOperations.updateShare, diff --git a/apps/sim/lib/workspace-files/application/resolve-workspace-file-reference.ts b/apps/sim/lib/workspace-files/application/resolve-workspace-file-reference.ts index 06d517477fa..35e55e4cf6e 100644 --- a/apps/sim/lib/workspace-files/application/resolve-workspace-file-reference.ts +++ b/apps/sim/lib/workspace-files/application/resolve-workspace-file-reference.ts @@ -11,6 +11,7 @@ import { } from '@/lib/uploads/contexts/workspace/workspace-file-manager' import { defineAuthorizedWorkspaceFileUseCase } from '@/lib/workspace-files/application/authorized-workspace-file-use-case' import { fileOperations } from '@/lib/workspace-files/application/operations' +import { assertOwnedFileAccess } from '@/lib/workspace-files/application/workspace-file-context' export interface ResolveWorkspaceFileReferenceInput { principal: Principal @@ -42,10 +43,18 @@ export interface ReferencedWorkspaceFileContext extends ActiveWorkspaceFileConte /** * Reads may reach a chat upload through its explicit `uploads/` reference (or its - * own id); every other file operation resolves workspace files only, so no write, move, - * rename, delete, or share can land on one. + * own id) and a test file through `tests/.test.js`; every other file operation resolves workspace files only, so no + * move, rename, delete, or share can land on any of them. */ -const CHAT_UPLOAD_LOOKUP: WorkspaceFileLookupOptions = { includeChatUploads: true } +const READ_CONTENT_LOOKUP: WorkspaceFileLookupOptions = { + includeChatUploads: true, + includeTestFiles: true, +} + +/** Content writes reach test files; no other write can. */ +const CONTENT_WRITE_LOOKUP: WorkspaceFileLookupOptions = { + includeTestFiles: true, +} /** * Resolves a VFS reference to its canonical authorization context, carrying the resolved @@ -60,13 +69,10 @@ export async function resolveReferencedWorkspaceFileContext( (principal.kind === 'delegated' && principal.serviceId === 'copilot' ? principal.resourceScope?.chatId : undefined) ?? input.chatId + const lookup = chatId === undefined ? options : { ...options, chatId } const file = input.folderId === undefined - ? await resolveStoredWorkspaceFileReference( - input.workspaceId, - input.reference, - chatId === undefined ? options : { ...options, chatId } - ) + ? await resolveStoredWorkspaceFileReference(input.workspaceId, input.reference, lookup) : await getWorkspaceFileByName(input.workspaceId, input.reference, { folderId: input.folderId, }) @@ -75,6 +81,7 @@ export async function resolveReferencedWorkspaceFileContext( if (!canonical || canonical.workspaceId !== input.workspaceId) { throw new OrchestrationError('not_found', 'File not found') } + await assertOwnedFileAccess(principal, canonical) return { ...canonical, file } } @@ -106,12 +113,13 @@ type WorkspaceFileReferenceUseCase = OperationUseCase< const workspaceFileReferenceUseCases = { [fileOperations.readContent.id]: defineWorkspaceFileReferenceUseCase( fileOperations.readContent, - CHAT_UPLOAD_LOOKUP + READ_CONTENT_LOOKUP ), [fileOperations.create.id]: defineWorkspaceFileReferenceUseCase(fileOperations.create), [fileOperations.rename.id]: defineWorkspaceFileReferenceUseCase(fileOperations.rename), [fileOperations.updateContent.id]: defineWorkspaceFileReferenceUseCase( - fileOperations.updateContent + fileOperations.updateContent, + CONTENT_WRITE_LOOKUP ), [fileOperations.move.id]: defineWorkspaceFileReferenceUseCase(fileOperations.move), [fileOperations.delete.id]: defineWorkspaceFileReferenceUseCase(fileOperations.delete), diff --git a/apps/sim/lib/workspace-files/application/update-workspace-file-content.ts b/apps/sim/lib/workspace-files/application/update-workspace-file-content.ts index 452ce7e9751..105f364c4ab 100644 --- a/apps/sim/lib/workspace-files/application/update-workspace-file-content.ts +++ b/apps/sim/lib/workspace-files/application/update-workspace-file-content.ts @@ -15,6 +15,7 @@ import { defineAuthorizedWorkspaceFileUseCase } from '@/lib/workspace-files/appl import { parseWorkspaceFileRevision } from '@/lib/workspace-files/application/file-revision' import { resolveWorkspaceFileVersionWrite } from '@/lib/workspace-files/application/file-version-write' import { fileOperations } from '@/lib/workspace-files/application/operations' +import { prepareOwnedFileContentWrite } from '@/lib/workspace-files/application/owned-file-content' import { resolveActiveWorkspaceFileContext } from '@/lib/workspace-files/application/workspace-file-context' import { MAX_WORKSPACE_FILE_CONTENT_BYTES } from '@/lib/workspace-files/orchestration' @@ -64,6 +65,7 @@ async function updateAuthorizedWorkspaceFileContent({ const expectedUpdatedAt = input.expectedRevision ? parseWorkspaceFileRevision(input.expectedRevision, canonical.fileId) : input.expectedUpdatedAt + const commitOwner = await prepareOwnedFileContentWrite(canonical, content) let file: VersionedWorkspaceFileRecord try { file = await updateStoredWorkspaceFileContent( @@ -93,6 +95,7 @@ async function updateAuthorizedWorkspaceFileContent({ throw error } + await commitOwner() logger.info('Updated workspace file content', { workspaceId: canonical.workspaceId, fileId: canonical.fileId, @@ -115,8 +118,8 @@ function projectUpdateWorkspaceFileContentAudit(result: UpdateWorkspaceFileConte const admitUpdateWorkspaceFileContentUseCase = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.updateContent, - resolveContext: ({ input }: { input: { fileId: string } }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ principal, input }: { principal: Principal; input: { fileId: string } }) => + resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute() {}, }) @@ -129,8 +132,13 @@ export async function admitUpdateWorkspaceFileContent( export const updateWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.updateContent, - resolveContext: ({ input }: { input: UpdateWorkspaceFileContentInput }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: UpdateWorkspaceFileContentInput + }) => resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ principal, input, context }): Promise { const content = Buffer.from(input.content, input.encoding === 'base64' ? 'base64' : 'utf-8') if (content.length > MAX_WORKSPACE_FILE_CONTENT_BYTES) { @@ -151,8 +159,13 @@ export const updateWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ export const updateWorkspaceFileContentFromBuffer = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.updateContent, - resolveContext: ({ input }: { input: UpdateWorkspaceFileContentBufferInput }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: UpdateWorkspaceFileContentBufferInput + }) => resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), execute: ({ principal, input, context }) => updateAuthorizedWorkspaceFileContent({ principal, diff --git a/apps/sim/lib/workspace-files/application/workspace-file-context.ts b/apps/sim/lib/workspace-files/application/workspace-file-context.ts index c8eaf651197..e15d9af0f77 100644 --- a/apps/sim/lib/workspace-files/application/workspace-file-context.ts +++ b/apps/sim/lib/workspace-files/application/workspace-file-context.ts @@ -1,3 +1,4 @@ +import type { Principal } from '@sim/auth/principal' import { OrchestrationError } from '@/lib/core/orchestration/types' import { type ActiveWorkspaceFileContext, @@ -5,6 +6,8 @@ import { loadWorkspaceFileLifecycleContext, type WorkspaceFileLifecycleContext, } from '@/lib/uploads/contexts/workspace/workspace-file-manager' +import { requireWorkflowTestsEnabled } from '@/lib/workflow-tests/feature-flag' +import { getLiveWorkflowTestByBodyFileId } from '@/lib/workflow-tests/repository' export interface WorkspaceFileContextInput { fileId: string @@ -15,6 +18,37 @@ export interface WorkspaceFileContextInput { * use cases set this: chat uploads stay out of listings and closed to writes. */ includeChatUploads?: boolean + /** + * Admit a file another resource owns — a test file (`context = 'test'`) — for this principal under its owner's policy. Only content reads and + * writes pass it; every other file operation never reaches an owned file. + */ + ownedFilePrincipal?: Principal +} + +/** Principals the test operations admit; workspace API keys and system callers never reach one. */ +const OWNED_FILE_PRINCIPAL_KINDS = new Set([ + 'session', + 'personal_api_key', + 'oauth_access_token', + 'delegated', +]) + +/** + * A test file follows its test: it needs a live test and admits only the test operations' + * principal kinds. + */ +export async function assertOwnedFileAccess( + principal: Principal, + context: ActiveWorkspaceFileContext +): Promise { + if (context.fileContext !== 'test') return + if (!OWNED_FILE_PRINCIPAL_KINDS.has(principal.kind)) { + throw new OrchestrationError('not_found', 'File not found') + } + if (!(await getLiveWorkflowTestByBodyFileId(context.fileId))) { + throw new OrchestrationError('not_found', 'File not found') + } + await requireWorkflowTestsEnabled(context.workspaceOrganizationId) } export async function resolveActiveWorkspaceFileContext( @@ -23,6 +57,7 @@ export async function resolveActiveWorkspaceFileContext( const canonical = await loadActiveWorkspaceFileContext(input.fileId, { includeDeleted: input.includeDeleted, includeChatUploads: input.includeChatUploads, + ...(input.ownedFilePrincipal ? { includeTestFiles: true } : {}), }) if ( !canonical || @@ -30,6 +65,7 @@ export async function resolveActiveWorkspaceFileContext( ) { throw new OrchestrationError('not_found', 'File not found') } + if (input.ownedFilePrincipal) await assertOwnedFileAccess(input.ownedFilePrincipal, canonical) return canonical } diff --git a/apps/sim/lib/workspace-files/query-scope.ts b/apps/sim/lib/workspace-files/query-scope.ts index 88c533e53f8..29069e82333 100644 --- a/apps/sim/lib/workspace-files/query-scope.ts +++ b/apps/sim/lib/workspace-files/query-scope.ts @@ -9,3 +9,9 @@ export function activeWorkspaceFileConditions(workspaceIds: string[]) { isNull(workspaceFiles.deletedAt), ] } + +/** Rows whose content the editor and Sim write: workspace files and test files. */ +export const contentWritableWorkspaceFileContextCondition = inArray(workspaceFiles.context, [ + 'workspace', + 'test', +]) diff --git a/apps/sim/providers/runtime-context.ts b/apps/sim/providers/runtime-context.ts index aaef60250ac..05c5f67680f 100644 --- a/apps/sim/providers/runtime-context.ts +++ b/apps/sim/providers/runtime-context.ts @@ -17,11 +17,14 @@ import { CHILD_EXECUTION_ID_OUTPUT_KEY, CHILD_TRACE_DISABLED_OUTPUT_KEY, } from '@/executor/constants' +import type { ExecutionTestHooks, MockedToolCall } from '@/executor/execution/types' import type { ExecutionContext } from '@/executor/types' import type { ResolvedSecretTraceRegistry } from '@/executor/utils/resolved-secret-trace-registry' +import { stripCloneSuffixes } from '@/executor/utils/subflow-node-id-codec' import { getPreparedProviderToolInputProvenance } from '@/providers/tool-input-provenance' import type { ProviderId } from '@/providers/types' import { type ExecuteToolOptions, executeTool } from '@/tools' +import { getToolParams } from '@/tools/metadata' import type { ToolResponse } from '@/tools/types' export interface ProviderRuntimeContext { @@ -124,6 +127,35 @@ function accumulateFailedFunctionToolCost( } } +/** The arguments a model sent a tool: no internal context, credentials, or fixed configuration. */ +function modelSuppliedToolInput( + toolId: string, + params: Record +): Record { + const declared = getToolParams(toolId) + const input: Record = {} + for (const [key, value] of Object.entries(params)) { + const visibility = declared?.[key]?.visibility + if (key !== '_context' && visibility !== 'user-only' && visibility !== 'hidden') { + input[key] = value + } + } + return input +} + +/** A test's answer to a tool call; a rejected mock becomes the tool's failure. */ +async function resolveMockedTool( + testHooks: ExecutionTestHooks, + call: MockedToolCall, + abortSignal: AbortSignal | undefined +): Promise { + try { + return { success: true, output: await testHooks.resolveToolMock(call, abortSignal) } + } catch (error) { + return { success: false, output: {}, error: getErrorMessage(error) } + } +} + export async function executeProviderTool( toolId: string, params: Parameters[1], @@ -224,6 +256,30 @@ export async function executeProviderTool( : registry.forkForToolCall() : undefined + const callerContext = options.executionContext ?? runtimeContext?.executionContext + const testHooks = callerContext?.testHooks + const callerBlockId = + isRecordLike(params._context) && typeof params._context.blockId === 'string' + ? stripCloneSuffixes(params._context.blockId) + : undefined + if ( + testHooks && + callerBlockId && + !memoryRetrieval && + testHooks.mocksTool(callerBlockId, executionToolId) + ) { + const response = await resolveMockedTool( + testHooks, + { + blockId: callerBlockId, + toolId: executionToolId, + input: modelSuppliedToolInput(executionToolId, params), + }, + callerContext?.abortSignal + ) + return { rawResponse: response, modelResponse: response } + } + try { const executionContext = options.executionContext ?? runtimeContext?.executionContext const result = memoryRetrieval diff --git a/apps/sim/sandbox-tasks/index.ts b/apps/sim/sandbox-tasks/index.ts index 3fab0599a5a..0ec96809128 100644 --- a/apps/sim/sandbox-tasks/index.ts +++ b/apps/sim/sandbox-tasks/index.ts @@ -12,6 +12,8 @@ * - `pptx-generate` → `apps/sim/sandbox-tasks/pptx-generate.ts` * - `docx-generate` → `apps/sim/sandbox-tasks/docx-generate.ts` * - `pdf-generate` → `apps/sim/sandbox-tasks/pdf-generate.ts` + * - `workflow-test-run`, `workflow-test-collect` → `apps/sim/sandbox-tasks/workflow-test.ts` + * (user code is an ES module importing only `vitest` and `sim:test`) * * Tier 2 — Raw isolated-vm consumers. * Value-producing executions where the user supplies arbitrary JS and the diff --git a/apps/sim/sandbox-tasks/registry.ts b/apps/sim/sandbox-tasks/registry.ts index 3866739764d..3df98e26467 100644 --- a/apps/sim/sandbox-tasks/registry.ts +++ b/apps/sim/sandbox-tasks/registry.ts @@ -2,6 +2,7 @@ import type { SandboxTask } from '@/lib/execution/sandbox/types' import { docxGenerateTask } from '@/sandbox-tasks/docx-generate' import { pdfGenerateTask } from '@/sandbox-tasks/pdf-generate' import { pptxGenerateTask } from '@/sandbox-tasks/pptx-generate' +import { workflowTestCollectTask, workflowTestRunTask } from '@/sandbox-tasks/workflow-test' /** * Every piece of user code that runs inside the isolated-vm sandbox is defined @@ -12,6 +13,8 @@ export const SANDBOX_TASKS = { 'pptx-generate': pptxGenerateTask, 'docx-generate': docxGenerateTask, 'pdf-generate': pdfGenerateTask, + 'workflow-test-run': workflowTestRunTask, + 'workflow-test-collect': workflowTestCollectTask, } as const satisfies Record export type SandboxTaskId = keyof typeof SANDBOX_TASKS diff --git a/apps/sim/sandbox-tasks/workflow-test.ts b/apps/sim/sandbox-tasks/workflow-test.ts new file mode 100644 index 00000000000..24eb81701bb --- /dev/null +++ b/apps/sim/sandbox-tasks/workflow-test.ts @@ -0,0 +1,476 @@ +import { defineSandboxTask } from '@/lib/execution/sandbox/define-task' +import type { SandboxTaskInput } from '@/lib/execution/sandbox/types' +import { workflowTestBrokers } from '@/lib/workflow-tests/brokers' + +/** Wall-clock budget for one test file: every test, workflow run, and judge call in it. */ +const WORKFLOW_TEST_FILE_TIMEOUT_MS = 15 * 60_000 +const DEFAULT_TEST_TIMEOUT_MS = 5 * 60_000 +const MAX_RUBRIC_RUNS = 5 + +/** + * The vitest-shaped harness. User code is a test file evaluated as an ES module; its + * `import ... from 'vitest'` and `'sim:test'` resolve to `WORKFLOW_TEST_MODULES`, which read + * `globalThis.__simTest`. Module evaluation only registers suites and tests; `finalize` + * runs them (or, for collection, just lists them) and returns the report as JSON bytes. + */ +const HARNESS = ` + const vx = globalThis.__bundles['vitest-expect']; + if (!vx) throw new Error('vitest-expect bundle not loaded'); + const brokers = globalThis.__brokers; + const call = (name, args) => brokers[name](args); + + const DEFAULT_TEST_TIMEOUT_MS = ${DEFAULT_TEST_TIMEOUT_MS}; + const MAX_RUBRIC_RUNS = ${MAX_RUBRIC_RUNS}; + + const newSuite = (name, parent, mode) => ({ + name, parent, mode, children: [], + hooks: { beforeAll: [], afterAll: [], beforeEach: [], afterEach: [] }, + }); + const root = newSuite('', null, 'run'); + let currentSuite = root; + let collecting = true; + let currentTest = null; + let hasOnly = false; + + const requireCollecting = (what) => { + if (!collecting) throw new Error(what + ' must be called at the top level of the file or inside describe()'); + }; + const requireRunning = (what) => { + if (collecting) throw new Error(what + ' can only be called inside a test or a hook'); + }; + const requireName = (what, name) => { + if (typeof name !== 'string' || name.trim() === '') throw new Error(what + ' needs a name'); + }; + + function defineSuite(mode) { + return function describe(name, fn) { + requireCollecting('describe()'); + requireName('describe()', name); + if (typeof fn !== 'function') throw new Error('describe("' + name + '") needs a callback'); + if (mode === 'only') hasOnly = true; + const suite = newSuite(name, currentSuite, mode); + currentSuite.children.push({ kind: 'suite', suite }); + const previous = currentSuite; + currentSuite = suite; + try { + const result = fn(); + if (result && typeof result.then === 'function') { + throw new Error('describe("' + name + '") callback must not be async; put awaits inside tests or hooks'); + } + } finally { + currentSuite = previous; + } + }; + } + function defineTest(mode) { + return function it(name, fn, timeoutMs) { + requireCollecting('it()'); + requireName('it()', name); + if (mode !== 'skip' && typeof fn !== 'function') throw new Error('it("' + name + '") needs a callback'); + if (timeoutMs !== undefined && !(Number.isInteger(timeoutMs) && timeoutMs > 0)) { + throw new Error('it("' + name + '") timeout must be a positive whole number of milliseconds'); + } + if (mode === 'only') hasOnly = true; + const site = callSite(new Error().stack); + if (!site) throw new Error('it("' + name + '") could not be located in the file'); + const span = typeof fn === 'function' ? String(fn).split('\\n').length - 1 : 0; + currentSuite.children.push({ kind: 'test', test: { name, fn, mode, timeoutMs: timeoutMs ?? DEFAULT_TEST_TIMEOUT_MS, line: site.line, endLine: site.line + span } }); + }; + } + const describe = defineSuite('run'); + describe.only = defineSuite('only'); + describe.skip = defineSuite('skip'); + const it = defineTest('run'); + it.only = defineTest('only'); + it.skip = defineTest('skip'); + const hook = (kind) => (fn) => { + requireCollecting(kind + '()'); + if (typeof fn !== 'function') throw new Error(kind + '() needs a callback'); + currentSuite.hooks[kind].push(fn); + }; + + function callSite(stack) { + for (const line of String(stack || '').split('\\n').slice(1)) { + const match = /\\(?([^\\s()]+):(\\d+):(\\d+)\\)?\\s*$/.exec(line.trim()); + if (match && !match[1].startsWith('sandbox/')) return { line: Number(match[2]), column: Number(match[3]) }; + } + return null; + } + function describeError(error) { + const info = { message: error && error.message ? String(error.message) : String(error) }; + if (error && error.showDiff !== false && error && 'expected' in error && 'actual' in error) { + for (const key of ['actual', 'expected']) { + try { + JSON.stringify(error[key]); + info[key] = error[key]; + } catch {} + } + } + return info; + } + + const baseExpect = vx.createExpect(); + function settle(check, status, error) { + if (check.status !== 'pending') return; + check.status = status; + if (error) Object.assign(check, describeError(error)); + } + function track(assertion, check) { + return new Proxy(assertion, { + get(target, key) { + let value; + try { + value = Reflect.get(target, key, target); + } catch (error) { + settle(check, 'fail', error); + throw error; + } + if (typeof value === 'function') { + return (...args) => { + let result; + try { + result = value.apply(target, args); + } catch (error) { + settle(check, 'fail', error); + throw error; + } + if (result && typeof result.then === 'function') { + return result.then( + (resolved) => { settle(check, 'pass'); return resolved; }, + (error) => { settle(check, 'fail', error); throw error; } + ); + } + settle(check, 'pass'); + return result && typeof result === 'object' && result.__flags ? track(result, check) : result; + }; + } + if (value && typeof value === 'object' && value.__flags) return track(value, check); + return value; + }, + }); + } + const expect = (value, message) => { + const assertion = baseExpect(value, message); + if (!currentTest) return assertion; + const check = { status: 'pending', ...(callSite(new Error().stack) || {}) }; + currentTest.checks.push(check); + currentTest.activeCheck = check; + return track(assertion, check); + }; + Object.setPrototypeOf(expect, baseExpect); + + baseExpect.extend({ + async toMatchRubric(received, rubric, options) { + if (typeof rubric !== 'string' || rubric.trim() === '') throw new Error('toMatchRubric() needs a rubric string'); + const runs = options && options.runs !== undefined ? options.runs : 1; + if (!Number.isInteger(runs) || runs < 1 || runs > MAX_RUBRIC_RUNS) { + throw new Error('toMatchRubric() runs must be a whole number from 1 to ' + MAX_RUBRIC_RUNS); + } + const value = typeof received === 'string' ? received : JSON.stringify(received, null, 2); + if (value === undefined) throw new Error('toMatchRubric() received undefined'); + const check = currentTest && currentTest.activeCheck; + let verdict = { pass: true, reason: '' }; + for (let i = 0; i < runs && verdict.pass; i++) { + verdict = await call('testJudge', { value, rubric }); + } + if (check) check.judge = verdict.reason; + return { + pass: verdict.pass, + message: () => verdict.pass ? 'Expected the output not to satisfy: ' + rubric + '. ' + verdict.reason : verdict.reason, + }; + }, + }); + + const vi = { fn: vx.fn, spyOn: vx.spyOn, isMockFunction: vx.isMockFunction }; + + const registrations = new Map(); + const registrationKey = (kind, workflow, block, tool) => kind + '\\u0000' + (workflow ?? '') + '\\u0000' + (block ?? '') + '\\u0000' + (tool ?? ''); + const describeRegistration = (r) => r.kind === 'tool' + ? 'mockTool("' + (r.block ? r.block + '", "' : '') + r.tool + '")' + : (r.kind === 'mock' ? 'mockBlock' : 'spyOnBlock') + '("' + (r.workflow ? r.workflow + '", "' : '') + r.block + '")'; + function addRegistration(entry) { + const key = registrationKey(entry.kind, entry.workflow, entry.block, entry.tool); + const existing = registrations.get(key); + if (existing) return existing.fn; + const fn = vx.fn().mockName(entry.label); + const registration = { ...entry, key, fn, test: currentTest, sample: undefined }; + if (entry.kind !== 'spy') { + fn.mockSampleOutput = (overrides) => { + if (overrides !== undefined && (overrides === null || typeof overrides !== 'object' || Array.isArray(overrides))) { + throw new Error(describeRegistration(registration) + '.mockSampleOutput() takes an object of fields to override'); + } + registration.sample = overrides ?? {}; + return fn; + }; + } + registrations.set(key, registration); + return fn; + } + function register(kind, first, second) { + const workflow = second === undefined ? null : first; + const block = second === undefined ? first : second; + const what = kind === 'mock' ? 'mockBlock()' : 'spyOnBlock()'; + requireName(what, block); + if (workflow !== null) requireName(what + ' workflow', workflow); + return addRegistration({ kind, workflow, block, tool: null, label: workflow ? workflow + ' / ' + block : block }); + } + const mockBlock = (first, second) => register('mock', first, second); + const spyOnBlock = (first, second) => register('spy', first, second); + const mockTool = (first, second) => { + const block = second === undefined ? null : first; + const tool = second === undefined ? first : second; + requireName('mockTool()', tool); + if (block !== null) requireName('mockTool() Agent block', block); + return addRegistration({ kind: 'tool', workflow: null, block, tool, label: block ? block + ' / ' + tool : tool }); + }; + + /** Overrides on a generated sample: objects merge, anything else replaces, and an unknown field is a mistake. */ + function mergeSample(sample, overrides, path) { + const merged = { ...sample }; + for (const [field, value] of Object.entries(overrides)) { + const at = path ? path + '.' + field : field; + if (!(field in sample)) { + throw new Error('mockSampleOutput(): the output has no field "' + at + '". It has: ' + (Object.keys(sample).join(', ') || 'no fields')); + } + const base = sample[field]; + merged[field] = base && typeof base === 'object' && !Array.isArray(base) && value && typeof value === 'object' && !Array.isArray(value) + ? mergeSample(base, value, at) + : value; + } + return merged; + } + + class WorkflowRunError extends Error { + constructor(message, executionId, output) { + super(message); + this.name = 'WorkflowRunError'; + this.executionId = executionId; + this.output = output; + } + } + + async function answerMock(event) { + const entry = registrations.get(event.key); + if (!entry) return { error: 'No mock is registered for ' + (event.tool ?? event.block) }; + try { + if (entry.sample !== undefined) { + let output; + try { + output = mergeSample(event.sample, entry.sample, ''); + } catch (error) { + return { error: String(error.message), mistake: describeRegistration(entry) + ': ' + String(error.message) }; + } + await entry.fn(event.input); + return { output }; + } + const output = await entry.fn(event.input); + if (output === undefined) { + return { error: describeRegistration(entry) + ' returned nothing. Give it a value with .mockResolvedValue(...) or .mockSampleOutput()' }; + } + return { output }; + } catch (error) { + return { error: error && error.message ? String(error.message) : String(error) }; + } + } + + async function runWorkflow(workflow, input, options) { + requireRunning('runWorkflow()'); + requireName('runWorkflow()', workflow); + if (options !== undefined) { + const extra = options === null || typeof options !== 'object' ? ['options'] : Object.keys(options).filter((key) => key !== 'trigger'); + if (extra.length > 0) throw new Error('runWorkflow() options take only { trigger: "Trigger block name" }; pick draft or deployed when you run the tests'); + requireName('runWorkflow() trigger', options.trigger); + } + const trigger = options ? options.trigger : null; + const targets = [...registrations.values()].map((r) => r.kind === 'tool' + ? { key: r.key, kind: 'tool', workflow: null, block: r.block, tool: r.tool } + : { key: r.key, kind: r.kind, workflow: r.workflow, block: r.block }); + const test = currentTest; + const mistakes = []; + let event = await call('testStart', { workflow, trigger, input: input === undefined ? null : input, targets }); + test.runIds.push(event.runId); + while (event.kind !== 'done') { + if (event.kind === 'mock') { + const { mistake, ...reply } = await answerMock(event); + if (mistake) mistakes.push(mistake); + event = await call('testReply', { runId: event.runId, callId: event.callId, ...reply }); + } else { + event = await call('testNext', { runId: event.runId }); + } + } + test.executions.push({ workflow, executionId: event.executionId }); + for (const key of event.matchedKeys) test.matchedKeys.add(key); + for (const spyCall of event.spyCalls) { + const entry = registrations.get(spyCall.key); + if (!entry) continue; + entry.fn.mockImplementationOnce(() => spyCall.output); + entry.fn(spyCall.input); + } + if (mistakes.length > 0) throw new Error(mistakes.join('\\n')); + if (event.error) throw new WorkflowRunError(event.error, event.executionId, event.output); + return { output: event.output, executionId: event.executionId }; + } + + const logLines = []; + for (const level of ['log', 'info', 'warn', 'error']) { + const original = console[level]; + console[level] = (...args) => { + if (currentTest) { + currentTest.logs.push(args.map((a) => typeof a === 'string' ? a : (() => { try { return JSON.stringify(a); } catch { return String(a); } })()).join(' ')); + } + return original.apply(console, args); + }; + } + + function withTimeout(promise, ms, label) { + let timer; + return Promise.race([ + promise, + new Promise((_, reject) => { timer = setTimeout(() => reject(new Error(label + ' timed out after ' + ms + 'ms')), ms); }), + ]).finally(() => clearTimeout(timer)); + } + + const suitePath = (suite) => { + const names = []; + for (let s = suite; s && s.parent; s = s.parent) names.unshift(s.name); + return names; + }; + const suiteChain = (suite) => { + const chain = []; + for (let s = suite; s; s = s.parent) chain.unshift(s); + return chain; + }; + function listTests(suite, out) { + for (const child of suite.children) { + if (child.kind === 'suite') listTests(child.suite, out); + else out.push({ suite, test: child.test, path: [...suitePath(suite), child.test.name] }); + } + return out; + } + + async function runHooks(fns) { + for (const fn of fns) await fn(); + } + + function willRun(suite, test, onlyNames) { + const chain = suiteChain(suite); + if (test.mode === 'skip' || chain.some((s) => s.mode === 'skip')) return false; + if (hasOnly && test.mode !== 'only' && !chain.some((s) => s.mode === 'only')) return false; + return !onlyNames || onlyNames.includes([...suitePath(suite), test.name].join(' > ')); + } + + async function runOne(suite, test, onlyNames) { + const path = [...suitePath(suite), test.name]; + const result = { path, status: 'pass', durationMs: 0, checks: [], executions: [], logs: [], runIds: [], matchedKeys: new Set(), activeCheck: null }; + if (!willRun(suite, test, onlyNames)) { + result.status = 'skip'; + return result; + } + await call('testProgress', { path, status: 'running' }); + const startedAt = Date.now(); + currentTest = result; + try { + await withTimeout((async () => { + for (const s of suiteChain(suite)) await runHooks(s.hooks.beforeEach); + try { + await test.fn(); + } finally { + for (const s of suiteChain(suite).reverse()) await runHooks(s.hooks.afterEach); + } + const unmatched = [...registrations.values()].filter((r) => r.test === result && result.runIds.length > 0 && !result.matchedKeys.has(r.key)); + if (unmatched.length > 0) { + const names = unmatched.map(describeRegistration); + throw new Error(names.join(', ') + ' matched no block in the workflows this test ran'); + } + })(), test.timeoutMs, 'Test'); + } catch (error) { + result.status = 'fail'; + const owned = result.checks.find((check) => check.status === 'fail'); + if (!owned) result.error = { ...describeError(error), ...(callSite(error && error.stack) || {}) }; + } finally { + for (const check of result.checks) if (check.status === 'pending') check.status = 'skip'; + for (const [key, registration] of registrations) if (registration.test === result) registrations.delete(key); + currentTest = null; + if (result.runIds.length) await call('testCancel', { runIds: result.runIds }); + result.durationMs = Date.now() - startedAt; + } + await call('testProgress', { path, status: result.status }); + return result; + } + + async function runSuiteTree(suite, onlyNames, out) { + const anyRuns = listTests(suite, []).some((entry) => willRun(entry.suite, entry.test, onlyNames)); + if (anyRuns) await runHooks(suite.hooks.beforeAll); + try { + for (const child of suite.children) { + if (child.kind === 'suite') await runSuiteTree(child.suite, onlyNames, out); + else out.push(await runOne(suite, child.test, onlyNames)); + } + } finally { + if (anyRuns) await runHooks(suite.hooks.afterAll); + } + } + + globalThis.__simTest = { + modules: { + vitest: { describe, it, test: it, expect, vi, beforeAll: hook('beforeAll'), afterAll: hook('afterAll'), beforeEach: hook('beforeEach'), afterEach: hook('afterEach') }, + sim: { runWorkflow, mockBlock, spyOnBlock, mockTool }, + }, + collect() { + collecting = false; + return { + topLevel: root.children.map((c) => c.kind === 'suite' ? { kind: 'suite', name: c.suite.name } : { kind: 'test', name: c.test.name }), + tests: listTests(root, []).map((e) => ({ path: e.path, mode: e.test.mode, line: e.test.line, endLine: e.test.endLine })), + }; + }, + async run() { + const plan = this.collect(); + const { only } = await call('testPlan', {}); + const results = []; + await runSuiteTree(root, only, results); + return { + topLevel: plan.topLevel, + tests: results.map(({ path, status, durationMs, checks, executions, logs, error }) => ({ path, status, durationMs, checks, executions, logs, ...(error ? { error } : {}) })), + }; + }, + }; +` + +const VITEST_MODULE = ` +const m = globalThis.__simTest.modules.vitest; +export const { describe, it, test, expect, vi, beforeAll, afterAll, beforeEach, afterEach } = m; +` + +const SIM_TEST_MODULE = ` +const m = globalThis.__simTest.modules.sim; +export const { runWorkflow, mockBlock, spyOnBlock, mockTool } = m; +` + +const WORKFLOW_TEST_MODULES = { vitest: VITEST_MODULE, 'sim:test': SIM_TEST_MODULE } + +const encodeReport = (expression: string) => + `return new TextEncoder().encode(JSON.stringify(${expression}));` + +/** Runs every test in a file (or only `testPlan().only`) and returns the report. */ +export const workflowTestRunTask = defineSandboxTask({ + id: 'workflow-test-run', + timeoutMs: WORKFLOW_TEST_FILE_TIMEOUT_MS, + bundles: ['vitest-expect'], + brokers: workflowTestBrokers, + bootstrap: HARNESS, + userModules: WORKFLOW_TEST_MODULES, + finalize: encodeReport('await globalThis.__simTest.run()'), + toResult: (bytes) => Buffer.from(bytes), +}) + +/** Evaluates a test file without running anything and lists its suites and tests. */ +export const workflowTestCollectTask = defineSandboxTask({ + id: 'workflow-test-collect', + timeoutMs: 10_000, + bundles: ['vitest-expect'], + brokers: [], + bootstrap: HARNESS, + userModules: WORKFLOW_TEST_MODULES, + finalize: encodeReport('globalThis.__simTest.collect()'), + toResult: (bytes) => Buffer.from(bytes), +}) diff --git a/apps/sim/stores/logs/filters/types.ts b/apps/sim/stores/logs/filters/types.ts index c2f8c874968..a5414fe6036 100644 --- a/apps/sim/stores/logs/filters/types.ts +++ b/apps/sim/stores/logs/filters/types.ts @@ -32,6 +32,7 @@ export const CORE_TRIGGER_TYPES = [ 'mothership', 'workflow', 'custom_block', + 'test', ] as const export type CoreTriggerType = (typeof CORE_TRIGGER_TYPES)[number] diff --git a/packages/db/migrations/0400_workflow_tests.sql b/packages/db/migrations/0400_workflow_tests.sql new file mode 100644 index 00000000000..b288571efe9 --- /dev/null +++ b/packages/db/migrations/0400_workflow_tests.sql @@ -0,0 +1,48 @@ +ALTER TYPE "public"."usage_log_source" ADD VALUE 'workflow-test';--> statement-breakpoint +CREATE TABLE "workflow_test" ( + "id" text PRIMARY KEY NOT NULL, + "workspace_id" text NOT NULL, + "name" text NOT NULL, + "title" text NOT NULL, + "description" text, + "body_file_id" text NOT NULL, + "cases" jsonb DEFAULT '[]' NOT NULL, + "source_hash" text NOT NULL, + "created_by_user_id" text, + "deleted_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "workflow_test_run" ( + "id" text PRIMARY KEY NOT NULL, + "test_id" text NOT NULL, + "workspace_id" text NOT NULL, + "version" text NOT NULL, + "status" text DEFAULT 'running' NOT NULL, + "passed" integer DEFAULT 0 NOT NULL, + "failed" integer DEFAULT 0 NOT NULL, + "skipped" integer DEFAULT 0 NOT NULL, + "report" jsonb, + "error" text, + "source_hash" text, + "ran_against" jsonb, + "progress" jsonb, + "triggered_by_actor" jsonb NOT NULL, + "triggered_by_user_id" text, + "started_at" timestamp DEFAULT now() NOT NULL, + "completed_at" timestamp, + CONSTRAINT "workflow_test_run_version_check" CHECK ("workflow_test_run"."version" IN ('draft', 'deployed')), + CONSTRAINT "workflow_test_run_status_check" CHECK ("workflow_test_run"."status" IN ('running', 'passed', 'failed', 'error')), + CONSTRAINT "workflow_test_run_completed_check" CHECK (("workflow_test_run"."status" = 'running') = ("workflow_test_run"."completed_at" IS NULL)) +); +--> statement-breakpoint +ALTER TABLE "workflow_test" ADD CONSTRAINT "workflow_test_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_test" ADD CONSTRAINT "workflow_test_body_file_id_workspace_files_id_fk" FOREIGN KEY ("body_file_id") REFERENCES "public"."workspace_files"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_test" ADD CONSTRAINT "workflow_test_created_by_user_id_user_id_fk" FOREIGN KEY ("created_by_user_id") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_test_run" ADD CONSTRAINT "workflow_test_run_test_id_workflow_test_id_fk" FOREIGN KEY ("test_id") REFERENCES "public"."workflow_test"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_test_run" ADD CONSTRAINT "workflow_test_run_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_test_run" ADD CONSTRAINT "workflow_test_run_triggered_by_user_id_user_id_fk" FOREIGN KEY ("triggered_by_user_id") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +CREATE UNIQUE INDEX "workflow_test_workspace_name_unique" ON "workflow_test" USING btree ("workspace_id","name") WHERE "workflow_test"."deleted_at" IS NULL;--> statement-breakpoint +CREATE UNIQUE INDEX "workflow_test_body_file_unique" ON "workflow_test" USING btree ("body_file_id");--> statement-breakpoint +CREATE INDEX "workflow_test_run_test_version_started_idx" ON "workflow_test_run" USING btree ("test_id","version","started_at"); \ No newline at end of file diff --git a/packages/db/migrations/meta/0400_snapshot.json b/packages/db/migrations/meta/0400_snapshot.json new file mode 100644 index 00000000000..845adea86da --- /dev/null +++ b/packages/db/migrations/meta/0400_snapshot.json @@ -0,0 +1,30555 @@ +{ + "id": "06710a63-14de-4a91-9966-03742e01c951", + "prevId": "40b244b6-62d3-4a72-9c2c-b04b18f00268", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.academy_certificate": { + "name": "academy_certificate", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "course_id": { + "name": "course_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "academy_cert_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "issued_at": { + "name": "issued_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "certificate_number": { + "name": "certificate_number", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "academy_certificate_user_id_idx": { + "name": "academy_certificate_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "academy_certificate_course_id_idx": { + "name": "academy_certificate_course_id_idx", + "columns": [ + { + "expression": "course_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "academy_certificate_user_course_unique": { + "name": "academy_certificate_user_course_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "course_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "academy_certificate_status_idx": { + "name": "academy_certificate_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "academy_certificate_user_id_user_id_fk": { + "name": "academy_certificate_user_id_user_id_fk", + "tableFrom": "academy_certificate", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "academy_certificate_certificate_number_unique": { + "name": "academy_certificate_certificate_number_unique", + "nullsNotDistinct": false, + "columns": ["certificate_number"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "oauth_config": { + "name": "oauth_config", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "account_user_id_idx": { + "name": "account_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_account_on_account_id_provider_id": { + "name": "idx_account_on_account_id_provider_id", + "columns": [ + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_memory_turn": { + "name": "agent_memory_turn", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "memory_id": { + "name": "memory_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "block_id": { + "name": "block_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "node_id": { + "name": "node_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_order": { + "name": "execution_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "encrypted_state": { + "name": "encrypted_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_memory_turn_invocation_unique": { + "name": "agent_memory_turn_invocation_unique", + "columns": [ + { + "expression": "memory_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "node_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_memory_turn_workflow_idx": { + "name": "agent_memory_turn_workflow_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_memory_turn_memory_id_memory_id_fk": { + "name": "agent_memory_turn_memory_id_memory_id_fk", + "tableFrom": "agent_memory_turn", + "tableTo": "memory", + "columnsFrom": ["memory_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "agent_memory_turn_workflow_id_workflow_id_fk": { + "name": "agent_memory_turn_workflow_id_workflow_id_fk", + "tableFrom": "agent_memory_turn", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.api_key": { + "name": "api_key", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key_hash": { + "name": "key_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'personal'" + }, + "last_used": { + "name": "last_used", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "api_key_workspace_type_idx": { + "name": "api_key_workspace_type_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "api_key_user_type_idx": { + "name": "api_key_user_type_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "api_key_key_hash_idx": { + "name": "api_key_key_hash_idx", + "columns": [ + { + "expression": "key_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "api_key_user_id_user_id_fk": { + "name": "api_key_user_id_user_id_fk", + "tableFrom": "api_key", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "api_key_workspace_id_workspace_id_fk": { + "name": "api_key_workspace_id_workspace_id_fk", + "tableFrom": "api_key", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "api_key_created_by_user_id_fk": { + "name": "api_key_created_by_user_id_fk", + "tableFrom": "api_key", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_key_key_unique": { + "name": "api_key_key_unique", + "nullsNotDistinct": false, + "columns": ["key"] + } + }, + "policies": {}, + "checkConstraints": { + "workspace_type_check": { + "name": "workspace_type_check", + "value": "(type = 'workspace' AND workspace_id IS NOT NULL) OR (type = 'personal' AND workspace_id IS NULL)" + } + }, + "isRLSEnabled": false + }, + "public.async_jobs": { + "name": "async_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "run_at": { + "name": "run_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "max_attempts": { + "name": "max_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 3 + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "output": { + "name": "output", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "async_jobs_status_started_at_idx": { + "name": "async_jobs_status_started_at_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "async_jobs_status_completed_at_idx": { + "name": "async_jobs_status_completed_at_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "completed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "async_jobs_schedule_pending_run_at_idx": { + "name": "async_jobs_schedule_pending_run_at_idx", + "columns": [ + { + "expression": "run_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"async_jobs\".\"type\" = 'schedule-execution' AND \"async_jobs\".\"status\" = 'pending'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "async_jobs_schedule_processing_started_at_idx": { + "name": "async_jobs_schedule_processing_started_at_idx", + "columns": [ + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"async_jobs\".\"type\" = 'schedule-execution' AND \"async_jobs\".\"status\" = 'processing'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "async_jobs_schedule_unreconciled_terminal_idx": { + "name": "async_jobs_schedule_unreconciled_terminal_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"async_jobs\".\"type\" = 'schedule-execution' AND \"async_jobs\".\"status\" IN ('completed', 'failed', 'cancelled') AND COALESCE(\"async_jobs\".\"metadata\" ->> 'scheduleReconciled', 'false') <> 'true'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.audit_log": { + "name": "audit_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_name": { + "name": "actor_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_email": { + "name": "actor_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resource_name": { + "name": "resource_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "surface": { + "name": "surface", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "audit_log_workspace_created_idx": { + "name": "audit_log_workspace_created_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_log_workspace_created_at_id_idx": { + "name": "audit_log_workspace_created_at_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date_trunc('milliseconds', \"created_at\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_log_actor_created_idx": { + "name": "audit_log_actor_created_idx", + "columns": [ + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_log_resource_idx": { + "name": "audit_log_resource_idx", + "columns": [ + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_log_action_idx": { + "name": "audit_log_action_idx", + "columns": [ + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "audit_log_workspace_id_workspace_id_fk": { + "name": "audit_log_workspace_id_workspace_id_fk", + "tableFrom": "audit_log", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "audit_log_actor_id_user_id_fk": { + "name": "audit_log_actor_id_user_id_fk", + "tableFrom": "audit_log", + "tableTo": "user", + "columnsFrom": ["actor_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.background_work_status": { + "name": "background_work_status", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "kind": { + "name": "kind", + "type": "background_work_kind", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "background_work_status_value", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "background_work_status_workspace_status_idx": { + "name": "background_work_status_workspace_status_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "background_work_status_workflow_status_idx": { + "name": "background_work_status_workflow_status_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "background_work_status_meta_child_ws_idx": { + "name": "background_work_status_meta_child_ws_idx", + "columns": [ + { + "expression": "(\"metadata\" ->> 'childWorkspaceId')", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "background_work_status_meta_other_ws_idx": { + "name": "background_work_status_meta_other_ws_idx", + "columns": [ + { + "expression": "(\"metadata\" ->> 'otherWorkspaceId')", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "background_work_status_workspace_id_workspace_id_fk": { + "name": "background_work_status_workspace_id_workspace_id_fk", + "tableFrom": "background_work_status", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "background_work_status_workflow_id_workflow_id_fk": { + "name": "background_work_status_workflow_id_workflow_id_fk", + "tableFrom": "background_work_status", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat": { + "name": "chat", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "customizations": { + "name": "customizations", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'public'" + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allowed_emails": { + "name": "allowed_emails", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'[]'" + }, + "output_configs": { + "name": "output_configs", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'[]'" + }, + "include_thinking": { + "name": "include_thinking", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "include_tool_calls": { + "name": "include_tool_calls", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "identifier_idx": { + "name": "identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"chat\".\"archived_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_archived_at_partial_idx": { + "name": "chat_archived_at_partial_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"chat\".\"archived_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_chat_on_workflow_id_archived_at": { + "name": "idx_chat_on_workflow_id_archived_at", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_workflow_id_workflow_id_fk": { + "name": "chat_workflow_id_workflow_id_fk", + "tableFrom": "chat", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_user_id_user_id_fk": { + "name": "chat_user_id_user_id_fk", + "tableFrom": "chat", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_async_tool_calls": { + "name": "copilot_async_tool_calls", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "checkpoint_id": { + "name": "checkpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "tool_call_id": { + "name": "tool_call_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tool_name": { + "name": "tool_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "args": { + "name": "args", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "status": { + "name": "status", + "type": "copilot_async_tool_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "permission_decision": { + "name": "permission_decision", + "type": "copilot_tool_permission_decision", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "permission_decided_at": { + "name": "permission_decided_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "permission_requested_at": { + "name": "permission_requested_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "pickup_deadline_at": { + "name": "pickup_deadline_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "persist_seq": { + "name": "persist_seq", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": "nextval('copilot_async_tool_calls_persist_seq')" + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "claimed_by": { + "name": "claimed_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "browser_download_started_at": { + "name": "browser_download_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "execution_started_at": { + "name": "execution_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "execution_settled_at": { + "name": "execution_settled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "execution_owner_token": { + "name": "execution_owner_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "execution_lease_expires_at": { + "name": "execution_lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "execution_revoked_at": { + "name": "execution_revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "client_workflow_execution_id": { + "name": "client_workflow_execution_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sandbox_processes": { + "name": "sandbox_processes", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "copilot_async_tool_calls_run_id_idx": { + "name": "copilot_async_tool_calls_run_id_idx", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_async_tool_calls_checkpoint_id_idx": { + "name": "copilot_async_tool_calls_checkpoint_id_idx", + "columns": [ + { + "expression": "checkpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_async_tool_calls_status_idx": { + "name": "copilot_async_tool_calls_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_async_tool_calls_run_status_idx": { + "name": "copilot_async_tool_calls_run_status_idx", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_async_tool_calls_tool_call_id_unique": { + "name": "copilot_async_tool_calls_tool_call_id_unique", + "columns": [ + { + "expression": "tool_call_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_async_tool_calls_run_id_copilot_runs_id_fk": { + "name": "copilot_async_tool_calls_run_id_copilot_runs_id_fk", + "tableFrom": "copilot_async_tool_calls", + "tableTo": "copilot_runs", + "columnsFrom": ["run_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_async_tool_calls_checkpoint_id_copilot_run_checkpoints_id_fk": { + "name": "copilot_async_tool_calls_checkpoint_id_copilot_run_checkpoints_id_fk", + "tableFrom": "copilot_async_tool_calls", + "tableTo": "copilot_run_checkpoints", + "columnsFrom": ["checkpoint_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_chats": { + "name": "copilot_chats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "chat_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'copilot'" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'claude-3-7-sonnet-latest'" + }, + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_conversation_key": { + "name": "external_conversation_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_conversation_metadata": { + "name": "external_conversation_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "preview_yaml": { + "name": "preview_yaml", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "plan_artifact": { + "name": "plan_artifact", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "auto_allowed_tools": { + "name": "auto_allowed_tools", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "pinned": { + "name": "pinned", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "copilot_chats_organization_id_idx": { + "name": "copilot_chats_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_external_conversation_unique": { + "name": "copilot_chats_external_conversation_unique", + "columns": [ + { + "expression": "external_conversation_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"copilot_chats\".\"external_conversation_key\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_user_org_created_idx": { + "name": "copilot_chats_user_org_created_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_user_id_idx": { + "name": "copilot_chats_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_workflow_id_idx": { + "name": "copilot_chats_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_user_workflow_idx": { + "name": "copilot_chats_user_workflow_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_user_workspace_idx": { + "name": "copilot_chats_user_workspace_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_created_at_idx": { + "name": "copilot_chats_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_updated_at_idx": { + "name": "copilot_chats_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_workspace_created_at_id_idx": { + "name": "copilot_chats_workspace_created_at_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date_trunc('milliseconds', \"created_at\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_user_workspace_deleted_partial_idx": { + "name": "copilot_chats_user_workspace_deleted_partial_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"copilot_chats\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_chats_user_id_user_id_fk": { + "name": "copilot_chats_user_id_user_id_fk", + "tableFrom": "copilot_chats", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_chats_workflow_id_workflow_id_fk": { + "name": "copilot_chats_workflow_id_workflow_id_fk", + "tableFrom": "copilot_chats", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_chats_workspace_id_workspace_id_fk": { + "name": "copilot_chats_workspace_id_workspace_id_fk", + "tableFrom": "copilot_chats", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_chats_organization_id_organization_id_fk": { + "name": "copilot_chats_organization_id_organization_id_fk", + "tableFrom": "copilot_chats", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "copilot_chats_owner_check": { + "name": "copilot_chats_owner_check", + "value": "num_nonnulls(\"copilot_chats\".\"workspace_id\", \"copilot_chats\".\"organization_id\") <= 1" + }, + "copilot_chats_organization_workflow_check": { + "name": "copilot_chats_organization_workflow_check", + "value": "\"copilot_chats\".\"organization_id\" IS NULL OR \"copilot_chats\".\"workflow_id\" IS NULL" + } + }, + "isRLSEnabled": false + }, + "public.copilot_feedback": { + "name": "copilot_feedback", + "schema": "", + "columns": { + "feedback_id": { + "name": "feedback_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_query": { + "name": "user_query", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "agent_response": { + "name": "agent_response", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_positive": { + "name": "is_positive", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workflow_yaml": { + "name": "workflow_yaml", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "copilot_feedback_user_id_idx": { + "name": "copilot_feedback_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_feedback_chat_id_idx": { + "name": "copilot_feedback_chat_id_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_feedback_user_chat_idx": { + "name": "copilot_feedback_user_chat_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_feedback_is_positive_idx": { + "name": "copilot_feedback_is_positive_idx", + "columns": [ + { + "expression": "is_positive", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_feedback_created_at_idx": { + "name": "copilot_feedback_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_feedback_user_id_user_id_fk": { + "name": "copilot_feedback_user_id_user_id_fk", + "tableFrom": "copilot_feedback", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_feedback_chat_id_copilot_chats_id_fk": { + "name": "copilot_feedback_chat_id_copilot_chats_id_fk", + "tableFrom": "copilot_feedback", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_messages": { + "name": "copilot_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "stream_id": { + "name": "stream_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "parent_message_id": { + "name": "parent_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tokens_in": { + "name": "tokens_in", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "tokens_out": { + "name": "tokens_out", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "seq": { + "name": "seq", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "copilot_messages_chat_message_unique": { + "name": "copilot_messages_chat_message_unique", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_messages_chat_created_at_idx": { + "name": "copilot_messages_chat_created_at_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"copilot_messages\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_messages_chat_seq_idx": { + "name": "copilot_messages_chat_seq_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "seq", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"copilot_messages\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_messages_chat_stream_idx": { + "name": "copilot_messages_chat_stream_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "stream_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"copilot_messages\".\"stream_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_messages_user_created_at_idx": { + "name": "copilot_messages_user_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"copilot_messages\".\"role\" = 'user' AND \"copilot_messages\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_messages_chat_id_copilot_chats_id_fk": { + "name": "copilot_messages_chat_id_copilot_chats_id_fk", + "tableFrom": "copilot_messages", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_organization_request_stops": { + "name": "copilot_organization_request_stops", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stream_id": { + "name": "stream_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stopped_at": { + "name": "stopped_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "copilot_organization_request_stops_user_id_user_id_fk": { + "name": "copilot_organization_request_stops_user_id_user_id_fk", + "tableFrom": "copilot_organization_request_stops", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_organization_request_stops_organization_id_organization_id_fk": { + "name": "copilot_organization_request_stops_organization_id_organization_id_fk", + "tableFrom": "copilot_organization_request_stops", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "copilot_organization_request_stops_user_id_organization_id_stream_id_pk": { + "name": "copilot_organization_request_stops_user_id_organization_id_stream_id_pk", + "columns": ["user_id", "organization_id", "stream_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_request_stops": { + "name": "copilot_request_stops", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stream_id": { + "name": "stream_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stopped_at": { + "name": "stopped_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "copilot_request_stops_user_id_user_id_fk": { + "name": "copilot_request_stops_user_id_user_id_fk", + "tableFrom": "copilot_request_stops", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_request_stops_workspace_id_workspace_id_fk": { + "name": "copilot_request_stops_workspace_id_workspace_id_fk", + "tableFrom": "copilot_request_stops", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "copilot_request_stops_user_id_workspace_id_stream_id_pk": { + "name": "copilot_request_stops_user_id_workspace_id_stream_id_pk", + "columns": ["user_id", "workspace_id", "stream_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_run_checkpoints": { + "name": "copilot_run_checkpoints", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pending_tool_call_id": { + "name": "pending_tool_call_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "conversation_snapshot": { + "name": "conversation_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "agent_state": { + "name": "agent_state", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "provider_request": { + "name": "provider_request", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "copilot_run_checkpoints_run_id_idx": { + "name": "copilot_run_checkpoints_run_id_idx", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_run_checkpoints_pending_tool_call_id_idx": { + "name": "copilot_run_checkpoints_pending_tool_call_id_idx", + "columns": [ + { + "expression": "pending_tool_call_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_run_checkpoints_run_pending_tool_unique": { + "name": "copilot_run_checkpoints_run_pending_tool_unique", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pending_tool_call_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_run_checkpoints_run_id_copilot_runs_id_fk": { + "name": "copilot_run_checkpoints_run_id_copilot_runs_id_fk", + "tableFrom": "copilot_run_checkpoints", + "tableTo": "copilot_runs", + "columnsFrom": ["run_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_runs": { + "name": "copilot_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tool_execution_version": { + "name": "tool_execution_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "tool_admission_closed_at": { + "name": "tool_admission_closed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "parent_run_id": { + "name": "parent_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stream_id": { + "name": "stream_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent": { + "name": "agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "copilot_run_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "request_context": { + "name": "request_context", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "desktop_device_id": { + "name": "desktop_device_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "copilot_runs_parent_run_id_idx": { + "name": "copilot_runs_parent_run_id_idx", + "columns": [ + { + "expression": "parent_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_chat_id_idx": { + "name": "copilot_runs_chat_id_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_chat_started_at_idx": { + "name": "copilot_runs_chat_started_at_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_user_id_idx": { + "name": "copilot_runs_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_workflow_id_idx": { + "name": "copilot_runs_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_workspace_id_idx": { + "name": "copilot_runs_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_status_idx": { + "name": "copilot_runs_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_chat_execution_idx": { + "name": "copilot_runs_chat_execution_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_execution_started_at_idx": { + "name": "copilot_runs_execution_started_at_idx", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_workspace_completed_at_id_idx": { + "name": "copilot_runs_workspace_completed_at_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date_trunc('milliseconds', \"completed_at\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_stream_id_unique": { + "name": "copilot_runs_stream_id_unique", + "columns": [ + { + "expression": "stream_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_desktop_device_started_at_idx": { + "name": "copilot_runs_desktop_device_started_at_idx", + "columns": [ + { + "expression": "desktop_device_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"copilot_runs\".\"desktop_device_id\" IS NOT NULL", + "concurrently": true, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_runs_chat_id_copilot_chats_id_fk": { + "name": "copilot_runs_chat_id_copilot_chats_id_fk", + "tableFrom": "copilot_runs", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_runs_user_id_user_id_fk": { + "name": "copilot_runs_user_id_user_id_fk", + "tableFrom": "copilot_runs", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_runs_workflow_id_workflow_id_fk": { + "name": "copilot_runs_workflow_id_workflow_id_fk", + "tableFrom": "copilot_runs", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_runs_workspace_id_workspace_id_fk": { + "name": "copilot_runs_workspace_id_workspace_id_fk", + "tableFrom": "copilot_runs", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_runs_organization_id_organization_id_fk": { + "name": "copilot_runs_organization_id_organization_id_fk", + "tableFrom": "copilot_runs", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_runs_desktop_device_id_desktop_devices_id_fk": { + "name": "copilot_runs_desktop_device_id_desktop_devices_id_fk", + "tableFrom": "copilot_runs", + "tableTo": "desktop_devices", + "columnsFrom": ["desktop_device_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_service_usage": { + "name": "copilot_service_usage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "stream_id": { + "name": "stream_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tool_call_id": { + "name": "tool_call_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "service": { + "name": "service", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cost_usd": { + "name": "cost_usd", + "type": "numeric(12, 8)", + "primaryKey": false, + "notNull": false + }, + "worker_origin": { + "name": "worker_origin", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "delivered_at": { + "name": "delivered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "copilot_service_usage_pending_idx": { + "name": "copilot_service_usage_pending_idx", + "columns": [ + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "delivered_at IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_task_subscriptions": { + "name": "copilot_task_subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "task_id": { + "name": "task_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "copilot_task_subscriptions_execution_idx": { + "name": "copilot_task_subscriptions_execution_idx", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_task_subscriptions_task_idx": { + "name": "copilot_task_subscriptions_task_idx", + "columns": [ + { + "expression": "task_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_task_subscriptions_chat_id_copilot_chats_id_fk": { + "name": "copilot_task_subscriptions_chat_id_copilot_chats_id_fk", + "tableFrom": "copilot_task_subscriptions", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_task_subscriptions_workspace_id_workspace_id_fk": { + "name": "copilot_task_subscriptions_workspace_id_workspace_id_fk", + "tableFrom": "copilot_task_subscriptions", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_task_subscriptions_user_id_user_id_fk": { + "name": "copilot_task_subscriptions_user_id_user_id_fk", + "tableFrom": "copilot_task_subscriptions", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_workflow_read_hashes": { + "name": "copilot_workflow_read_hashes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "copilot_workflow_read_hashes_chat_id_idx": { + "name": "copilot_workflow_read_hashes_chat_id_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_workflow_read_hashes_workflow_id_idx": { + "name": "copilot_workflow_read_hashes_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_workflow_read_hashes_chat_workflow_unique": { + "name": "copilot_workflow_read_hashes_chat_workflow_unique", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_workflow_read_hashes_chat_id_copilot_chats_id_fk": { + "name": "copilot_workflow_read_hashes_chat_id_copilot_chats_id_fk", + "tableFrom": "copilot_workflow_read_hashes", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_workflow_read_hashes_workflow_id_workflow_id_fk": { + "name": "copilot_workflow_read_hashes_workflow_id_workflow_id_fk", + "tableFrom": "copilot_workflow_read_hashes", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.credential": { + "name": "credential", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slack_app_id": { + "name": "slack_app_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "credential_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "unredacted": { + "name": "unredacted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env_key": { + "name": "env_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env_owner_user_id": { + "name": "env_owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_service_account_key": { + "name": "encrypted_service_account_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_personal_token": { + "name": "encrypted_personal_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorization_app_id": { + "name": "authorization_app_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credential_group_enrollment_id": { + "name": "credential_group_enrollment_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credential_group_option_id": { + "name": "credential_group_option_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mcp_server_id": { + "name": "mcp_server_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mcp_oauth_config_version": { + "name": "mcp_oauth_config_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "managed_oauth_scope_version": { + "name": "managed_oauth_scope_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "provider_subject_id": { + "name": "provider_subject_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_tenant_id": { + "name": "provider_tenant_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "managed_oauth_status": { + "name": "managed_oauth_status", + "type": "managed_oauth_credential_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "granted_scopes": { + "name": "granted_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "provider_metadata": { + "name": "provider_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "encrypted_oauth_token_set": { + "name": "encrypted_oauth_token_set", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mcp_tools": { + "name": "mcp_tools", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "mcp_tools_refreshed_at": { + "name": "mcp_tools_refreshed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "granted_at": { + "name": "granted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_refreshed_at": { + "name": "last_refreshed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "credential_organization_id_idx": { + "name": "credential_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_organization_account_unique": { + "name": "credential_organization_account_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"credential\".\"account_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_org_personal_token_unique": { + "name": "credential_org_personal_token_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_subject_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"credential\".\"type\" = 'personal_token'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_workspace_id_idx": { + "name": "credential_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_type_idx": { + "name": "credential_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_provider_id_idx": { + "name": "credential_provider_id_idx", + "columns": [ + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_account_id_idx": { + "name": "credential_account_id_idx", + "columns": [ + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_env_owner_user_id_idx": { + "name": "credential_env_owner_user_id_idx", + "columns": [ + { + "expression": "env_owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_enrollment_idx": { + "name": "credential_group_enrollment_idx", + "columns": [ + { + "expression": "credential_group_enrollment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_mcp_server_idx": { + "name": "credential_mcp_server_idx", + "columns": [ + { + "expression": "mcp_server_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_option_unique": { + "name": "credential_group_option_unique", + "columns": [ + { + "expression": "credential_group_enrollment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "credential_group_option_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"credential\".\"type\" = 'managed_oauth'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_managed_mcp_enrollment_server_unique": { + "name": "credential_managed_mcp_enrollment_server_unique", + "columns": [ + { + "expression": "credential_group_enrollment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "mcp_server_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"credential\".\"type\" = 'managed_mcp'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_workspace_account_unique": { + "name": "credential_workspace_account_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "account_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_workspace_env_unique": { + "name": "credential_workspace_env_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "env_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "type = 'env_workspace'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_workspace_personal_env_unique": { + "name": "credential_workspace_personal_env_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "env_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "env_owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "type = 'env_personal'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_personal_token_identity_unique": { + "name": "credential_personal_token_identity_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_subject_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "type = 'personal_token'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "credential_workspace_id_workspace_id_fk": { + "name": "credential_workspace_id_workspace_id_fk", + "tableFrom": "credential", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_organization_id_organization_id_fk": { + "name": "credential_organization_id_organization_id_fk", + "tableFrom": "credential", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_slack_app_id_slack_app_id_fk": { + "name": "credential_slack_app_id_slack_app_id_fk", + "tableFrom": "credential", + "tableTo": "slack_app", + "columnsFrom": ["slack_app_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "credential_account_id_account_id_fk": { + "name": "credential_account_id_account_id_fk", + "tableFrom": "credential", + "tableTo": "account", + "columnsFrom": ["account_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_env_owner_user_id_user_id_fk": { + "name": "credential_env_owner_user_id_user_id_fk", + "tableFrom": "credential", + "tableTo": "user", + "columnsFrom": ["env_owner_user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_credential_group_enrollment_id_credential_group_enrollment_id_fk": { + "name": "credential_credential_group_enrollment_id_credential_group_enrollment_id_fk", + "tableFrom": "credential", + "tableTo": "credential_group_enrollment", + "columnsFrom": ["credential_group_enrollment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_mcp_server_id_mcp_servers_id_fk": { + "name": "credential_mcp_server_id_mcp_servers_id_fk", + "tableFrom": "credential", + "tableTo": "mcp_servers", + "columnsFrom": ["mcp_server_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_created_by_user_id_fk": { + "name": "credential_created_by_user_id_fk", + "tableFrom": "credential", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "credential_owner_check": { + "name": "credential_owner_check", + "value": "num_nonnulls(\"credential\".\"workspace_id\", \"credential\".\"organization_id\") = 1" + }, + "credential_organization_type_check": { + "name": "credential_organization_type_check", + "value": "\"credential\".\"organization_id\" IS NULL OR \"credential\".\"type\" IN ('oauth', 'managed_oauth', 'managed_mcp', 'service_account', 'personal_token')" + }, + "credential_personal_token_source_check": { + "name": "credential_personal_token_source_check", + "value": "(type::text <> 'personal_token') OR (\n created_by IS NOT NULL\n AND provider_id IS NOT NULL\n AND provider_id = 'gitlab'\n AND provider_subject_id IS NOT NULL\n AND provider_tenant_id IS NOT NULL\n AND encrypted_personal_token IS NOT NULL\n AND granted_scopes IS NOT NULL\n AND cardinality(granted_scopes) > 0\n AND account_id IS NULL\n AND env_key IS NULL\n AND env_owner_user_id IS NULL\n AND authorization_app_id IS NULL\n AND encrypted_oauth_token_set IS NULL\n AND encrypted_service_account_key IS NULL\n AND unredacted = false\n )" + }, + "credential_oauth_source_check": { + "name": "credential_oauth_source_check", + "value": "(type <> 'oauth') OR (account_id IS NOT NULL AND provider_id IS NOT NULL)" + }, + "credential_managed_oauth_source_check": { + "name": "credential_managed_oauth_source_check", + "value": "(type::text <> 'managed_oauth') OR (\n account_id IS NULL\n AND provider_id IS NOT NULL\n AND authorization_app_id IS NOT NULL\n AND provider_subject_id IS NOT NULL\n AND managed_oauth_status IS NOT NULL\n AND granted_scopes IS NOT NULL\n AND encrypted_oauth_token_set IS NOT NULL\n AND granted_at IS NOT NULL\n )" + }, + "credential_managed_oauth_group_binding_check": { + "name": "credential_managed_oauth_group_binding_check", + "value": "(type::text <> 'managed_oauth') OR (\n credential_group_enrollment_id IS NOT NULL\n AND credential_group_option_id IS NOT NULL\n AND managed_oauth_scope_version IS NOT NULL\n AND managed_oauth_scope_version > 0\n )" + }, + "credential_managed_mcp_source_check": { + "name": "credential_managed_mcp_source_check", + "value": "(type::text <> 'managed_mcp') OR (\n id LIKE 'mcp-cg-%'\n AND account_id IS NULL\n AND provider_id IS NULL\n AND authorization_app_id IS NULL\n AND credential_group_enrollment_id IS NOT NULL\n AND credential_group_option_id IS NULL\n AND mcp_server_id IS NOT NULL\n AND managed_oauth_status IS NOT NULL\n AND (managed_oauth_status <> 'active' OR (\n encrypted_oauth_token_set IS NOT NULL\n AND mcp_tools IS NOT NULL\n ))\n AND granted_at IS NOT NULL\n AND managed_oauth_scope_version IS NULL\n AND provider_subject_id IS NULL\n AND provider_tenant_id IS NULL\n AND granted_scopes IS NULL\n AND provider_metadata IS NULL\n AND created_by IS NULL\n AND env_key IS NULL\n AND env_owner_user_id IS NULL\n AND encrypted_service_account_key IS NULL\n AND unredacted = false\n )" + }, + "credential_creator_source_check": { + "name": "credential_creator_source_check", + "value": "(type::text = 'managed_mcp') OR created_by IS NOT NULL" + }, + "credential_workspace_env_source_check": { + "name": "credential_workspace_env_source_check", + "value": "(type <> 'env_workspace') OR (env_key IS NOT NULL AND env_owner_user_id IS NULL)" + }, + "credential_personal_env_source_check": { + "name": "credential_personal_env_source_check", + "value": "(type <> 'env_personal') OR (env_key IS NOT NULL AND env_owner_user_id IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.credential_group": { + "name": "credential_group", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "public_id": { + "name": "public_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "options": { + "name": "options", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "encrypted_provider_configuration": { + "name": "encrypted_provider_configuration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "credential_group_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "credential_group_organization_id_idx": { + "name": "credential_group_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_organization_unique": { + "name": "credential_group_organization_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_public_id_unique": { + "name": "credential_group_public_id_unique", + "columns": [ + { + "expression": "public_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_workspace_unique": { + "name": "credential_group_workspace_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "credential_group_workspace_id_workspace_id_fk": { + "name": "credential_group_workspace_id_workspace_id_fk", + "tableFrom": "credential_group", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_group_organization_id_organization_id_fk": { + "name": "credential_group_organization_id_organization_id_fk", + "tableFrom": "credential_group", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_group_created_by_user_id_fk": { + "name": "credential_group_created_by_user_id_fk", + "tableFrom": "credential_group", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "credential_group_owner_check": { + "name": "credential_group_owner_check", + "value": "num_nonnulls(\"credential_group\".\"workspace_id\", \"credential_group\".\"organization_id\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.credential_group_enrollment": { + "name": "credential_group_enrollment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "credential_group_id": { + "name": "credential_group_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "credential_group_enrollment_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'invited'" + }, + "invitation_token_hash": { + "name": "invitation_token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invitation_expires_at": { + "name": "invitation_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "invited_at": { + "name": "invited_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_delivery_error": { + "name": "last_delivery_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "credential_group_enrollment_group_user_unique": { + "name": "credential_group_enrollment_group_user_unique", + "columns": [ + { + "expression": "credential_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"credential_group_enrollment\".\"user_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_enrollment_user_id_idx": { + "name": "credential_group_enrollment_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_enrollment_group_email_unique": { + "name": "credential_group_enrollment_group_email_unique", + "columns": [ + { + "expression": "credential_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_enrollment_invitation_token_hash_unique": { + "name": "credential_group_enrollment_invitation_token_hash_unique", + "columns": [ + { + "expression": "invitation_token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_enrollment_group_status_idx": { + "name": "credential_group_enrollment_group_status_idx", + "columns": [ + { + "expression": "credential_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_enrollment_group_invited_at_id_idx": { + "name": "credential_group_enrollment_group_invited_at_id_idx", + "columns": [ + { + "expression": "credential_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "invited_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "credential_group_enrollment_credential_group_id_credential_group_id_fk": { + "name": "credential_group_enrollment_credential_group_id_credential_group_id_fk", + "tableFrom": "credential_group_enrollment", + "tableTo": "credential_group", + "columnsFrom": ["credential_group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_group_enrollment_user_id_user_id_fk": { + "name": "credential_group_enrollment_user_id_user_id_fk", + "tableFrom": "credential_group_enrollment", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_group_enrollment_created_by_user_id_fk": { + "name": "credential_group_enrollment_created_by_user_id_fk", + "tableFrom": "credential_group_enrollment", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "credential_group_enrollment_normalized_email_check": { + "name": "credential_group_enrollment_normalized_email_check", + "value": "\"credential_group_enrollment\".\"email\" = lower(btrim(\"credential_group_enrollment\".\"email\")) AND length(\"credential_group_enrollment\".\"email\") BETWEEN 3 AND 320" + }, + "credential_group_enrollment_invitation_token_hash_length_check": { + "name": "credential_group_enrollment_invitation_token_hash_length_check", + "value": "length(\"credential_group_enrollment\".\"invitation_token_hash\") = 64" + } + }, + "isRLSEnabled": false + }, + "public.credential_member": { + "name": "credential_member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "credential_member_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "status": { + "name": "status", + "type": "credential_member_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "joined_at": { + "name": "joined_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "invited_by": { + "name": "invited_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "credential_member_user_id_idx": { + "name": "credential_member_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_member_role_idx": { + "name": "credential_member_role_idx", + "columns": [ + { + "expression": "role", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_member_status_idx": { + "name": "credential_member_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_member_unique": { + "name": "credential_member_unique", + "columns": [ + { + "expression": "credential_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "credential_member_credential_id_credential_id_fk": { + "name": "credential_member_credential_id_credential_id_fk", + "tableFrom": "credential_member", + "tableTo": "credential", + "columnsFrom": ["credential_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_member_user_id_user_id_fk": { + "name": "credential_member_user_id_user_id_fk", + "tableFrom": "credential_member", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_member_invited_by_user_id_fk": { + "name": "credential_member_invited_by_user_id_fk", + "tableFrom": "credential_member", + "tableTo": "user", + "columnsFrom": ["invited_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.custom_block": { + "name": "custom_block", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "icon_url": { + "name": "icon_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "inputs": { + "name": "inputs", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "outputs": { + "name": "outputs", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "trace_child_runs": { + "name": "trace_child_runs", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "custom_block_organization_id_idx": { + "name": "custom_block_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "custom_block_workflow_id_idx": { + "name": "custom_block_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "custom_block_organization_type_unique": { + "name": "custom_block_organization_type_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "custom_block_organization_id_organization_id_fk": { + "name": "custom_block_organization_id_organization_id_fk", + "tableFrom": "custom_block", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "custom_block_workflow_id_workflow_id_fk": { + "name": "custom_block_workflow_id_workflow_id_fk", + "tableFrom": "custom_block", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "custom_block_created_by_user_id_fk": { + "name": "custom_block_created_by_user_id_fk", + "tableFrom": "custom_block", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.custom_tools": { + "name": "custom_tools", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schema": { + "name": "schema", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "custom_tools_workspace_id_idx": { + "name": "custom_tools_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "custom_tools_workspace_title_unique": { + "name": "custom_tools_workspace_title_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "title", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "custom_tools_workspace_id_workspace_id_fk": { + "name": "custom_tools_workspace_id_workspace_id_fk", + "tableFrom": "custom_tools", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "custom_tools_user_id_user_id_fk": { + "name": "custom_tools_user_id_user_id_fk", + "tableFrom": "custom_tools", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.dashboard": { + "name": "dashboard", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "dashboard_workspace_id_unique": { + "name": "dashboard_workspace_id_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "dashboard_workspace_id_workspace_id_fk": { + "name": "dashboard_workspace_id_workspace_id_fk", + "tableFrom": "dashboard", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "dashboard_created_by_user_id_fk": { + "name": "dashboard_created_by_user_id_fk", + "tableFrom": "dashboard", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "dashboard_updated_by_user_id_fk": { + "name": "dashboard_updated_by_user_id_fk", + "tableFrom": "dashboard", + "tableTo": "user", + "columnsFrom": ["updated_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.data_drain_runs": { + "name": "data_drain_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "drain_id": { + "name": "drain_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "data_drain_run_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "trigger": { + "name": "trigger", + "type": "data_drain_run_trigger", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "rows_exported": { + "name": "rows_exported", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "bytes_written": { + "name": "bytes_written", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "cursor_before": { + "name": "cursor_before", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cursor_after": { + "name": "cursor_after", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "locators": { + "name": "locators", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + } + }, + "indexes": { + "data_drain_runs_drain_started_idx": { + "name": "data_drain_runs_drain_started_idx", + "columns": [ + { + "expression": "drain_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "data_drain_runs_drain_id_data_drains_id_fk": { + "name": "data_drain_runs_drain_id_data_drains_id_fk", + "tableFrom": "data_drain_runs", + "tableTo": "data_drains", + "columnsFrom": ["drain_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.data_drains": { + "name": "data_drains", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "data_drain_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "destination_type": { + "name": "destination_type", + "type": "data_drain_destination", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "destination_config": { + "name": "destination_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "destination_credentials": { + "name": "destination_credentials", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schedule_cadence": { + "name": "schedule_cadence", + "type": "data_drain_cadence", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "cursor": { + "name": "cursor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_run_at": { + "name": "last_run_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_success_at": { + "name": "last_success_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "data_drains_org_idx": { + "name": "data_drains_org_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_drains_due_idx": { + "name": "data_drains_due_idx", + "columns": [ + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_run_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_drains_org_name_unique": { + "name": "data_drains_org_name_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "data_drains_organization_id_organization_id_fk": { + "name": "data_drains_organization_id_organization_id_fk", + "tableFrom": "data_drains", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "data_drains_created_by_user_id_fk": { + "name": "data_drains_created_by_user_id_fk", + "tableFrom": "data_drains", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.desktop_devices": { + "name": "desktop_devices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "app_version": { + "name": "app_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "capabilities": { + "name": "capabilities", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "desktop_devices_user_id_idx": { + "name": "desktop_devices_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "desktop_devices_session_id_idx": { + "name": "desktop_devices_session_id_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "desktop_devices_user_id_user_id_fk": { + "name": "desktop_devices_user_id_user_id_fk", + "tableFrom": "desktop_devices", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "desktop_devices_session_id_session_id_fk": { + "name": "desktop_devices_session_id_session_id_fk", + "tableFrom": "desktop_devices", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.docs_embeddings": { + "name": "docs_embeddings", + "schema": "", + "columns": { + "chunk_id": { + "name": "chunk_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chunk_text": { + "name": "chunk_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_document": { + "name": "source_document", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_link": { + "name": "source_link", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "header_text": { + "name": "header_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "header_level": { + "name": "header_level", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "token_count": { + "name": "token_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": true + }, + "embedding_model": { + "name": "embedding_model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text-embedding-3-small'" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "chunk_text_tsv": { + "name": "chunk_text_tsv", + "type": "tsvector", + "primaryKey": false, + "notNull": false, + "generated": { + "as": "to_tsvector('english', \"docs_embeddings\".\"chunk_text\")", + "type": "stored" + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "docs_emb_source_document_idx": { + "name": "docs_emb_source_document_idx", + "columns": [ + { + "expression": "source_document", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_emb_header_level_idx": { + "name": "docs_emb_header_level_idx", + "columns": [ + { + "expression": "header_level", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_emb_source_header_idx": { + "name": "docs_emb_source_header_idx", + "columns": [ + { + "expression": "source_document", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "header_level", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_emb_model_idx": { + "name": "docs_emb_model_idx", + "columns": [ + { + "expression": "embedding_model", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_emb_created_at_idx": { + "name": "docs_emb_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_embedding_vector_hnsw_idx": { + "name": "docs_embedding_vector_hnsw_idx", + "columns": [ + { + "expression": "embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": { + "m": 16, + "ef_construction": 64 + } + }, + "docs_emb_metadata_gin_idx": { + "name": "docs_emb_metadata_gin_idx", + "columns": [ + { + "expression": "metadata", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "docs_emb_chunk_text_fts_idx": { + "name": "docs_emb_chunk_text_fts_idx", + "columns": [ + { + "expression": "chunk_text_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "docs_embedding_not_null_check": { + "name": "docs_embedding_not_null_check", + "value": "\"embedding\" IS NOT NULL" + }, + "docs_header_level_check": { + "name": "docs_header_level_check", + "value": "\"header_level\" >= 1 AND \"header_level\" <= 6" + } + }, + "isRLSEnabled": false + }, + "public.document": { + "name": "document", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "filename": { + "name": "filename", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_url": { + "name": "file_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storage_key": { + "name": "storage_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_size": { + "name": "file_size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chunk_count": { + "name": "chunk_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "token_count": { + "name": "token_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "character_count": { + "name": "character_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "processing_status": { + "name": "processing_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "processing_attempts": { + "name": "processing_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "processing_queued_at": { + "name": "processing_queued_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "processing_queue_token": { + "name": "processing_queue_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "processing_started_at": { + "name": "processing_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "processing_deferred_until": { + "name": "processing_deferred_until", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "processing_completed_at": { + "name": "processing_completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "processing_error": { + "name": "processing_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "processing_recovery_after": { + "name": "processing_recovery_after", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "user_excluded": { + "name": "user_excluded", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "tag1": { + "name": "tag1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag2": { + "name": "tag2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag3": { + "name": "tag3", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag4": { + "name": "tag4", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag5": { + "name": "tag5", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag6": { + "name": "tag6", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag7": { + "name": "tag7", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "number1": { + "name": "number1", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "number2": { + "name": "number2", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "number3": { + "name": "number3", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "number4": { + "name": "number4", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "number5": { + "name": "number5", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "date1": { + "name": "date1", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "date2": { + "name": "date2", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "boolean1": { + "name": "boolean1", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "boolean2": { + "name": "boolean2", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "boolean3": { + "name": "boolean3", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "connector_id": { + "name": "connector_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_url": { + "name": "source_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret_provenance_version": { + "name": "secret_provenance_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "uploaded_by": { + "name": "uploaded_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "acl": { + "name": "acl", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{ws}'::text[]" + }, + "acl_requirements": { + "name": "acl_requirements", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "acl_verified_at": { + "name": "acl_verified_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "source_modified_at": { + "name": "source_modified_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "source_seen_at": { + "name": "source_seen_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "doc_kb_id_idx": { + "name": "doc_kb_id_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_kb_source_modified_idx": { + "name": "doc_kb_source_modified_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_modified_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"deleted_at\" IS NULL", + "concurrently": true, + "method": "btree", + "with": {} + }, + "doc_acl_gin_idx": { + "name": "doc_acl_gin_idx", + "columns": [ + { + "expression": "acl", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "array_ops" + } + ], + "isUnique": false, + "where": "\"document\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "gin", + "with": {} + }, + "doc_filename_idx": { + "name": "doc_filename_idx", + "columns": [ + { + "expression": "filename", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_processing_status_idx": { + "name": "doc_processing_status_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "processing_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_processing_recovery_idx": { + "name": "doc_processing_recovery_idx", + "columns": [ + { + "expression": "uploaded_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"processing_status\" IN ('pending', 'processing', 'failed') AND \"document\".\"connector_id\" IS NOT NULL AND \"document\".\"content_hash\" IS NOT NULL AND \"document\".\"storage_key\" IS NOT NULL AND \"document\".\"user_excluded\" = false AND \"document\".\"archived_at\" IS NULL AND \"document\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_connector_processing_recovery_idx": { + "name": "doc_connector_processing_recovery_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "uploaded_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"processing_status\" IN ('pending', 'processing', 'failed') AND \"document\".\"connector_id\" IS NOT NULL AND \"document\".\"content_hash\" IS NOT NULL AND \"document\".\"storage_key\" IS NOT NULL AND \"document\".\"user_excluded\" = false AND \"document\".\"archived_at\" IS NULL AND \"document\".\"deleted_at\" IS NULL", + "concurrently": true, + "method": "btree", + "with": {} + }, + "doc_connector_processing_status_idx": { + "name": "doc_connector_processing_status_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "processing_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"processing_status\" IN ('pending', 'processing', 'failed') AND \"document\".\"connector_id\" IS NOT NULL AND \"document\".\"user_excluded\" = false AND \"document\".\"archived_at\" IS NULL AND \"document\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_connector_external_id_idx": { + "name": "doc_connector_external_id_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"document\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_connector_source_lookup_idx": { + "name": "doc_connector_source_lookup_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_connector_reconciliation_idx": { + "name": "doc_connector_reconciliation_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "COALESCE(\"source_seen_at\", '-infinity'::timestamp)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"user_excluded\" = false AND \"document\".\"archived_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_connector_reconciliation_v2_idx": { + "name": "doc_connector_reconciliation_v2_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"user_excluded\" = false AND \"document\".\"archived_at\" IS NULL", + "concurrently": true, + "method": "btree", + "with": {} + }, + "doc_active_kb_token_count_idx": { + "name": "doc_active_kb_token_count_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "token_count", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"user_excluded\" = false AND \"document\".\"archived_at\" IS NULL AND \"document\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_storage_key_idx": { + "name": "doc_storage_key_idx", + "columns": [ + { + "expression": "storage_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"storage_key\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_archived_at_partial_idx": { + "name": "doc_archived_at_partial_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"archived_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_deleted_at_partial_idx": { + "name": "doc_deleted_at_partial_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_connector_tombstone_idx": { + "name": "doc_connector_tombstone_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"archived_at\" IS NULL AND (\"document\".\"deleted_at\" IS NOT NULL OR \"document\".\"content_hash\" IS NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_connector_live_idx": { + "name": "doc_connector_live_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"user_excluded\" = false AND \"document\".\"archived_at\" IS NULL AND \"document\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_kb_tag1_lower_idx": { + "name": "doc_kb_tag1_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag1\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_kb_tag2_lower_idx": { + "name": "doc_kb_tag2_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag2\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_kb_tag3_lower_idx": { + "name": "doc_kb_tag3_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag3\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_kb_tag4_lower_idx": { + "name": "doc_kb_tag4_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag4\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_kb_tag5_lower_idx": { + "name": "doc_kb_tag5_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag5\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_kb_tag6_lower_idx": { + "name": "doc_kb_tag6_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag6\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_kb_tag7_lower_idx": { + "name": "doc_kb_tag7_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag7\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_number1_idx": { + "name": "doc_number1_idx", + "columns": [ + { + "expression": "number1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_number2_idx": { + "name": "doc_number2_idx", + "columns": [ + { + "expression": "number2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_number3_idx": { + "name": "doc_number3_idx", + "columns": [ + { + "expression": "number3", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_number4_idx": { + "name": "doc_number4_idx", + "columns": [ + { + "expression": "number4", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_number5_idx": { + "name": "doc_number5_idx", + "columns": [ + { + "expression": "number5", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_date1_idx": { + "name": "doc_date1_idx", + "columns": [ + { + "expression": "date1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "doc_date2_idx": { + "name": "doc_date2_idx", + "columns": [ + { + "expression": "date2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "doc_boolean1_idx": { + "name": "doc_boolean1_idx", + "columns": [ + { + "expression": "boolean1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_boolean2_idx": { + "name": "doc_boolean2_idx", + "columns": [ + { + "expression": "boolean2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_boolean3_idx": { + "name": "doc_boolean3_idx", + "columns": [ + { + "expression": "boolean3", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "document_knowledge_base_id_knowledge_base_id_fk": { + "name": "document_knowledge_base_id_knowledge_base_id_fk", + "tableFrom": "document", + "tableTo": "knowledge_base", + "columnsFrom": ["knowledge_base_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "document_connector_id_knowledge_connector_id_fk": { + "name": "document_connector_id_knowledge_connector_id_fk", + "tableFrom": "document", + "tableTo": "knowledge_connector", + "columnsFrom": ["connector_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "document_uploaded_by_user_id_fk": { + "name": "document_uploaded_by_user_id_fk", + "tableFrom": "document", + "tableTo": "user", + "columnsFrom": ["uploaded_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "doc_acl_token_shape_check": { + "name": "doc_acl_token_shape_check", + "value": "array_position(\"document\".\"acl\", NULL) IS NULL AND (cardinality(\"document\".\"acl\") = 0 OR (cardinality(\"document\".\"acl\") = array_length(string_to_array(array_to_string(\"document\".\"acl\", E'\\n'), E'\\n'), 1) AND array_to_string(\"document\".\"acl\", E'\\n') ~ '^((ws|pub|link|u:[^\\nA-Z]+@[^\\nA-Z]+|[gs]:[^\\n:]+:[^\\n:]+:[^\\n]+)(\\n(ws|pub|link|u:[^\\nA-Z]+@[^\\nA-Z]+|[gs]:[^\\n:]+:[^\\n:]+:[^\\n]+))*)$'))" + } + }, + "isRLSEnabled": false + }, + "public.document_secret_provenance": { + "name": "document_secret_provenance", + "schema": "", + "columns": { + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "source_hash": { + "name": "source_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entries": { + "name": "entries", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "document_secret_provenance_document_id_document_id_fk": { + "name": "document_secret_provenance_document_id_document_id_fk", + "tableFrom": "document_secret_provenance", + "tableTo": "document", + "columnsFrom": ["document_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "document_secret_provenance_status_check": { + "name": "document_secret_provenance_status_check", + "value": "\"document_secret_provenance\".\"status\" IN ('exact', 'unknown')" + } + }, + "isRLSEnabled": false + }, + "public.embedding": { + "name": "embedding", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chunk_index": { + "name": "chunk_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "chunk_hash": { + "name": "chunk_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret_provenance_version": { + "name": "secret_provenance_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "content_length": { + "name": "content_length", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "token_count": { + "name": "token_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "embedding_384": { + "name": "embedding_384", + "type": "vector(384)", + "primaryKey": false, + "notNull": false + }, + "embedding_768": { + "name": "embedding_768", + "type": "vector(768)", + "primaryKey": false, + "notNull": false + }, + "embedding_1024": { + "name": "embedding_1024", + "type": "vector(1024)", + "primaryKey": false, + "notNull": false + }, + "embedding_3072": { + "name": "embedding_3072", + "type": "vector(3072)", + "primaryKey": false, + "notNull": false + }, + "embedding_model": { + "name": "embedding_model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text-embedding-3-small'" + }, + "start_offset": { + "name": "start_offset", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "end_offset": { + "name": "end_offset", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tag1": { + "name": "tag1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag2": { + "name": "tag2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag3": { + "name": "tag3", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag4": { + "name": "tag4", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag5": { + "name": "tag5", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag6": { + "name": "tag6", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag7": { + "name": "tag7", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "number1": { + "name": "number1", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "number2": { + "name": "number2", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "number3": { + "name": "number3", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "number4": { + "name": "number4", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "number5": { + "name": "number5", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "date1": { + "name": "date1", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "date2": { + "name": "date2", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "boolean1": { + "name": "boolean1", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "boolean2": { + "name": "boolean2", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "boolean3": { + "name": "boolean3", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "content_tsv": { + "name": "content_tsv", + "type": "tsvector", + "primaryKey": false, + "notNull": false, + "generated": { + "as": "to_tsvector('english', \"embedding\".\"content\")", + "type": "stored" + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "emb_doc_chunk_idx": { + "name": "emb_doc_chunk_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chunk_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_model_idx": { + "name": "emb_kb_model_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "embedding_model", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_enabled_idx": { + "name": "emb_kb_enabled_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_doc_enabled_idx": { + "name": "emb_doc_enabled_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_tag1_lower_idx": { + "name": "emb_kb_tag1_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag1\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_tag2_lower_idx": { + "name": "emb_kb_tag2_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag2\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_tag3_lower_idx": { + "name": "emb_kb_tag3_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag3\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_tag4_lower_idx": { + "name": "emb_kb_tag4_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag4\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_tag5_lower_idx": { + "name": "emb_kb_tag5_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag5\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_tag6_lower_idx": { + "name": "emb_kb_tag6_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag6\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_tag7_lower_idx": { + "name": "emb_kb_tag7_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag7\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_number1_idx": { + "name": "emb_number1_idx", + "columns": [ + { + "expression": "number1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_number2_idx": { + "name": "emb_number2_idx", + "columns": [ + { + "expression": "number2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_number3_idx": { + "name": "emb_number3_idx", + "columns": [ + { + "expression": "number3", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_number4_idx": { + "name": "emb_number4_idx", + "columns": [ + { + "expression": "number4", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_number5_idx": { + "name": "emb_number5_idx", + "columns": [ + { + "expression": "number5", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_date1_idx": { + "name": "emb_date1_idx", + "columns": [ + { + "expression": "date1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "emb_date2_idx": { + "name": "emb_date2_idx", + "columns": [ + { + "expression": "date2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "emb_boolean1_idx": { + "name": "emb_boolean1_idx", + "columns": [ + { + "expression": "boolean1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_boolean2_idx": { + "name": "emb_boolean2_idx", + "columns": [ + { + "expression": "boolean2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_boolean3_idx": { + "name": "emb_boolean3_idx", + "columns": [ + { + "expression": "boolean3", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_content_fts_idx": { + "name": "emb_content_fts_idx", + "columns": [ + { + "expression": "content_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "embedding_knowledge_base_id_knowledge_base_id_fk": { + "name": "embedding_knowledge_base_id_knowledge_base_id_fk", + "tableFrom": "embedding", + "tableTo": "knowledge_base", + "columnsFrom": ["knowledge_base_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "embedding_document_id_document_id_fk": { + "name": "embedding_document_id_document_id_fk", + "tableFrom": "embedding", + "tableTo": "document", + "columnsFrom": ["document_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "embedding_width_check": { + "name": "embedding_width_check", + "value": "num_nonnulls(\"embedding\", \"embedding_384\", \"embedding_768\", \"embedding_1024\", \"embedding_3072\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.embedding_keyword_search": { + "name": "embedding_keyword_search", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "content_tsv": { + "name": "content_tsv", + "type": "tsvector", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "embedding_keyword_search_kb_idx": { + "name": "embedding_keyword_search_kb_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_keyword_search_document_idx": { + "name": "embedding_keyword_search_document_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_keyword_search_content_idx": { + "name": "embedding_keyword_search_content_idx", + "columns": [ + { + "expression": "content_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "embedding_keyword_search_id_embedding_id_fk": { + "name": "embedding_keyword_search_id_embedding_id_fk", + "tableFrom": "embedding_keyword_search", + "tableTo": "embedding", + "columnsFrom": ["id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.embedding_keyword_tin": { + "name": "embedding_keyword_tin", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "connector_id": { + "name": "connector_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "acl": { + "name": "acl", + "type": "text[]", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "embedding_keyword_tin_document_idx": { + "name": "embedding_keyword_tin_document_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "embedding_keyword_tin_id_embedding_id_fk": { + "name": "embedding_keyword_tin_id_embedding_id_fk", + "tableFrom": "embedding_keyword_tin", + "tableTo": "embedding", + "columnsFrom": ["id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.embedding_search": { + "name": "embedding_search", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "connector_id": { + "name": "connector_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "acl": { + "name": "acl", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "binary": { + "name": "binary", + "type": "bit(1536)", + "primaryKey": false, + "notNull": false + }, + "binary_384": { + "name": "binary_384", + "type": "bit(384)", + "primaryKey": false, + "notNull": false + }, + "binary_768": { + "name": "binary_768", + "type": "bit(768)", + "primaryKey": false, + "notNull": false + }, + "binary_1024": { + "name": "binary_1024", + "type": "bit(1024)", + "primaryKey": false, + "notNull": false + }, + "binary_3072": { + "name": "binary_3072", + "type": "bit(3072)", + "primaryKey": false, + "notNull": false + }, + "vector": { + "name": "vector", + "type": "halfvec(1536)", + "primaryKey": false, + "notNull": false + }, + "vector_384": { + "name": "vector_384", + "type": "halfvec(384)", + "primaryKey": false, + "notNull": false + }, + "vector_512": { + "name": "vector_512", + "type": "halfvec(512)", + "primaryKey": false, + "notNull": false + }, + "vector_768": { + "name": "vector_768", + "type": "halfvec(768)", + "primaryKey": false, + "notNull": false + }, + "vector_1024": { + "name": "vector_1024", + "type": "halfvec(1024)", + "primaryKey": false, + "notNull": false + }, + "vector_3072": { + "name": "vector_3072", + "type": "halfvec(3072)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "embedding_search_kb_idx": { + "name": "embedding_search_kb_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_search_document_lookup_idx": { + "name": "embedding_search_document_lookup_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"embedding_search\".\"enabled\"", + "concurrently": true, + "method": "btree", + "with": {} + }, + "embedding_search_cosine_hnsw_idx": { + "name": "embedding_search_cosine_hnsw_idx", + "columns": [ + { + "expression": "vector", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "halfvec_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": { + "m": 16, + "ef_construction": 64 + } + }, + "embedding_search_512_cosine_hnsw_idx": { + "name": "embedding_search_512_cosine_hnsw_idx", + "columns": [ + { + "expression": "vector_512", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "halfvec_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": { + "m": 16, + "ef_construction": 64 + } + }, + "embedding_search_384_cosine_hnsw_idx": { + "name": "embedding_search_384_cosine_hnsw_idx", + "columns": [ + { + "expression": "vector_384", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "halfvec_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": { + "m": 16, + "ef_construction": 64 + } + }, + "embedding_search_768_cosine_hnsw_idx": { + "name": "embedding_search_768_cosine_hnsw_idx", + "columns": [ + { + "expression": "vector_768", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "halfvec_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": { + "m": 16, + "ef_construction": 64 + } + }, + "embedding_search_1024_cosine_hnsw_idx": { + "name": "embedding_search_1024_cosine_hnsw_idx", + "columns": [ + { + "expression": "vector_1024", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "halfvec_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": { + "m": 16, + "ef_construction": 64 + } + }, + "embedding_search_3072_cosine_hnsw_idx": { + "name": "embedding_search_3072_cosine_hnsw_idx", + "columns": [ + { + "expression": "vector_3072", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "halfvec_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": { + "m": 16, + "ef_construction": 64 + } + } + }, + "foreignKeys": { + "embedding_search_id_embedding_id_fk": { + "name": "embedding_search_id_embedding_id_fk", + "tableFrom": "embedding_search", + "tableTo": "embedding", + "columnsFrom": ["id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "embedding_search_width_check": { + "name": "embedding_search_width_check", + "value": "num_nonnulls(\"binary\", \"binary_384\", \"binary_768\", \"binary_1024\", \"binary_3072\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.embedding_secret_provenance": { + "name": "embedding_secret_provenance", + "schema": "", + "columns": { + "embedding_id": { + "name": "embedding_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entries": { + "name": "entries", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "embedding_secret_provenance_embedding_id_embedding_id_fk": { + "name": "embedding_secret_provenance_embedding_id_embedding_id_fk", + "tableFrom": "embedding_secret_provenance", + "tableTo": "embedding", + "columnsFrom": ["embedding_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "embedding_secret_provenance_status_check": { + "name": "embedding_secret_provenance_status_check", + "value": "\"embedding_secret_provenance\".\"status\" IN ('exact', 'unknown')" + } + }, + "isRLSEnabled": false + }, + "public.environment": { + "name": "environment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "variables": { + "name": "variables", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "environment_user_id_user_id_fk": { + "name": "environment_user_id_user_id_fk", + "tableFrom": "environment", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "environment_user_id_unique": { + "name": "environment_user_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.execution_large_value_dependencies": { + "name": "execution_large_value_dependencies", + "schema": "", + "columns": { + "parent_key": { + "name": "parent_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "child_key": { + "name": "child_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "execution_large_value_dependencies_workspace_parent_key_idx": { + "name": "execution_large_value_dependencies_workspace_parent_key_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_large_value_dependencies_workspace_child_key_idx": { + "name": "execution_large_value_dependencies_workspace_child_key_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "child_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "execution_large_value_dependencies_workspace_id_workspace_id_fk": { + "name": "execution_large_value_dependencies_workspace_id_workspace_id_fk", + "tableFrom": "execution_large_value_dependencies", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "execution_large_value_dependencies_parent_key_child_key_pk": { + "name": "execution_large_value_dependencies_parent_key_child_key_pk", + "columns": ["parent_key", "child_key"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.execution_large_value_references": { + "name": "execution_large_value_references", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "execution_large_value_reference_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "execution_large_value_references_workspace_execution_source_idx": { + "name": "execution_large_value_references_workspace_execution_source_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_large_value_references_workflow_id_idx": { + "name": "execution_large_value_references_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "execution_large_value_references_workspace_id_workspace_id_fk": { + "name": "execution_large_value_references_workspace_id_workspace_id_fk", + "tableFrom": "execution_large_value_references", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "execution_large_value_references_workflow_id_workflow_id_fk": { + "name": "execution_large_value_references_workflow_id_workflow_id_fk", + "tableFrom": "execution_large_value_references", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "execution_large_value_references_key_execution_id_source_pk": { + "name": "execution_large_value_references_key_execution_id_source_pk", + "columns": ["key", "execution_id", "source"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.execution_large_values": { + "name": "execution_large_values", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_execution_id": { + "name": "owner_execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "execution_large_values_owner_execution_id_idx": { + "name": "execution_large_values_owner_execution_id_idx", + "columns": [ + { + "expression": "owner_execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_large_values_cleanup_idx": { + "name": "execution_large_values_cleanup_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"execution_large_values\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_large_values_tombstone_cleanup_idx": { + "name": "execution_large_values_tombstone_cleanup_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"execution_large_values\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_large_values_workflow_id_idx": { + "name": "execution_large_values_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "execution_large_values_workspace_id_workspace_id_fk": { + "name": "execution_large_values_workspace_id_workspace_id_fk", + "tableFrom": "execution_large_values", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "execution_large_values_workflow_id_workflow_id_fk": { + "name": "execution_large_values_workflow_id_workflow_id_fk", + "tableFrom": "execution_large_values", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.folder": { + "name": "folder", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "resource_type": { + "name": "resource_type", + "type": "folder_resource_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "locked": { + "name": "locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "folder_user_idx": { + "name": "folder_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "folder_workspace_resource_parent_idx": { + "name": "folder_workspace_resource_parent_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "folder_parent_sort_idx": { + "name": "folder_parent_sort_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "folder_deleted_at_idx": { + "name": "folder_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "folder_workspace_deleted_partial_idx": { + "name": "folder_workspace_deleted_partial_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"folder\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "folder_workspace_resource_parent_name_active_unique": { + "name": "folder_workspace_resource_parent_name_active_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"parent_id\", '')", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"folder\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "folder_user_id_user_id_fk": { + "name": "folder_user_id_user_id_fk", + "tableFrom": "folder", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "folder_workspace_id_workspace_id_fk": { + "name": "folder_workspace_id_workspace_id_fk", + "tableFrom": "folder", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "folder_parent_id_folder_id_fk": { + "name": "folder_parent_id_folder_id_fk", + "tableFrom": "folder", + "tableTo": "folder", + "columnsFrom": ["parent_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.idempotency_key": { + "name": "idempotency_key", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "result": { + "name": "result", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idempotency_key_created_at_idx": { + "name": "idempotency_key_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "invitation_kind", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'organization'" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "membership_intent": { + "name": "membership_intent", + "type": "invitation_membership_intent", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'internal'" + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "invitation_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "invitation_email_idx": { + "name": "invitation_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "invitation_organization_id_idx": { + "name": "invitation_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "invitation_status_idx": { + "name": "invitation_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "invitation_pending_email_org_unique": { + "name": "invitation_pending_email_org_unique", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"invitation\".\"status\" = 'pending' AND \"invitation\".\"organization_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "invitation_inviter_id_user_id_fk": { + "name": "invitation_inviter_id_user_id_fk", + "tableFrom": "invitation", + "tableTo": "user", + "columnsFrom": ["inviter_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "invitation_token_unique": { + "name": "invitation_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation_workspace_grant": { + "name": "invitation_workspace_grant", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "invitation_id": { + "name": "invitation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission": { + "name": "permission", + "type": "permission_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "invitation_workspace_grant_unique": { + "name": "invitation_workspace_grant_unique", + "columns": [ + { + "expression": "invitation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "invitation_workspace_grant_workspace_id_idx": { + "name": "invitation_workspace_grant_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "invitation_workspace_grant_invitation_id_invitation_id_fk": { + "name": "invitation_workspace_grant_invitation_id_invitation_id_fk", + "tableFrom": "invitation_workspace_grant", + "tableTo": "invitation", + "columnsFrom": ["invitation_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_workspace_grant_workspace_id_workspace_id_fk": { + "name": "invitation_workspace_grant_workspace_id_workspace_id_fk", + "tableFrom": "invitation_workspace_grant", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.job_execution_logs": { + "name": "job_execution_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "schedule_id": { + "name": "schedule_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "level": { + "name": "level", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'running'" + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "total_duration_ms": { + "name": "total_duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "execution_data": { + "name": "execution_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "cost": { + "name": "cost", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "job_execution_logs_schedule_id_idx": { + "name": "job_execution_logs_schedule_id_idx", + "columns": [ + { + "expression": "schedule_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "job_execution_logs_workspace_started_at_idx": { + "name": "job_execution_logs_workspace_started_at_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "job_execution_logs_workspace_ended_at_id_idx": { + "name": "job_execution_logs_workspace_ended_at_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date_trunc('milliseconds', \"ended_at\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "job_execution_logs_execution_id_unique": { + "name": "job_execution_logs_execution_id_unique", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "job_execution_logs_trigger_idx": { + "name": "job_execution_logs_trigger_idx", + "columns": [ + { + "expression": "trigger", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "job_execution_logs_schedule_id_workflow_schedule_id_fk": { + "name": "job_execution_logs_schedule_id_workflow_schedule_id_fk", + "tableFrom": "job_execution_logs", + "tableTo": "workflow_schedule", + "columnsFrom": ["schedule_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "job_execution_logs_workspace_id_workspace_id_fk": { + "name": "job_execution_logs_workspace_id_workspace_id_fk", + "tableFrom": "job_execution_logs", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.knowledge_base": { + "name": "knowledge_base", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "folder_id": { + "name": "folder_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_search_index": { + "name": "is_search_index", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "token_count": { + "name": "token_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "embedding_model": { + "name": "embedding_model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text-embedding-3-small'" + }, + "embedding_dimension": { + "name": "embedding_dimension", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1536 + }, + "chunking_config": { + "name": "chunking_config", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{\"maxSize\": 1024, \"minSize\": 1, \"overlap\": 200}'" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kb_organization_id_idx": { + "name": "kb_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_organization_search_index_unique": { + "name": "kb_organization_search_index_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"knowledge_base\".\"is_search_index\" = true AND \"knowledge_base\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_organization_name_active_unique": { + "name": "kb_organization_name_active_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"knowledge_base\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_user_id_idx": { + "name": "kb_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_workspace_id_idx": { + "name": "kb_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_user_workspace_idx": { + "name": "kb_user_workspace_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_folder_id_idx": { + "name": "kb_folder_id_idx", + "columns": [ + { + "expression": "folder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_deleted_at_idx": { + "name": "kb_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_workspace_deleted_partial_idx": { + "name": "kb_workspace_deleted_partial_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"knowledge_base\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_workspace_name_active_unique": { + "name": "kb_workspace_name_active_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"knowledge_base\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_workspace_search_index_unique": { + "name": "kb_workspace_search_index_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"knowledge_base\".\"is_search_index\" = true AND \"knowledge_base\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_base_user_id_user_id_fk": { + "name": "knowledge_base_user_id_user_id_fk", + "tableFrom": "knowledge_base", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_base_workspace_id_workspace_id_fk": { + "name": "knowledge_base_workspace_id_workspace_id_fk", + "tableFrom": "knowledge_base", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_base_organization_id_organization_id_fk": { + "name": "knowledge_base_organization_id_organization_id_fk", + "tableFrom": "knowledge_base", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_base_folder_id_folder_id_fk": { + "name": "knowledge_base_folder_id_folder_id_fk", + "tableFrom": "knowledge_base", + "tableTo": "folder", + "columnsFrom": ["folder_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kb_owner_check": { + "name": "kb_owner_check", + "value": "num_nonnulls(\"knowledge_base\".\"workspace_id\", \"knowledge_base\".\"organization_id\") = 1" + }, + "kb_organization_search_index_check": { + "name": "kb_organization_search_index_check", + "value": "\"knowledge_base\".\"organization_id\" IS NULL OR \"knowledge_base\".\"is_search_index\"" + }, + "kb_organization_folder_check": { + "name": "kb_organization_folder_check", + "value": "\"knowledge_base\".\"organization_id\" IS NULL OR \"knowledge_base\".\"folder_id\" IS NULL" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_base_tag_definitions": { + "name": "knowledge_base_tag_definitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tag_slot": { + "name": "tag_slot", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "field_type": { + "name": "field_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kb_tag_definitions_kb_slot_idx": { + "name": "kb_tag_definitions_kb_slot_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tag_slot", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_tag_definitions_kb_display_name_idx": { + "name": "kb_tag_definitions_kb_display_name_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "display_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_tag_definitions_kb_id_idx": { + "name": "kb_tag_definitions_kb_id_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_base_tag_definitions_knowledge_base_id_knowledge_base_id_fk": { + "name": "knowledge_base_tag_definitions_knowledge_base_id_knowledge_base_id_fk", + "tableFrom": "knowledge_base_tag_definitions", + "tableTo": "knowledge_base", + "columnsFrom": ["knowledge_base_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.knowledge_connector": { + "name": "knowledge_connector", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "connector_type": { + "name": "connector_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_api_key": { + "name": "encrypted_api_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_config": { + "name": "source_config", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "sync_mode": { + "name": "sync_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'full'" + }, + "sync_interval_minutes": { + "name": "sync_interval_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1440 + }, + "access_mode": { + "name": "access_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'workspace'" + }, + "credential_group_id": { + "name": "credential_group_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credential_group_option_id": { + "name": "credential_group_option_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "member_sync_status": { + "name": "member_sync_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "member_sync_lock_token": { + "name": "member_sync_lock_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "member_sync_lock_lease_at": { + "name": "member_sync_lock_lease_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "next_member_sync_at": { + "name": "next_member_sync_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + }, + "last_member_sync_at": { + "name": "last_member_sync_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_member_sync_error": { + "name": "last_member_sync_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "member_sync_consecutive_failures": { + "name": "member_sync_consecutive_failures", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "access_rewrite_pending": { + "name": "access_rewrite_pending", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "member_tombstone_cursor": { + "name": "member_tombstone_cursor", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "member_resurrection_cursor": { + "name": "member_resurrection_cursor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_sync_error": { + "name": "last_sync_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_sync_doc_count": { + "name": "last_sync_doc_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "listing_checkpoint": { + "name": "listing_checkpoint", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "directory_checkpoint": { + "name": "directory_checkpoint", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "next_sync_at": { + "name": "next_sync_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + }, + "next_directory_sync_at": { + "name": "next_directory_sync_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "consecutive_failures": { + "name": "consecutive_failures", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sync_lock_token": { + "name": "sync_lock_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sync_lock_lease_at": { + "name": "sync_lock_lease_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "detached_at": { + "name": "detached_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "detach_reserved_bytes": { + "name": "detach_reserved_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "kc_knowledge_base_id_idx": { + "name": "kc_knowledge_base_id_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kc_status_next_sync_idx": { + "name": "kc_status_next_sync_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_sync_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kc_archived_at_partial_idx": { + "name": "kc_archived_at_partial_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"knowledge_connector\".\"archived_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "kc_deleted_at_partial_idx": { + "name": "kc_deleted_at_partial_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"knowledge_connector\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "kc_member_sync_due_idx": { + "name": "kc_member_sync_due_idx", + "columns": [ + { + "expression": "member_sync_status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_member_sync_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"knowledge_connector\".\"access_mode\" = 'members' AND \"knowledge_connector\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "kc_directory_sync_due_idx": { + "name": "kc_directory_sync_due_idx", + "columns": [ + { + "expression": "next_directory_sync_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"knowledge_connector\".\"access_mode\" = 'admin' AND \"knowledge_connector\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_connector_knowledge_base_id_knowledge_base_id_fk": { + "name": "knowledge_connector_knowledge_base_id_knowledge_base_id_fk", + "tableFrom": "knowledge_connector", + "tableTo": "knowledge_base", + "columnsFrom": ["knowledge_base_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_connector_credential_group_id_credential_group_id_fk": { + "name": "knowledge_connector_credential_group_id_credential_group_id_fk", + "tableFrom": "knowledge_connector", + "tableTo": "credential_group", + "columnsFrom": ["credential_group_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kc_access_mode_check": { + "name": "kc_access_mode_check", + "value": "\"knowledge_connector\".\"access_mode\" IN ('workspace', 'members', 'admin')" + }, + "kc_member_sync_status_check": { + "name": "kc_member_sync_status_check", + "value": "\"knowledge_connector\".\"member_sync_status\" IN ('idle', 'pending', 'running', 'error', 'disabled')" + }, + "kc_sync_lock_exclusive_check": { + "name": "kc_sync_lock_exclusive_check", + "value": "NOT (\"knowledge_connector\".\"sync_lock_token\" IS NOT NULL AND \"knowledge_connector\".\"member_sync_lock_token\" IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_connector_member": { + "name": "knowledge_connector_member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "connector_id": { + "name": "connector_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subject_token": { + "name": "subject_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "consecutive_failures": { + "name": "consecutive_failures", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_started_at": { + "name": "last_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_complete_listing_at": { + "name": "last_complete_listing_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_listed_count": { + "name": "last_listed_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "member_synced_through": { + "name": "member_synced_through", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope_renewed_at": { + "name": "scope_renewed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope_renewal_cursor": { + "name": "scope_renewal_cursor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scope_renewal_started_at": { + "name": "scope_renewal_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "change_cursor": { + "name": "change_cursor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "listing_checkpoint": { + "name": "listing_checkpoint", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "suspended_at": { + "name": "suspended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kcm_organization_id_idx": { + "name": "kcm_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kcm_connector_credential_unique": { + "name": "kcm_connector_credential_unique", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "credential_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kcm_connector_queue_idx": { + "name": "kcm_connector_queue_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "first" + }, + { + "expression": "last_started_at", + "isExpression": false, + "asc": true, + "nulls": "first" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kcm_credential_idx": { + "name": "kcm_credential_idx", + "columns": [ + { + "expression": "credential_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_connector_member_workspace_id_workspace_id_fk": { + "name": "knowledge_connector_member_workspace_id_workspace_id_fk", + "tableFrom": "knowledge_connector_member", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_connector_member_organization_id_organization_id_fk": { + "name": "knowledge_connector_member_organization_id_organization_id_fk", + "tableFrom": "knowledge_connector_member", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_connector_member_connector_id_knowledge_connector_id_fk": { + "name": "knowledge_connector_member_connector_id_knowledge_connector_id_fk", + "tableFrom": "knowledge_connector_member", + "tableTo": "knowledge_connector", + "columnsFrom": ["connector_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_connector_member_credential_id_credential_id_fk": { + "name": "knowledge_connector_member_credential_id_credential_id_fk", + "tableFrom": "knowledge_connector_member", + "tableTo": "credential", + "columnsFrom": ["credential_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kcm_owner_check": { + "name": "kcm_owner_check", + "value": "num_nonnulls(\"knowledge_connector_member\".\"workspace_id\", \"knowledge_connector_member\".\"organization_id\") = 1" + }, + "kcm_status_check": { + "name": "kcm_status_check", + "value": "\"knowledge_connector_member\".\"status\" IN ('active', 'suspended', 'disabled')" + }, + "kcm_subject_token_shape_check": { + "name": "kcm_subject_token_shape_check", + "value": "\"knowledge_connector_member\".\"subject_token\" ~ '^s:[^:]+:[^:]+:.+$'" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_connector_member_sync_log": { + "name": "knowledge_connector_member_sync_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "connector_id": { + "name": "connector_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "members_claimed": { + "name": "members_claimed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "members_completed": { + "name": "members_completed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "members_incomplete": { + "name": "members_incomplete", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "members_failed": { + "name": "members_failed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_failed": { + "name": "docs_failed", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "processing_dispatch_failed": { + "name": "processing_dispatch_failed", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "docs_listed": { + "name": "docs_listed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_added": { + "name": "docs_added", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_updated": { + "name": "docs_updated", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_unchanged": { + "name": "docs_unchanged", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_hydrated_once": { + "name": "docs_hydrated_once", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "observations_added": { + "name": "observations_added", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "observations_renewed": { + "name": "observations_renewed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "observations_removed": { + "name": "observations_removed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_tombstoned": { + "name": "docs_tombstoned", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_resurrected": { + "name": "docs_resurrected", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_purged": { + "name": "docs_purged", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "credentials_audited": { + "name": "credentials_audited", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "database_failure_class": { + "name": "database_failure_class", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "kcmsl_connector_started_at_idx": { + "name": "kcmsl_connector_started_at_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"started_at\" DESC", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kcmsl_started_at_partial_idx": { + "name": "kcmsl_started_at_partial_idx", + "columns": [ + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"knowledge_connector_member_sync_log\".\"status\" = 'started'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_connector_member_sync_log_connector_id_knowledge_connector_id_fk": { + "name": "knowledge_connector_member_sync_log_connector_id_knowledge_connector_id_fk", + "tableFrom": "knowledge_connector_member_sync_log", + "tableTo": "knowledge_connector", + "columnsFrom": ["connector_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kcmsl_status_check": { + "name": "kcmsl_status_check", + "value": "\"knowledge_connector_member_sync_log\".\"status\" IN ('started', 'partial', 'completed', 'failed')" + }, + "kcmsl_database_failure_class_check": { + "name": "kcmsl_database_failure_class_check", + "value": "\"knowledge_connector_member_sync_log\".\"database_failure_class\" IN ('capacity', 'conflict', 'connection')" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_connector_partition": { + "name": "knowledge_connector_partition", + "schema": "", + "columns": { + "connector_id": { + "name": "connector_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "partition_key": { + "name": "partition_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "generation_id": { + "name": "generation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "context": { + "name": "context", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "cursor": { + "name": "cursor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "retry_at": { + "name": "retry_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_served_at": { + "name": "last_served_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "failure": { + "name": "failure", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "permission_cursor": { + "name": "permission_cursor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "permission_attempts": { + "name": "permission_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "permission_retry_at": { + "name": "permission_retry_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "permission_last_served_at": { + "name": "permission_last_served_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "permission_started_at": { + "name": "permission_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "permission_failure": { + "name": "permission_failure", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "kcp_content_due_idx": { + "name": "kcp_content_due_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "generation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "retry_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_served_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kcp_permission_due_idx": { + "name": "kcp_permission_due_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "generation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "permission_retry_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "permission_last_served_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_connector_partition_connector_id_knowledge_connector_id_fk": { + "name": "knowledge_connector_partition_connector_id_knowledge_connector_id_fk", + "tableFrom": "knowledge_connector_partition", + "tableTo": "knowledge_connector", + "columnsFrom": ["connector_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "kcp_pk": { + "name": "kcp_pk", + "columns": ["connector_id", "partition_key"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kcp_partition_key_check": { + "name": "kcp_partition_key_check", + "value": "octet_length(\"knowledge_connector_partition\".\"partition_key\") BETWEEN 1 AND 1024" + }, + "kcp_context_check": { + "name": "kcp_context_check", + "value": "jsonb_typeof(\"knowledge_connector_partition\".\"context\") = 'object' AND octet_length(\"knowledge_connector_partition\".\"context\"::text) <= 16384" + }, + "kcp_status_check": { + "name": "kcp_status_check", + "value": "\"knowledge_connector_partition\".\"status\" IN ('pending', 'complete', 'blocked')" + }, + "kcp_cursor_check": { + "name": "kcp_cursor_check", + "value": "(\"knowledge_connector_partition\".\"cursor\" IS NULL OR octet_length(\"knowledge_connector_partition\".\"cursor\") <= 393216) AND (\"knowledge_connector_partition\".\"permission_cursor\" IS NULL OR octet_length(\"knowledge_connector_partition\".\"permission_cursor\") <= 393216)" + }, + "kcp_attempts_check": { + "name": "kcp_attempts_check", + "value": "\"knowledge_connector_partition\".\"attempts\" >= 0 AND \"knowledge_connector_partition\".\"permission_attempts\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_connector_permission_grant": { + "name": "knowledge_connector_permission_grant", + "schema": "", + "columns": { + "connector_id": { + "name": "connector_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "group_key": { + "name": "group_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subject_token": { + "name": "subject_token", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "kcpg_subject_idx": { + "name": "kcpg_subject_idx", + "columns": [ + { + "expression": "subject_token", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "group_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kcpg_snapshot_fk": { + "name": "kcpg_snapshot_fk", + "tableFrom": "knowledge_connector_permission_grant", + "tableTo": "knowledge_connector_permission_snapshot", + "columnsFrom": ["connector_id"], + "columnsTo": ["connector_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "kcpg_pk": { + "name": "kcpg_pk", + "columns": ["connector_id", "group_key", "subject_token"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kcpg_group_check": { + "name": "kcpg_group_check", + "value": "length(\"knowledge_connector_permission_grant\".\"group_key\") BETWEEN 1 AND 255" + }, + "kcpg_subject_check": { + "name": "kcpg_subject_check", + "value": "\"knowledge_connector_permission_grant\".\"subject_token\" ~ '^u:[^[:space:]A-Z]+@[^[:space:]A-Z]+$'" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_connector_permission_snapshot": { + "name": "knowledge_connector_permission_snapshot", + "schema": "", + "columns": { + "connector_id": { + "name": "connector_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "kcps_connector_fk": { + "name": "kcps_connector_fk", + "tableFrom": "knowledge_connector_permission_snapshot", + "tableTo": "knowledge_connector", + "columnsFrom": ["connector_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kcps_revision_check": { + "name": "kcps_revision_check", + "value": "\"knowledge_connector_permission_snapshot\".\"revision\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_connector_sync_log": { + "name": "knowledge_connector_sync_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "connector_id": { + "name": "connector_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "docs_added": { + "name": "docs_added", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_updated": { + "name": "docs_updated", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_deleted": { + "name": "docs_deleted", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_unchanged": { + "name": "docs_unchanged", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_skipped": { + "name": "docs_skipped", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_failed": { + "name": "docs_failed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "listed_count": { + "name": "listed_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "database_failure_class": { + "name": "database_failure_class", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "kcsl_connector_started_at_idx": { + "name": "kcsl_connector_started_at_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"started_at\" DESC", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kcsl_started_at_partial_idx": { + "name": "kcsl_started_at_partial_idx", + "columns": [ + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"knowledge_connector_sync_log\".\"status\" = 'started'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_connector_sync_log_connector_id_knowledge_connector_id_fk": { + "name": "knowledge_connector_sync_log_connector_id_knowledge_connector_id_fk", + "tableFrom": "knowledge_connector_sync_log", + "tableTo": "knowledge_connector", + "columnsFrom": ["connector_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kcsl_database_failure_class_check": { + "name": "kcsl_database_failure_class_check", + "value": "\"knowledge_connector_sync_log\".\"database_failure_class\" IN ('capacity', 'conflict', 'connection')" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_document_observation": { + "name": "knowledge_document_observation", + "schema": "", + "columns": { + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "member_id": { + "name": "member_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "run_id": { + "name": "run_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "kdo_member_idx": { + "name": "kdo_member_idx", + "columns": [ + { + "expression": "member_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_document_observation_document_id_document_id_fk": { + "name": "knowledge_document_observation_document_id_document_id_fk", + "tableFrom": "knowledge_document_observation", + "tableTo": "document", + "columnsFrom": ["document_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_document_observation_member_id_knowledge_connector_member_id_fk": { + "name": "knowledge_document_observation_member_id_knowledge_connector_member_id_fk", + "tableFrom": "knowledge_document_observation", + "tableTo": "knowledge_connector_member", + "columnsFrom": ["member_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "knowledge_document_observation_document_id_member_id_pk": { + "name": "knowledge_document_observation_document_id_member_id_pk", + "columns": ["document_id", "member_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.knowledge_external_directory": { + "name": "knowledge_external_directory", + "schema": "", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sync_lock_token": { + "name": "sync_lock_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sync_lock_lease_at": { + "name": "sync_lock_lease_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_started_at": { + "name": "last_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_complete_sync_at": { + "name": "last_complete_sync_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "ked_organization_id_idx": { + "name": "ked_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ked_workspace_identity_unique": { + "name": "ked_workspace_identity_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ked_organization_identity_unique": { + "name": "ked_organization_identity_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_external_directory_workspace_id_workspace_id_fk": { + "name": "knowledge_external_directory_workspace_id_workspace_id_fk", + "tableFrom": "knowledge_external_directory", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_external_directory_organization_id_organization_id_fk": { + "name": "knowledge_external_directory_organization_id_organization_id_fk", + "tableFrom": "knowledge_external_directory", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "ked_owner_check": { + "name": "ked_owner_check", + "value": "num_nonnulls(\"knowledge_external_directory\".\"workspace_id\", \"knowledge_external_directory\".\"organization_id\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_external_group": { + "name": "knowledge_external_group", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_group_id": { + "name": "external_group_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "keg_organization_id_idx": { + "name": "keg_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "keg_organization_identity_unique": { + "name": "keg_organization_identity_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "keg_organization_synced_idx": { + "name": "keg_organization_synced_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_synced_at", + "isExpression": false, + "asc": true, + "nulls": "first" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "keg_identity_unique": { + "name": "keg_identity_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "keg_workspace_synced_idx": { + "name": "keg_workspace_synced_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_synced_at", + "isExpression": false, + "asc": true, + "nulls": "first" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_external_group_organization_id_organization_id_fk": { + "name": "knowledge_external_group_organization_id_organization_id_fk", + "tableFrom": "knowledge_external_group", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "keg_workspace_fk": { + "name": "keg_workspace_fk", + "tableFrom": "knowledge_external_group", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "keg_owner_check": { + "name": "keg_owner_check", + "value": "num_nonnulls(\"knowledge_external_group\".\"workspace_id\", \"knowledge_external_group\".\"organization_id\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_external_group_member": { + "name": "knowledge_external_group_member", + "schema": "", + "columns": { + "group_id": { + "name": "group_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subject_token": { + "name": "subject_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kegm_subject_token_idx": { + "name": "kegm_subject_token_idx", + "columns": [ + { + "expression": "subject_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kegm_group_fk": { + "name": "kegm_group_fk", + "tableFrom": "knowledge_external_group_member", + "tableTo": "knowledge_external_group", + "columnsFrom": ["group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "knowledge_external_group_member_group_id_subject_token_pk": { + "name": "knowledge_external_group_member_group_id_subject_token_pk", + "columns": ["group_id", "subject_token"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.knowledge_projection_dirty": { + "name": "knowledge_projection_dirty", + "schema": "", + "columns": { + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "generation": { + "name": "generation", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "content": { + "name": "content", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "marked_at": { + "name": "marked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "knowledge_projection_dirty_marked_at_idx": { + "name": "knowledge_projection_dirty_marked_at_idx", + "columns": [ + { + "expression": "marked_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_projection_dirty_document_id_document_id_fk": { + "name": "knowledge_projection_dirty_document_id_document_id_fk", + "tableFrom": "knowledge_projection_dirty", + "tableTo": "document", + "columnsFrom": ["document_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mcp_server_oauth": { + "name": "mcp_server_oauth", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mcp_server_id": { + "name": "mcp_server_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_information": { + "name": "client_information", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tokens": { + "name": "tokens", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code_verifier": { + "name": "code_verifier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state_created_at": { + "name": "state_created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_refreshed_at": { + "name": "last_refreshed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "mcp_server_oauth_server_unique": { + "name": "mcp_server_oauth_server_unique", + "columns": [ + { + "expression": "mcp_server_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "mcp_server_oauth_state_idx": { + "name": "mcp_server_oauth_state_idx", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mcp_server_oauth_mcp_server_id_mcp_servers_id_fk": { + "name": "mcp_server_oauth_mcp_server_id_mcp_servers_id_fk", + "tableFrom": "mcp_server_oauth", + "tableTo": "mcp_servers", + "columnsFrom": ["mcp_server_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_server_oauth_user_id_user_id_fk": { + "name": "mcp_server_oauth_user_id_user_id_fk", + "tableFrom": "mcp_server_oauth", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "mcp_server_oauth_workspace_id_workspace_id_fk": { + "name": "mcp_server_oauth_workspace_id_workspace_id_fk", + "tableFrom": "mcp_server_oauth", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_server_oauth_organization_id_organization_id_fk": { + "name": "mcp_server_oauth_organization_id_organization_id_fk", + "tableFrom": "mcp_server_oauth", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "mcp_server_oauth_owner_check": { + "name": "mcp_server_oauth_owner_check", + "value": "num_nonnulls(\"mcp_server_oauth\".\"workspace_id\", \"mcp_server_oauth\".\"organization_id\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.mcp_servers": { + "name": "mcp_servers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credential_group_id": { + "name": "credential_group_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "managed_connector_id": { + "name": "managed_connector_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "oauth_config_version": { + "name": "oauth_config_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "transport": { + "name": "transport", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'headers'" + }, + "oauth_client_id": { + "name": "oauth_client_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "oauth_client_secret": { + "name": "oauth_client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "headers": { + "name": "headers", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "timeout": { + "name": "timeout", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 30000 + }, + "retries": { + "name": "retries", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "last_connected": { + "name": "last_connected", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "connection_status": { + "name": "connection_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'disconnected'" + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status_config": { + "name": "status_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "tool_count": { + "name": "tool_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "last_tools_refresh": { + "name": "last_tools_refresh", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "total_requests": { + "name": "total_requests", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "last_used": { + "name": "last_used", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "mcp_servers_organization_id_idx": { + "name": "mcp_servers_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "mcp_servers_workspace_enabled_idx": { + "name": "mcp_servers_workspace_enabled_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "mcp_servers_credential_group_idx": { + "name": "mcp_servers_credential_group_idx", + "columns": [ + { + "expression": "credential_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "mcp_servers_credential_group_managed_connector_unique": { + "name": "mcp_servers_credential_group_managed_connector_unique", + "columns": [ + { + "expression": "credential_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "managed_connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"mcp_servers\".\"credential_group_id\" IS NOT NULL AND \"mcp_servers\".\"managed_connector_id\" IS NOT NULL AND \"mcp_servers\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "mcp_servers_workspace_deleted_partial_idx": { + "name": "mcp_servers_workspace_deleted_partial_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"mcp_servers\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mcp_servers_workspace_id_workspace_id_fk": { + "name": "mcp_servers_workspace_id_workspace_id_fk", + "tableFrom": "mcp_servers", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_servers_organization_id_organization_id_fk": { + "name": "mcp_servers_organization_id_organization_id_fk", + "tableFrom": "mcp_servers", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_servers_credential_group_id_credential_group_id_fk": { + "name": "mcp_servers_credential_group_id_credential_group_id_fk", + "tableFrom": "mcp_servers", + "tableTo": "credential_group", + "columnsFrom": ["credential_group_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "mcp_servers_created_by_user_id_fk": { + "name": "mcp_servers_created_by_user_id_fk", + "tableFrom": "mcp_servers", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "mcp_servers_owner_check": { + "name": "mcp_servers_owner_check", + "value": "num_nonnulls(\"mcp_servers\".\"workspace_id\", \"mcp_servers\".\"organization_id\") = 1" + }, + "mcp_servers_organization_managed_check": { + "name": "mcp_servers_organization_managed_check", + "value": "\"mcp_servers\".\"organization_id\" IS NULL OR \"mcp_servers\".\"credential_group_id\" IS NOT NULL" + }, + "mcp_servers_credential_group_managed_connector_check": { + "name": "mcp_servers_credential_group_managed_connector_check", + "value": "\"mcp_servers\".\"credential_group_id\" IS NULL OR \"mcp_servers\".\"managed_connector_id\" IS NOT NULL" + }, + "mcp_servers_managed_connector_oauth_check": { + "name": "mcp_servers_managed_connector_oauth_check", + "value": "\"mcp_servers\".\"managed_connector_id\" IS NULL OR \"mcp_servers\".\"auth_type\" = 'oauth'" + } + }, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "member_user_id_unique": { + "name": "member_user_id_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "member_organization_id_idx": { + "name": "member_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "member_user_id_user_id_fk": { + "name": "member_user_id_user_id_fk", + "tableFrom": "member", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory": { + "name": "memory", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "storage_version": { + "name": "storage_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "encrypted_context_summary": { + "name": "encrypted_context_summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret_provenance_version": { + "name": "secret_provenance_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "memory_key_idx": { + "name": "memory_key_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "memory_workspace_idx": { + "name": "memory_workspace_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "memory_workspace_key_idx": { + "name": "memory_workspace_key_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "memory_workspace_deleted_partial_idx": { + "name": "memory_workspace_deleted_partial_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"memory\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "memory_workspace_id_workspace_id_fk": { + "name": "memory_workspace_id_workspace_id_fk", + "tableFrom": "memory", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory_artifact": { + "name": "memory_artifact", + "schema": "", + "columns": { + "memory_id": { + "name": "memory_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "memory_artifact_key_idx": { + "name": "memory_artifact_key_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "memory_artifact_memory_id_memory_id_fk": { + "name": "memory_artifact_memory_id_memory_id_fk", + "tableFrom": "memory_artifact", + "tableTo": "memory", + "columnsFrom": ["memory_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "memory_artifact_key_execution_large_values_key_fk": { + "name": "memory_artifact_key_execution_large_values_key_fk", + "tableFrom": "memory_artifact", + "tableTo": "execution_large_values", + "columnsFrom": ["key"], + "columnsTo": ["key"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "memory_artifact_memory_id_key_pk": { + "name": "memory_artifact_memory_id_key_pk", + "columns": ["memory_id", "key"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory_item": { + "name": "memory_item", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "memory_id": { + "name": "memory_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sequence": { + "name": "sequence", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "identity": { + "type": "always", + "name": "memory_item_sequence_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "append_key": { + "name": "append_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "turn_id": { + "name": "turn_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provenance_status": { + "name": "provenance_status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provenance_entries": { + "name": "provenance_entries", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "memory_item_append_unique": { + "name": "memory_item_append_unique", + "columns": [ + { + "expression": "memory_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "append_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "memory_item_sequence_idx": { + "name": "memory_item_sequence_idx", + "columns": [ + { + "expression": "memory_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sequence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "memory_item_memory_id_memory_id_fk": { + "name": "memory_item_memory_id_memory_id_fk", + "tableFrom": "memory_item", + "tableTo": "memory", + "columnsFrom": ["memory_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "memory_item_kind_check": { + "name": "memory_item_kind_check", + "value": "\"memory_item\".\"kind\" IN ('message', 'exchange')" + }, + "memory_item_provenance_status_check": { + "name": "memory_item_provenance_status_check", + "value": "\"memory_item\".\"provenance_status\" IN ('exact', 'unknown')" + } + }, + "isRLSEnabled": false + }, + "public.memory_secret_provenance": { + "name": "memory_secret_provenance", + "schema": "", + "columns": { + "memory_id": { + "name": "memory_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entries": { + "name": "entries", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "memory_secret_provenance_memory_id_memory_id_fk": { + "name": "memory_secret_provenance_memory_id_memory_id_fk", + "tableFrom": "memory_secret_provenance", + "tableTo": "memory", + "columnsFrom": ["memory_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "memory_secret_provenance_status_check": { + "name": "memory_secret_provenance_status_check", + "value": "\"memory_secret_provenance\".\"status\" IN ('exact', 'unknown')" + } + }, + "isRLSEnabled": false + }, + "public.mothership_inbox_allowed_sender": { + "name": "mothership_inbox_allowed_sender", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "added_by": { + "name": "added_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "inbox_sender_ws_email_idx": { + "name": "inbox_sender_ws_email_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mothership_inbox_allowed_sender_workspace_id_workspace_id_fk": { + "name": "mothership_inbox_allowed_sender_workspace_id_workspace_id_fk", + "tableFrom": "mothership_inbox_allowed_sender", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mothership_inbox_allowed_sender_added_by_user_id_fk": { + "name": "mothership_inbox_allowed_sender_added_by_user_id_fk", + "tableFrom": "mothership_inbox_allowed_sender", + "tableTo": "user", + "columnsFrom": ["added_by"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mothership_inbox_task": { + "name": "mothership_inbox_task", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "from_email": { + "name": "from_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "from_name": { + "name": "from_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body_preview": { + "name": "body_preview", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "body_text": { + "name": "body_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "body_html": { + "name": "body_html", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email_message_id": { + "name": "email_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "in_reply_to": { + "name": "in_reply_to", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "response_message_id": { + "name": "response_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agentmail_message_id": { + "name": "agentmail_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'received'" + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "trigger_job_id": { + "name": "trigger_job_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "result_summary": { + "name": "result_summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rejection_reason": { + "name": "rejection_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "has_attachments": { + "name": "has_attachments", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cc_recipients": { + "name": "cc_recipients", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "processing_started_at": { + "name": "processing_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "inbox_task_ws_created_at_idx": { + "name": "inbox_task_ws_created_at_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inbox_task_ws_status_idx": { + "name": "inbox_task_ws_status_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inbox_task_response_msg_id_idx": { + "name": "inbox_task_response_msg_id_idx", + "columns": [ + { + "expression": "response_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inbox_task_email_msg_id_idx": { + "name": "inbox_task_email_msg_id_idx", + "columns": [ + { + "expression": "email_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mothership_inbox_task_workspace_id_workspace_id_fk": { + "name": "mothership_inbox_task_workspace_id_workspace_id_fk", + "tableFrom": "mothership_inbox_task", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mothership_inbox_task_chat_id_copilot_chats_id_fk": { + "name": "mothership_inbox_task_chat_id_copilot_chats_id_fk", + "tableFrom": "mothership_inbox_task", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mothership_inbox_webhook": { + "name": "mothership_inbox_webhook", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "webhook_id": { + "name": "webhook_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "mothership_inbox_webhook_workspace_id_workspace_id_fk": { + "name": "mothership_inbox_webhook_workspace_id_workspace_id_fk", + "tableFrom": "mothership_inbox_webhook", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mothership_inbox_webhook_workspace_id_unique": { + "name": "mothership_inbox_webhook_workspace_id_unique", + "nullsNotDistinct": false, + "columns": ["workspace_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mothership_resource_effects": { + "name": "mothership_resource_effects", + "schema": "", + "columns": { + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "effect_id": { + "name": "effect_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "mothership_resource_effects_chat_id_copilot_chats_id_fk": { + "name": "mothership_resource_effects_chat_id_copilot_chats_id_fk", + "tableFrom": "mothership_resource_effects", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "mothership_resource_effects_chat_id_effect_id_pk": { + "name": "mothership_resource_effects_chat_id_effect_id_pk", + "columns": ["chat_id", "effect_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mothership_settings": { + "name": "mothership_settings", + "schema": "", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mcp_tool_refs": { + "name": "mcp_tool_refs", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "custom_tool_refs": { + "name": "custom_tool_refs", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "skill_refs": { + "name": "skill_refs", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "mothership_settings_workspace_id_workspace_id_fk": { + "name": "mothership_settings_workspace_id_workspace_id_fk", + "tableFrom": "mothership_settings", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_access_token": { + "name": "oauth_access_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_id": { + "name": "refresh_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "resource": { + "name": "resource", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "oauth_access_token_client_id_idx": { + "name": "oauth_access_token_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_access_token_session_id_idx": { + "name": "oauth_access_token_session_id_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_access_token_refresh_id_idx": { + "name": "oauth_access_token_refresh_id_idx", + "columns": [ + { + "expression": "refresh_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_access_token_user_client_idx": { + "name": "oauth_access_token_user_client_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_access_token_expires_at_idx": { + "name": "oauth_access_token_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_access_token_client_id_oauth_client_client_id_fk": { + "name": "oauth_access_token_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "oauth_client", + "columnsFrom": ["client_id"], + "columnsTo": ["client_id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_access_token_session_id_session_id_fk": { + "name": "oauth_access_token_session_id_session_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "oauth_access_token_user_id_user_id_fk": { + "name": "oauth_access_token_user_id_user_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_access_token_refresh_id_oauth_refresh_token_id_fk": { + "name": "oauth_access_token_refresh_id_oauth_refresh_token_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "oauth_refresh_token", + "columnsFrom": ["refresh_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_access_token_token_unique": { + "name": "oauth_access_token_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": { + "oauth_access_token_search_resource_check": { + "name": "oauth_access_token_search_resource_check", + "value": "NOT ('search:read' = ANY(\"oauth_access_token\".\"scopes\")) OR \"oauth_access_token\".\"resource\" IS NOT NULL" + } + }, + "isRLSEnabled": false + }, + "public.oauth_client": { + "name": "oauth_client", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "skip_consent": { + "name": "skip_consent", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "enable_end_session": { + "name": "enable_end_session", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subject_type": { + "name": "subject_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contacts": { + "name": "contacts", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "tos": { + "name": "tos", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "policy": { + "name": "policy", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_id": { + "name": "software_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_version": { + "name": "software_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_statement": { + "name": "software_statement", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uris": { + "name": "redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "post_logout_redirect_uris": { + "name": "post_logout_redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "token_endpoint_auth_method": { + "name": "token_endpoint_auth_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "grant_types": { + "name": "grant_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "response_types": { + "name": "response_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "public": { + "name": "public", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "require_pkce": { + "name": "require_pkce", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "oauth_client_user_id_idx": { + "name": "oauth_client_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_client_user_id_user_id_fk": { + "name": "oauth_client_user_id_user_id_fk", + "tableFrom": "oauth_client", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_client_client_id_unique": { + "name": "oauth_client_client_id_unique", + "nullsNotDistinct": false, + "columns": ["client_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_consent": { + "name": "oauth_consent", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "oauth_consent_client_id_idx": { + "name": "oauth_consent_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_consent_client_id_oauth_client_client_id_fk": { + "name": "oauth_consent_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "oauth_client", + "columnsFrom": ["client_id"], + "columnsTo": ["client_id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_consent_user_id_user_id_fk": { + "name": "oauth_consent_user_id_user_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_consent_user_client_reference_unique": { + "name": "oauth_consent_user_client_reference_unique", + "nullsNotDistinct": true, + "columns": ["user_id", "client_id", "reference_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_refresh_token": { + "name": "oauth_refresh_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "revoked": { + "name": "revoked", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "auth_time": { + "name": "auth_time", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "resource": { + "name": "resource", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "family_id": { + "name": "family_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "generation": { + "name": "generation", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "oauth_refresh_token_client_id_idx": { + "name": "oauth_refresh_token_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_refresh_token_session_id_idx": { + "name": "oauth_refresh_token_session_id_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_refresh_token_user_client_idx": { + "name": "oauth_refresh_token_user_client_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_refresh_token_expires_at_idx": { + "name": "oauth_refresh_token_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_refresh_token_client_id_oauth_client_client_id_fk": { + "name": "oauth_refresh_token_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "oauth_client", + "columnsFrom": ["client_id"], + "columnsTo": ["client_id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_refresh_token_session_id_session_id_fk": { + "name": "oauth_refresh_token_session_id_session_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "oauth_refresh_token_user_id_user_id_fk": { + "name": "oauth_refresh_token_user_id_user_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_refresh_token_family_id_oauth_token_family_id_fk": { + "name": "oauth_refresh_token_family_id_oauth_token_family_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "oauth_token_family", + "columnsFrom": ["family_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_refresh_token_token_unique": { + "name": "oauth_refresh_token_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + }, + "oauth_refresh_token_family_generation_unique": { + "name": "oauth_refresh_token_family_generation_unique", + "nullsNotDistinct": false, + "columns": ["family_id", "generation"] + } + }, + "policies": {}, + "checkConstraints": { + "oauth_refresh_token_generation_check": { + "name": "oauth_refresh_token_generation_check", + "value": "\"oauth_refresh_token\".\"generation\" BETWEEN 0 AND 1000" + }, + "oauth_refresh_token_search_resource_check": { + "name": "oauth_refresh_token_search_resource_check", + "value": "NOT ('search:read' = ANY(\"oauth_refresh_token\".\"scopes\")) OR \"oauth_refresh_token\".\"resource\" IS NOT NULL" + } + }, + "isRLSEnabled": false + }, + "public.oauth_token_family": { + "name": "oauth_token_family", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "consent_id": { + "name": "consent_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "current_generation": { + "name": "current_generation", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "oauth_token_family_client_id_idx": { + "name": "oauth_token_family_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_token_family_session_id_idx": { + "name": "oauth_token_family_session_id_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_token_family_user_client_idx": { + "name": "oauth_token_family_user_client_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_token_family_consent_id_idx": { + "name": "oauth_token_family_consent_id_idx", + "columns": [ + { + "expression": "consent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_token_family_expires_at_idx": { + "name": "oauth_token_family_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_token_family_client_id_oauth_client_client_id_fk": { + "name": "oauth_token_family_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_token_family", + "tableTo": "oauth_client", + "columnsFrom": ["client_id"], + "columnsTo": ["client_id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_token_family_session_id_session_id_fk": { + "name": "oauth_token_family_session_id_session_id_fk", + "tableFrom": "oauth_token_family", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "oauth_token_family_user_id_user_id_fk": { + "name": "oauth_token_family_user_id_user_id_fk", + "tableFrom": "oauth_token_family", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_token_family_consent_id_oauth_consent_id_fk": { + "name": "oauth_token_family_consent_id_oauth_consent_id_fk", + "tableFrom": "oauth_token_family", + "tableTo": "oauth_consent", + "columnsFrom": ["consent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "oauth_token_family_generation_check": { + "name": "oauth_token_family_generation_check", + "value": "\"oauth_token_family\".\"current_generation\" BETWEEN 0 AND 1000" + } + }, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "session_policy_settings": { + "name": "session_policy_settings", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "security_policy_version": { + "name": "security_policy_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "require_sso": { + "name": "require_sso", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "whitelabel_settings": { + "name": "whitelabel_settings", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "data_retention_settings": { + "name": "data_retention_settings", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "org_usage_limit": { + "name": "org_usage_limit", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "storage_used_bytes": { + "name": "storage_used_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "limit_notifications": { + "name": "limit_notifications", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "credit_balance": { + "name": "credit_balance", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_access_request_settings": { + "name": "organization_access_request_settings", + "schema": "", + "columns": { + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "allow_requests": { + "name": "allow_requests", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "organization_access_request_settings_organization_id_organization_id_fk": { + "name": "organization_access_request_settings_organization_id_organization_id_fk", + "tableFrom": "organization_access_request_settings", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_access_request_settings_updated_by_user_id_fk": { + "name": "organization_access_request_settings_updated_by_user_id_fk", + "tableFrom": "organization_access_request_settings", + "tableTo": "user", + "columnsFrom": ["updated_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_byok_keys": { + "name": "organization_byok_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_api_key": { + "name": "encrypted_api_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organization_byok_organization_provider_idx": { + "name": "organization_byok_organization_provider_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_byok_keys_organization_id_organization_id_fk": { + "name": "organization_byok_keys_organization_id_organization_id_fk", + "tableFrom": "organization_byok_keys", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_byok_keys_created_by_user_id_fk": { + "name": "organization_byok_keys_created_by_user_id_fk", + "tableFrom": "organization_byok_keys", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_member_usage_limit": { + "name": "organization_member_usage_limit", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "usage_limit": { + "name": "usage_limit", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "set_by": { + "name": "set_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "org_member_usage_limit_org_user_unique": { + "name": "org_member_usage_limit_org_user_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "org_member_usage_limit_organization_id_idx": { + "name": "org_member_usage_limit_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_member_usage_limit_organization_id_organization_id_fk": { + "name": "organization_member_usage_limit_organization_id_organization_id_fk", + "tableFrom": "organization_member_usage_limit", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_member_usage_limit_user_id_user_id_fk": { + "name": "organization_member_usage_limit_user_id_user_id_fk", + "tableFrom": "organization_member_usage_limit", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_member_usage_limit_set_by_user_id_fk": { + "name": "organization_member_usage_limit_set_by_user_id_fk", + "tableFrom": "organization_member_usage_limit", + "tableTo": "user", + "columnsFrom": ["set_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_search_history": { + "name": "organization_search_history", + "schema": "", + "columns": { + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sources": { + "name": "sources", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "queries": { + "name": "queries", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + } + }, + "indexes": { + "organization_search_history_user_idx": { + "name": "organization_search_history_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_search_history_organization_id_organization_id_fk": { + "name": "organization_search_history_organization_id_organization_id_fk", + "tableFrom": "organization_search_history", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_search_history_user_id_user_id_fk": { + "name": "organization_search_history_user_id_user_id_fk", + "tableFrom": "organization_search_history", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "organization_search_history_organization_id_user_id_pk": { + "name": "organization_search_history_organization_id_user_id_pk", + "columns": ["organization_id", "user_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_search_integration": { + "name": "organization_search_integration", + "schema": "", + "columns": { + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "connector_type": { + "name": "connector_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "approved": { + "name": "approved", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "organization_search_integration_organization_id_organization_id_fk": { + "name": "organization_search_integration_organization_id_organization_id_fk", + "tableFrom": "organization_search_integration", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "organization_search_integration_organization_id_connector_type_pk": { + "name": "organization_search_integration_organization_id_connector_type_pk", + "columns": ["organization_id", "connector_type"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_search_invocation": { + "name": "organization_search_invocation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "surface": { + "name": "surface", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_types": { + "name": "source_types", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "result_count": { + "name": "result_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organization_search_invocation_org_created_idx": { + "name": "organization_search_invocation_org_created_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "organization_search_invocation_user_idx": { + "name": "organization_search_invocation_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_search_invocation_organization_id_organization_id_fk": { + "name": "organization_search_invocation_organization_id_organization_id_fk", + "tableFrom": "organization_search_invocation", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_search_invocation_user_id_user_id_fk": { + "name": "organization_search_invocation_user_id_user_id_fk", + "tableFrom": "organization_search_invocation", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "organization_search_invocation_result_count_bounds": { + "name": "organization_search_invocation_result_count_bounds", + "value": "\"organization_search_invocation\".\"result_count\" BETWEEN 0 AND 100" + }, + "organization_search_invocation_source_types_bounds": { + "name": "organization_search_invocation_source_types_bounds", + "value": "cardinality(\"organization_search_invocation\".\"source_types\") <= 100" + } + }, + "isRLSEnabled": false + }, + "public.organization_search_mcp_invocation": { + "name": "organization_search_mcp_invocation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auth_kind": { + "name": "auth_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "oauth_client_id": { + "name": "oauth_client_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_name": { + "name": "client_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tool_name": { + "name": "tool_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "duration_ms": { + "name": "duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organization_search_mcp_invocation_org_created_idx": { + "name": "organization_search_mcp_invocation_org_created_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "organization_search_mcp_invocation_user_idx": { + "name": "organization_search_mcp_invocation_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "org_search_mcp_invocation_org_fk": { + "name": "org_search_mcp_invocation_org_fk", + "tableFrom": "organization_search_mcp_invocation", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "org_search_mcp_invocation_user_fk": { + "name": "org_search_mcp_invocation_user_fk", + "tableFrom": "organization_search_mcp_invocation", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "organization_search_mcp_invocation_tool_check": { + "name": "organization_search_mcp_invocation_tool_check", + "value": "\"organization_search_mcp_invocation\".\"tool_name\" IN ('search', 'read_document', 'chat')" + }, + "organization_search_mcp_invocation_outcome_check": { + "name": "organization_search_mcp_invocation_outcome_check", + "value": "\"organization_search_mcp_invocation\".\"outcome\" IN ('success', 'error', 'cancelled', 'rate_limited')" + }, + "organization_search_mcp_invocation_duration_check": { + "name": "organization_search_mcp_invocation_duration_check", + "value": "\"organization_search_mcp_invocation\".\"duration_ms\" >= 0" + }, + "organization_search_mcp_invocation_client_name_check": { + "name": "organization_search_mcp_invocation_client_name_check", + "value": "length(\"organization_search_mcp_invocation\".\"client_name\") <= 256" + }, + "organization_search_mcp_invocation_auth_check": { + "name": "organization_search_mcp_invocation_auth_check", + "value": "(\"organization_search_mcp_invocation\".\"auth_kind\" = 'oauth_access_token' AND \"organization_search_mcp_invocation\".\"oauth_client_id\" IS NOT NULL)\n OR (\"organization_search_mcp_invocation\".\"auth_kind\" IN ('personal_api_key', 'workspace_api_key') AND \"organization_search_mcp_invocation\".\"oauth_client_id\" IS NULL AND \"organization_search_mcp_invocation\".\"client_name\" IS NULL)" + } + }, + "isRLSEnabled": false + }, + "public.organization_secret": { + "name": "organization_secret", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "source_id": { + "name": "source_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_user_id": { + "name": "owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_value": { + "name": "encrypted_value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organization_secret_shared_unique": { + "name": "organization_secret_shared_unique", + "columns": [ + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"organization_secret\".\"owner_user_id\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "organization_secret_member_unique": { + "name": "organization_secret_member_unique", + "columns": [ + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"organization_secret\".\"owner_user_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "organization_secret_owner_idx": { + "name": "organization_secret_owner_idx", + "columns": [ + { + "expression": "owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_secret_source_id_organization_secret_source_id_fk": { + "name": "organization_secret_source_id_organization_secret_source_id_fk", + "tableFrom": "organization_secret", + "tableTo": "organization_secret_source", + "columnsFrom": ["source_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_secret_owner_user_id_user_id_fk": { + "name": "organization_secret_owner_user_id_user_id_fk", + "tableFrom": "organization_secret", + "tableTo": "user", + "columnsFrom": ["owner_user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_secret_source": { + "name": "organization_secret_source", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organization_secret_source_org_unique": { + "name": "organization_secret_source_org_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_secret_source_organization_id_organization_id_fk": { + "name": "organization_secret_source_organization_id_organization_id_fk", + "tableFrom": "organization_secret_source", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "organization_secret_source_mode_check": { + "name": "organization_secret_source_mode_check", + "value": "\"organization_secret_source\".\"mode\" IN ('organization', 'member')" + } + }, + "isRLSEnabled": false + }, + "public.outbox_event": { + "name": "outbox_event", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "max_attempts": { + "name": "max_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 10 + }, + "available_at": { + "name": "available_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "locked_at": { + "name": "locked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "outbox_event_status_available_idx": { + "name": "outbox_event_status_available_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "available_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "outbox_event_pending_type_available_idx": { + "name": "outbox_event_pending_type_available_idx", + "columns": [ + { + "expression": "event_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "available_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"outbox_event\".\"status\" = 'pending'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "outbox_event_locked_at_idx": { + "name": "outbox_event_locked_at_idx", + "columns": [ + { + "expression": "locked_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "outbox_event_type_created_idx": { + "name": "outbox_event_type_created_idx", + "columns": [ + { + "expression": "event_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.paused_executions": { + "name": "paused_executions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_snapshot": { + "name": "execution_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "pause_points": { + "name": "pause_points", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "total_pause_count": { + "name": "total_pause_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "resumed_count": { + "name": "resumed_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "automatic_resume_retry_count": { + "name": "automatic_resume_retry_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'paused'" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "paused_at": { + "name": "paused_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "next_resume_at": { + "name": "next_resume_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "paused_executions_workflow_id_idx": { + "name": "paused_executions_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "paused_executions_status_idx": { + "name": "paused_executions_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "paused_executions_execution_id_unique": { + "name": "paused_executions_execution_id_unique", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "paused_executions_next_resume_at_idx": { + "name": "paused_executions_next_resume_at_idx", + "columns": [ + { + "expression": "next_resume_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "status = 'paused' AND next_resume_at IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "paused_executions_workflow_id_workflow_id_fk": { + "name": "paused_executions_workflow_id_workflow_id_fk", + "tableFrom": "paused_executions", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pending_credential_draft": { + "name": "pending_credential_draft", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "oauth_config": { + "name": "oauth_config", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pending_draft_organization_id_idx": { + "name": "pending_draft_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pending_draft_user_provider_org": { + "name": "pending_draft_user_provider_org", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pending_draft_user_provider_ws": { + "name": "pending_draft_user_provider_ws", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pending_credential_draft_user_id_user_id_fk": { + "name": "pending_credential_draft_user_id_user_id_fk", + "tableFrom": "pending_credential_draft", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pending_credential_draft_workspace_id_workspace_id_fk": { + "name": "pending_credential_draft_workspace_id_workspace_id_fk", + "tableFrom": "pending_credential_draft", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pending_credential_draft_organization_id_organization_id_fk": { + "name": "pending_credential_draft_organization_id_organization_id_fk", + "tableFrom": "pending_credential_draft", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pending_credential_draft_credential_id_credential_id_fk": { + "name": "pending_credential_draft_credential_id_credential_id_fk", + "tableFrom": "pending_credential_draft", + "tableTo": "credential", + "columnsFrom": ["credential_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "pending_draft_owner_check": { + "name": "pending_draft_owner_check", + "value": "num_nonnulls(\"pending_credential_draft\".\"workspace_id\", \"pending_credential_draft\".\"organization_id\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.permission_access_request": { + "name": "permission_access_request", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requester_id": { + "name": "requester_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scope_key": { + "name": "scope_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_key": { + "name": "target_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target": { + "name": "target", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "target_label": { + "name": "target_label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "membership_id": { + "name": "membership_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "group_id": { + "name": "group_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "decision_reason": { + "name": "decision_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "decided_by": { + "name": "decided_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "decision": { + "name": "decision", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "decided_at": { + "name": "decided_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "permission_access_request_pending_unique": { + "name": "permission_access_request_pending_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "requester_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"permission_access_request\".\"status\" = 'pending'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "permission_access_request_org_queue_idx": { + "name": "permission_access_request_org_queue_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permission_access_request_requester_idx": { + "name": "permission_access_request_requester_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "requester_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "permission_access_request_organization_id_organization_id_fk": { + "name": "permission_access_request_organization_id_organization_id_fk", + "tableFrom": "permission_access_request", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "permission_access_request_requester_id_user_id_fk": { + "name": "permission_access_request_requester_id_user_id_fk", + "tableFrom": "permission_access_request", + "tableTo": "user", + "columnsFrom": ["requester_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "permission_access_request_decided_by_user_id_fk": { + "name": "permission_access_request_decided_by_user_id_fk", + "tableFrom": "permission_access_request", + "tableTo": "user", + "columnsFrom": ["decided_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "permission_access_request_status_check": { + "name": "permission_access_request_status_check", + "value": "\"permission_access_request\".\"status\" in ('pending', 'fulfilled', 'declined', 'cancelled', 'closed')" + } + }, + "isRLSEnabled": false + }, + "public.permission_group": { + "name": "permission_group", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "membership_mode": { + "name": "membership_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'inherit'" + } + }, + "indexes": { + "permission_group_created_by_idx": { + "name": "permission_group_created_by_idx", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permission_group_organization_name_unique": { + "name": "permission_group_organization_name_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permission_group_organization_default_unique": { + "name": "permission_group_organization_default_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "is_default = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "permission_group_organization_id_organization_id_fk": { + "name": "permission_group_organization_id_organization_id_fk", + "tableFrom": "permission_group", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "permission_group_created_by_user_id_fk": { + "name": "permission_group_created_by_user_id_fk", + "tableFrom": "permission_group", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.permission_group_member": { + "name": "permission_group_member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "permission_group_id": { + "name": "permission_group_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "assigned_by": { + "name": "assigned_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assigned_at": { + "name": "assigned_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "permission_group_member_group_id_idx": { + "name": "permission_group_member_group_id_idx", + "columns": [ + { + "expression": "permission_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permission_group_member_group_user_unique": { + "name": "permission_group_member_group_user_unique", + "columns": [ + { + "expression": "permission_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permission_group_member_organization_user_idx": { + "name": "permission_group_member_organization_user_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "permission_group_member_permission_group_id_permission_group_id_fk": { + "name": "permission_group_member_permission_group_id_permission_group_id_fk", + "tableFrom": "permission_group_member", + "tableTo": "permission_group", + "columnsFrom": ["permission_group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "permission_group_member_organization_id_organization_id_fk": { + "name": "permission_group_member_organization_id_organization_id_fk", + "tableFrom": "permission_group_member", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "permission_group_member_user_id_user_id_fk": { + "name": "permission_group_member_user_id_user_id_fk", + "tableFrom": "permission_group_member", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "permission_group_member_assigned_by_user_id_fk": { + "name": "permission_group_member_assigned_by_user_id_fk", + "tableFrom": "permission_group_member", + "tableTo": "user", + "columnsFrom": ["assigned_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.permission_group_workspace": { + "name": "permission_group_workspace", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "permission_group_id": { + "name": "permission_group_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "permission_group_workspace_workspace_id_idx": { + "name": "permission_group_workspace_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permission_group_workspace_group_workspace_unique": { + "name": "permission_group_workspace_group_workspace_unique", + "columns": [ + { + "expression": "permission_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "permission_group_workspace_permission_group_id_permission_group_id_fk": { + "name": "permission_group_workspace_permission_group_id_permission_group_id_fk", + "tableFrom": "permission_group_workspace", + "tableTo": "permission_group", + "columnsFrom": ["permission_group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "permission_group_workspace_workspace_id_workspace_id_fk": { + "name": "permission_group_workspace_workspace_id_workspace_id_fk", + "tableFrom": "permission_group_workspace", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "permission_group_workspace_organization_id_organization_id_fk": { + "name": "permission_group_workspace_organization_id_organization_id_fk", + "tableFrom": "permission_group_workspace", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.permissions": { + "name": "permissions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission_type": { + "name": "permission_type", + "type": "permission_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "permissions_user_id_idx": { + "name": "permissions_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permissions_entity_idx": { + "name": "permissions_entity_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permissions_user_entity_type_idx": { + "name": "permissions_user_entity_type_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permissions_user_entity_permission_idx": { + "name": "permissions_user_entity_permission_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "permission_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permissions_unique_constraint": { + "name": "permissions_unique_constraint", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "permissions_user_id_user_id_fk": { + "name": "permissions_user_id_user_id_fk", + "tableFrom": "permissions", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pinned_item": { + "name": "pinned_item", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pinned_at": { + "name": "pinned_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pinned_item_user_workspace_idx": { + "name": "pinned_item_user_workspace_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pinned_item_resource_idx": { + "name": "pinned_item_resource_idx", + "columns": [ + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pinned_item_user_resource_unique": { + "name": "pinned_item_user_resource_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pinned_item_user_id_user_id_fk": { + "name": "pinned_item_user_id_user_id_fk", + "tableFrom": "pinned_item", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pinned_item_workspace_id_workspace_id_fk": { + "name": "pinned_item_workspace_id_workspace_id_fk", + "tableFrom": "pinned_item", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "project_organization_archive_id_idx": { + "name": "project_organization_archive_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "project_owner_archive_id_idx": { + "name": "project_owner_archive_id_idx", + "columns": [ + { + "expression": "owner_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "project_organization_id_organization_id_fk": { + "name": "project_organization_id_organization_id_fk", + "tableFrom": "project", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "project_owner_id_user_id_fk": { + "name": "project_owner_id_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": ["owner_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "project_name_length": { + "name": "project_name_length", + "value": "char_length(btrim(\"project\".\"name\")) BETWEEN 1 AND 100" + } + }, + "isRLSEnabled": false + }, + "public.project_workspace": { + "name": "project_workspace", + "schema": "", + "columns": { + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "project_workspace_workspace_id_unique": { + "name": "project_workspace_workspace_id_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "project_workspace_project_id_project_id_fk": { + "name": "project_workspace_project_id_project_id_fk", + "tableFrom": "project_workspace", + "tableTo": "project", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "project_workspace_workspace_id_workspace_id_fk": { + "name": "project_workspace_workspace_id_workspace_id_fk", + "tableFrom": "project_workspace", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "project_workspace_project_id_workspace_id_pk": { + "name": "project_workspace_project_id_workspace_id_pk", + "columns": ["project_id", "workspace_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.public_share": { + "name": "public_share", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'public'" + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allowed_emails": { + "name": "allowed_emails", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'[]'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "public_share_token_unique": { + "name": "public_share_token_unique", + "columns": [ + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "public_share_resource_unique": { + "name": "public_share_resource_unique", + "columns": [ + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "public_share_resource_id_idx": { + "name": "public_share_resource_id_idx", + "columns": [ + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "public_share_workspace_id_idx": { + "name": "public_share_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "public_share_workspace_id_workspace_id_fk": { + "name": "public_share_workspace_id_workspace_id_fk", + "tableFrom": "public_share", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "public_share_created_by_user_id_fk": { + "name": "public_share_created_by_user_id_fk", + "tableFrom": "public_share", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rate_limit_bucket": { + "name": "rate_limit_bucket", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "tokens": { + "name": "tokens", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "last_refill_at": { + "name": "last_refill_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "blocked_until": { + "name": "blocked_until", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "capacity_state": { + "name": "capacity_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.resource_policy": { + "name": "resource_policy", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "document": { + "name": "document", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "resource_policy_organization_id_idx": { + "name": "resource_policy_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "resource_policy_resource_unique": { + "name": "resource_policy_resource_unique", + "columns": [ + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "resource_policy_workspace_id_idx": { + "name": "resource_policy_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "resource_policy_workspace_id_workspace_id_fk": { + "name": "resource_policy_workspace_id_workspace_id_fk", + "tableFrom": "resource_policy", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "resource_policy_organization_id_organization_id_fk": { + "name": "resource_policy_organization_id_organization_id_fk", + "tableFrom": "resource_policy", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "resource_policy_created_by_user_id_fk": { + "name": "resource_policy_created_by_user_id_fk", + "tableFrom": "resource_policy", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "resource_policy_updated_by_user_id_fk": { + "name": "resource_policy_updated_by_user_id_fk", + "tableFrom": "resource_policy", + "tableTo": "user", + "columnsFrom": ["updated_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "resource_policy_owner_check": { + "name": "resource_policy_owner_check", + "value": "num_nonnulls(\"resource_policy\".\"workspace_id\", \"resource_policy\".\"organization_id\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.resume_queue": { + "name": "resume_queue", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "paused_execution_id": { + "name": "paused_execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_execution_id": { + "name": "parent_execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "new_execution_id": { + "name": "new_execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "context_id": { + "name": "context_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resume_input": { + "name": "resume_input", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "queued_at": { + "name": "queued_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "resume_queue_parent_status_idx": { + "name": "resume_queue_parent_status_idx", + "columns": [ + { + "expression": "parent_execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "queued_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "resume_queue_new_execution_idx": { + "name": "resume_queue_new_execution_idx", + "columns": [ + { + "expression": "new_execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "resume_queue_paused_execution_id_paused_executions_id_fk": { + "name": "resume_queue_paused_execution_id_paused_executions_id_fk", + "tableFrom": "resume_queue", + "tableTo": "paused_executions", + "columnsFrom": ["paused_execution_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sandbox_image": { + "name": "sandbox_image", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "spec_hash": { + "name": "spec_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "spec": { + "name": "spec", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "sandbox_image_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "image_ref": { + "name": "image_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_image_id": { + "name": "provider_image_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "build_id": { + "name": "build_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "materialization_generation": { + "name": "materialization_generation", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_detail": { + "name": "error_detail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sandbox_image_provider_spec_unique": { + "name": "sandbox_image_provider_spec_unique", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "spec_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sandbox_image_status_idx": { + "name": "sandbox_image_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sandbox_image_last_used_idx": { + "name": "sandbox_image_last_used_idx", + "columns": [ + { + "expression": "last_used_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scim_connection": { + "name": "scim_connection", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "settings": { + "name": "settings", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "last_request_at": { + "name": "last_request_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "reconcile_lock_token": { + "name": "reconcile_lock_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reconcile_lease_at": { + "name": "reconcile_lease_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "reconciled_at": { + "name": "reconciled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scim_connection_organization_unique": { + "name": "scim_connection_organization_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_connection_reconcile_due_idx": { + "name": "scim_connection_reconcile_due_idx", + "columns": [ + { + "expression": "reconciled_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scim_connection_organization_id_organization_id_fk": { + "name": "scim_connection_organization_id_organization_id_fk", + "tableFrom": "scim_connection", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scim_connection_created_by_user_id_fk": { + "name": "scim_connection_created_by_user_id_fk", + "tableFrom": "scim_connection", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scim_credential": { + "name": "scim_credential", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_prefix": { + "name": "token_prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scopes": { + "name": "scopes", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "revoked_by": { + "name": "revoked_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scim_credential_token_hash_unique": { + "name": "scim_credential_token_hash_unique", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_credential_connection_idx": { + "name": "scim_credential_connection_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scim_credential_connection_id_scim_connection_id_fk": { + "name": "scim_credential_connection_id_scim_connection_id_fk", + "tableFrom": "scim_credential", + "tableTo": "scim_connection", + "columnsFrom": ["connection_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scim_credential_revoked_by_user_id_fk": { + "name": "scim_credential_revoked_by_user_id_fk", + "tableFrom": "scim_credential", + "tableTo": "user", + "columnsFrom": ["revoked_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "scim_credential_created_by_user_id_fk": { + "name": "scim_credential_created_by_user_id_fk", + "tableFrom": "scim_credential", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scim_group": { + "name": "scim_group", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name_key": { + "name": "display_name_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "order_key": { + "name": "order_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scim_group_connection_display_name_unique": { + "name": "scim_group_connection_display_name_unique", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "display_name_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_group_connection_external_id_unique": { + "name": "scim_group_connection_external_id_unique", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "external_id is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_group_connection_order_idx": { + "name": "scim_group_connection_order_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "order_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scim_group_connection_id_scim_connection_id_fk": { + "name": "scim_group_connection_id_scim_connection_id_fk", + "tableFrom": "scim_group", + "tableTo": "scim_connection", + "columnsFrom": ["connection_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scim_group_mapping": { + "name": "scim_group_mapping", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "group_id": { + "name": "group_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_kind": { + "name": "target_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission_group_id": { + "name": "permission_group_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "permission_type": { + "name": "permission_type", + "type": "permission_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scim_group_mapping_group_idx": { + "name": "scim_group_mapping_group_idx", + "columns": [ + { + "expression": "group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_group_mapping_permission_group_idx": { + "name": "scim_group_mapping_permission_group_idx", + "columns": [ + { + "expression": "permission_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_group_mapping_workspace_idx": { + "name": "scim_group_mapping_workspace_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_group_mapping_group_target_unique": { + "name": "scim_group_mapping_group_target_unique", + "columns": [ + { + "expression": "group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"permission_group_id\", \"workspace_id\", \"role\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scim_group_mapping_group_id_scim_group_id_fk": { + "name": "scim_group_mapping_group_id_scim_group_id_fk", + "tableFrom": "scim_group_mapping", + "tableTo": "scim_group", + "columnsFrom": ["group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scim_group_mapping_permission_group_id_permission_group_id_fk": { + "name": "scim_group_mapping_permission_group_id_permission_group_id_fk", + "tableFrom": "scim_group_mapping", + "tableTo": "permission_group", + "columnsFrom": ["permission_group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scim_group_mapping_workspace_id_workspace_id_fk": { + "name": "scim_group_mapping_workspace_id_workspace_id_fk", + "tableFrom": "scim_group_mapping", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scim_group_mapping_created_by_user_id_fk": { + "name": "scim_group_mapping_created_by_user_id_fk", + "tableFrom": "scim_group_mapping", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "scim_group_mapping_target_shape": { + "name": "scim_group_mapping_target_shape", + "value": "(\n (\"scim_group_mapping\".\"target_kind\" = 'permission_group' AND \"scim_group_mapping\".\"permission_group_id\" IS NOT NULL AND \"scim_group_mapping\".\"workspace_id\" IS NULL AND \"scim_group_mapping\".\"permission_type\" IS NULL AND \"scim_group_mapping\".\"role\" IS NULL)\n OR (\"scim_group_mapping\".\"target_kind\" = 'workspace' AND \"scim_group_mapping\".\"workspace_id\" IS NOT NULL AND \"scim_group_mapping\".\"permission_type\" IS NOT NULL AND \"scim_group_mapping\".\"permission_group_id\" IS NULL AND \"scim_group_mapping\".\"role\" IS NULL)\n OR (\"scim_group_mapping\".\"target_kind\" = 'org_role' AND \"scim_group_mapping\".\"role\" IS NOT NULL AND \"scim_group_mapping\".\"permission_group_id\" IS NULL AND \"scim_group_mapping\".\"workspace_id\" IS NULL AND \"scim_group_mapping\".\"permission_type\" IS NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.scim_group_member": { + "name": "scim_group_member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "group_id": { + "name": "group_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scim_user_id": { + "name": "scim_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scim_group_member_group_user_unique": { + "name": "scim_group_member_group_user_unique", + "columns": [ + { + "expression": "group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scim_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_group_member_scim_user_idx": { + "name": "scim_group_member_scim_user_idx", + "columns": [ + { + "expression": "scim_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scim_group_member_group_id_scim_group_id_fk": { + "name": "scim_group_member_group_id_scim_group_id_fk", + "tableFrom": "scim_group_member", + "tableTo": "scim_group", + "columnsFrom": ["group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scim_group_member_scim_user_id_scim_user_id_fk": { + "name": "scim_group_member_scim_user_id_scim_user_id_fk", + "tableFrom": "scim_group_member", + "tableTo": "scim_user", + "columnsFrom": ["scim_user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scim_projection_grant": { + "name": "scim_projection_grant", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scim_user_id": { + "name": "scim_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_kind": { + "name": "target_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission_type": { + "name": "permission_type", + "type": "permission_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "baseline_permission": { + "name": "baseline_permission", + "type": "permission_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "origin": { + "name": "origin", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'directory'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scim_projection_grant_user_target_unique": { + "name": "scim_projection_grant_user_target_unique", + "columns": [ + { + "expression": "scim_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_projection_grant_connection_idx": { + "name": "scim_projection_grant_connection_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scim_projection_grant_connection_id_scim_connection_id_fk": { + "name": "scim_projection_grant_connection_id_scim_connection_id_fk", + "tableFrom": "scim_projection_grant", + "tableTo": "scim_connection", + "columnsFrom": ["connection_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scim_projection_grant_scim_user_id_scim_user_id_fk": { + "name": "scim_projection_grant_scim_user_id_scim_user_id_fk", + "tableFrom": "scim_projection_grant", + "tableTo": "scim_user", + "columnsFrom": ["scim_user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scim_request_log": { + "name": "scim_request_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "method": { + "name": "method", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "scim_type": { + "name": "scim_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "detail": { + "name": "detail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "duration_ms": { + "name": "duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scim_request_log_connection_created_idx": { + "name": "scim_request_log_connection_created_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scim_request_log_connection_id_scim_connection_id_fk": { + "name": "scim_request_log_connection_id_scim_connection_id_fk", + "tableFrom": "scim_request_log", + "tableTo": "scim_connection", + "columnsFrom": ["connection_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scim_user": { + "name": "scim_user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_name": { + "name": "user_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "attributes": { + "name": "attributes", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "order_key": { + "name": "order_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scim_user_connection_user_unique": { + "name": "scim_user_connection_user_unique", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_user_connection_user_name_unique": { + "name": "scim_user_connection_user_name_unique", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_user_connection_external_id_unique": { + "name": "scim_user_connection_external_id_unique", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "external_id is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_user_connection_order_idx": { + "name": "scim_user_connection_order_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "order_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_user_user_idx": { + "name": "scim_user_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scim_user_connection_id_scim_connection_id_fk": { + "name": "scim_user_connection_id_scim_connection_id_fk", + "tableFrom": "scim_user", + "tableTo": "scim_connection", + "columnsFrom": ["connection_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scim_user_user_id_user_id_fk": { + "name": "scim_user_user_id_user_id_fk", + "tableFrom": "scim_user", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scim_user_tombstone": { + "name": "scim_user_tombstone", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scim_user_tombstone_connection_external_id_unique": { + "name": "scim_user_tombstone_connection_external_id_unique", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_user_tombstone_user_idx": { + "name": "scim_user_tombstone_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scim_user_tombstone_connection_id_scim_connection_id_fk": { + "name": "scim_user_tombstone_connection_id_scim_connection_id_fk", + "tableFrom": "scim_user_tombstone", + "tableTo": "scim_connection", + "columnsFrom": ["connection_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scim_user_tombstone_user_id_user_id_fk": { + "name": "scim_user_tombstone_user_id_user_id_fk", + "tableFrom": "scim_user_tombstone", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.secret_usage": { + "name": "secret_usage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret_name": { + "name": "secret_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret_scope": { + "name": "secret_scope", + "type": "secret_usage_scope", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "secret_owner_user_id": { + "name": "secret_owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "source": { + "name": "source", + "type": "secret_usage_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "usage_date": { + "name": "usage_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "use_count": { + "name": "use_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "last_execution_id": { + "name": "last_execution_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_trigger": { + "name": "last_trigger", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "secret_usage_bucket_unique": { + "name": "secret_usage_bucket_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "secret_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "secret_scope", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "secret_owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "actor_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "usage_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "secret_usage_secret_recent_idx": { + "name": "secret_usage_secret_recent_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "secret_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "secret_scope", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "secret_owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_used_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "secret_usage_workspace_id_workspace_id_fk": { + "name": "secret_usage_workspace_id_workspace_id_fk", + "tableFrom": "secret_usage", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "session_user_id_idx": { + "name": "session_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "session_active_organization_id_organization_id_fk": { + "name": "session_active_organization_id_organization_id_fk", + "tableFrom": "session", + "tableTo": "organization", + "columnsFrom": ["active_organization_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.settings": { + "name": "settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'system'" + }, + "auto_connect": { + "name": "auto_connect", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "telemetry_enabled": { + "name": "telemetry_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "email_preferences": { + "name": "email_preferences", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "billing_usage_notifications_enabled": { + "name": "billing_usage_notifications_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "show_training_controls": { + "name": "show_training_controls", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "super_user_mode_enabled": { + "name": "super_user_mode_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "mothership_environment": { + "name": "mothership_environment", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'default'" + }, + "error_notifications_enabled": { + "name": "error_notifications_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "snap_to_grid_size": { + "name": "snap_to_grid_size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "show_action_bar": { + "name": "show_action_bar", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "auto_focus_on_click": { + "name": "auto_focus_on_click", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "copilot_enabled_models": { + "name": "copilot_enabled_models", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "copilot_auto_allowed_tools": { + "name": "copilot_auto_allowed_tools", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "last_active_workspace_id": { + "name": "last_active_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "settings_user_id_user_id_fk": { + "name": "settings_user_id_user_id_fk", + "tableFrom": "settings", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "settings_user_id_unique": { + "name": "settings_user_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sim_trigger_state": { + "name": "sim_trigger_state", + "schema": "", + "columns": { + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "block_id": { + "name": "block_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope_key": { + "name": "scope_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "last_fired_at": { + "name": "last_fired_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "sim_trigger_state_workflow_id_workflow_id_fk": { + "name": "sim_trigger_state_workflow_id_workflow_id_fk", + "tableFrom": "sim_trigger_state", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "sim_trigger_state_workflow_id_block_id_scope_key_pk": { + "name": "sim_trigger_state_workflow_id_block_id_scope_key_pk", + "columns": ["workflow_id", "block_id", "scope_key"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.skill": { + "name": "skill", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "skill_workspace_name_unique": { + "name": "skill_workspace_name_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "skill_workspace_id_workspace_id_fk": { + "name": "skill_workspace_id_workspace_id_fk", + "tableFrom": "skill", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "skill_user_id_user_id_fk": { + "name": "skill_user_id_user_id_fk", + "tableFrom": "skill", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.skill_member": { + "name": "skill_member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "skill_id": { + "name": "skill_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invited_by": { + "name": "invited_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "skill_member_user_id_idx": { + "name": "skill_member_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "skill_member_unique": { + "name": "skill_member_unique", + "columns": [ + { + "expression": "skill_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "skill_member_skill_id_skill_id_fk": { + "name": "skill_member_skill_id_skill_id_fk", + "tableFrom": "skill_member", + "tableTo": "skill", + "columnsFrom": ["skill_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "skill_member_user_id_user_id_fk": { + "name": "skill_member_user_id_user_id_fk", + "tableFrom": "skill_member", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "skill_member_invited_by_user_id_fk": { + "name": "skill_member_invited_by_user_id_fk", + "tableFrom": "skill_member", + "tableTo": "user", + "columnsFrom": ["invited_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack_app": { + "name": "slack_app", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_client_secret": { + "name": "encrypted_client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_signing_secret": { + "name": "encrypted_signing_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revision": { + "name": "revision", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "slack_app_organization_id_organization_id_fk": { + "name": "slack_app_organization_id_organization_id_fk", + "tableFrom": "slack_app", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "slack_app_owner_check": { + "name": "slack_app_owner_check", + "value": "(\"slack_app\".\"kind\" = 'custom' AND \"slack_app\".\"organization_id\" IS NOT NULL) OR (\"slack_app\".\"kind\" = 'shared' AND \"slack_app\".\"organization_id\" IS NULL)" + }, + "slack_app_custom_credentials_check": { + "name": "slack_app_custom_credentials_check", + "value": "\"slack_app\".\"kind\" = 'shared' OR (\"slack_app\".\"client_id\" IS NOT NULL AND \"slack_app\".\"encrypted_client_secret\" IS NOT NULL AND \"slack_app\".\"encrypted_signing_secret\" IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.slack_search_installation": { + "name": "slack_search_installation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "app_id": { + "name": "app_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slack_app_id": { + "name": "slack_app_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_name": { + "name": "team_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bot_user_id": { + "name": "bot_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enterprise_id": { + "name": "enterprise_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "credential_version": { + "name": "credential_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_outcome": { + "name": "last_outcome", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_event_at": { + "name": "last_event_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "slack_search_installation_organization_idx": { + "name": "slack_search_installation_organization_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "slack_search_installation_credential_unique": { + "name": "slack_search_installation_credential_unique", + "columns": [ + { + "expression": "credential_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "slack_search_installation_app_team_unique": { + "name": "slack_search_installation_app_team_unique", + "columns": [ + { + "expression": "app_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "slack_search_installation_active_team_unique": { + "name": "slack_search_installation_active_team_unique", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"slack_search_installation\".\"enabled\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "slack_search_installation_organization_id_organization_id_fk": { + "name": "slack_search_installation_organization_id_organization_id_fk", + "tableFrom": "slack_search_installation", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "slack_search_installation_credential_id_credential_id_fk": { + "name": "slack_search_installation_credential_id_credential_id_fk", + "tableFrom": "slack_search_installation", + "tableTo": "credential", + "columnsFrom": ["credential_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "slack_search_installation_slack_app_id_slack_app_id_fk": { + "name": "slack_search_installation_slack_app_id_slack_app_id_fk", + "tableFrom": "slack_search_installation", + "tableTo": "slack_app", + "columnsFrom": ["slack_app_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack_search_turn": { + "name": "slack_search_turn", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "ordinal": { + "name": "ordinal", + "type": "integer", + "primaryKey": false, + "notNull": true, + "identity": { + "type": "always", + "name": "slack_search_turn_ordinal_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "installation_id": { + "name": "installation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "conversation_key": { + "name": "conversation_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_id": { + "name": "event_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "lease_id": { + "name": "lease_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "slack_search_turn_event_unique": { + "name": "slack_search_turn_event_unique", + "columns": [ + { + "expression": "installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "slack_search_turn_pending_idx": { + "name": "slack_search_turn_pending_idx", + "columns": [ + { + "expression": "installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "slack_search_turn_thread_idx": { + "name": "slack_search_turn_thread_idx", + "columns": [ + { + "expression": "conversation_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "slack_search_turn_active_thread_unique": { + "name": "slack_search_turn_active_thread_unique", + "columns": [ + { + "expression": "conversation_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"slack_search_turn\".\"status\" = 'running'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "slack_search_turn_installation_id_slack_search_installation_id_fk": { + "name": "slack_search_turn_installation_id_slack_search_installation_id_fk", + "tableFrom": "slack_search_turn", + "tableTo": "slack_search_installation", + "columnsFrom": ["installation_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sso_domain": { + "name": "sso_domain", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "verification_token": { + "name": "verification_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "primary_provider_id": { + "name": "primary_provider_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sso_domain_organization_id_idx": { + "name": "sso_domain_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sso_domain_domain_idx": { + "name": "sso_domain_domain_idx", + "columns": [ + { + "expression": "domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sso_domain_org_domain_unique": { + "name": "sso_domain_org_domain_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sso_domain_verified_unique": { + "name": "sso_domain_verified_unique", + "columns": [ + { + "expression": "domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "status = 'verified'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "sso_domain_organization_id_organization_id_fk": { + "name": "sso_domain_organization_id_organization_id_fk", + "tableFrom": "sso_domain", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "sso_domain_created_by_user_id_fk": { + "name": "sso_domain_created_by_user_id_fk", + "tableFrom": "sso_domain", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sso_provider": { + "name": "sso_provider", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "issuer": { + "name": "issuer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "oidc_config": { + "name": "oidc_config", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "saml_config": { + "name": "saml_config", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domain_verified": { + "name": "domain_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "jit_provisioning_enabled": { + "name": "jit_provisioning_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + } + }, + "indexes": { + "sso_provider_provider_id_unique": { + "name": "sso_provider_provider_id_unique", + "columns": [ + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sso_provider_domain_idx": { + "name": "sso_provider_domain_idx", + "columns": [ + { + "expression": "domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sso_provider_user_id_idx": { + "name": "sso_provider_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sso_provider_organization_id_idx": { + "name": "sso_provider_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "sso_provider_user_id_user_id_fk": { + "name": "sso_provider_user_id_user_id_fk", + "tableFrom": "sso_provider", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "sso_provider_organization_id_organization_id_fk": { + "name": "sso_provider_organization_id_organization_id_fk", + "tableFrom": "sso_provider", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.subscription": { + "name": "subscription", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "period_start": { + "name": "period_start", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "period_end": { + "name": "period_end", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "cancel_at": { + "name": "cancel_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "canceled_at": { + "name": "canceled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "seats": { + "name": "seats", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "trial_start": { + "name": "trial_start", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "trial_end": { + "name": "trial_end", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "billing_interval": { + "name": "billing_interval", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_schedule_id": { + "name": "stripe_schedule_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "last_closed_period_start": { + "name": "last_closed_period_start", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "subscription_reference_status_idx": { + "name": "subscription_reference_status_idx", + "columns": [ + { + "expression": "reference_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "subscription_cycle_close_lagging_idx": { + "name": "subscription_cycle_close_lagging_idx", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"subscription\".\"status\" in ('active', 'past_due') and \"subscription\".\"period_start\" is not null and (\"subscription\".\"last_closed_period_start\" is null or \"subscription\".\"last_closed_period_start\" < \"subscription\".\"period_start\")", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "check_enterprise_metadata": { + "name": "check_enterprise_metadata", + "value": "plan != 'enterprise' OR metadata IS NOT NULL" + } + }, + "isRLSEnabled": false + }, + "public.table_jobs": { + "name": "table_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'running'" + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "rows_processed": { + "name": "rows_processed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "table_jobs_one_active_per_table": { + "name": "table_jobs_one_active_per_table", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"table_jobs\".\"status\" = 'running' AND \"table_jobs\".\"type\" <> 'export'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "table_jobs_watchdog_idx": { + "name": "table_jobs_watchdog_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "table_jobs_table_started_idx": { + "name": "table_jobs_table_started_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "table_jobs_table_id_user_table_definitions_id_fk": { + "name": "table_jobs_table_id_user_table_definitions_id_fk", + "tableFrom": "table_jobs", + "tableTo": "user_table_definitions", + "columnsFrom": ["table_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "table_jobs_workspace_id_workspace_id_fk": { + "name": "table_jobs_workspace_id_workspace_id_fk", + "tableFrom": "table_jobs", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.table_row_executions": { + "name": "table_row_executions", + "schema": "", + "columns": { + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "row_id": { + "name": "row_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "group_id": { + "name": "group_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "job_id": { + "name": "job_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "running_block_ids": { + "name": "running_block_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "block_errors": { + "name": "block_errors", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "capability_governed_user_id": { + "name": "capability_governed_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enrichment_details": { + "name": "enrichment_details", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "table_row_executions_table_status_idx": { + "name": "table_row_executions_table_status_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"table_row_executions\".\"status\" IN ('queued', 'running', 'pending')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "table_row_executions_execution_id_idx": { + "name": "table_row_executions_execution_id_idx", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"table_row_executions\".\"execution_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "table_row_executions_table_group_idx": { + "name": "table_row_executions_table_group_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "table_row_executions_table_id_user_table_definitions_id_fk": { + "name": "table_row_executions_table_id_user_table_definitions_id_fk", + "tableFrom": "table_row_executions", + "tableTo": "user_table_definitions", + "columnsFrom": ["table_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "table_row_executions_row_id_user_table_rows_id_fk": { + "name": "table_row_executions_row_id_user_table_rows_id_fk", + "tableFrom": "table_row_executions", + "tableTo": "user_table_rows", + "columnsFrom": ["row_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "table_row_executions_capability_governed_user_id_user_id_fk": { + "name": "table_row_executions_capability_governed_user_id_user_id_fk", + "tableFrom": "table_row_executions", + "tableTo": "user", + "columnsFrom": ["capability_governed_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "table_row_executions_row_id_group_id_pk": { + "name": "table_row_executions_row_id_group_id_pk", + "columns": ["row_id", "group_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.table_run_dispatches": { + "name": "table_run_dispatches", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "cursor": { + "name": "cursor", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "limit": { + "name": "limit", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "processed_count": { + "name": "processed_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "is_manual_run": { + "name": "is_manual_run", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "triggered_by_user_id": { + "name": "triggered_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "capability_governed_user_id": { + "name": "capability_governed_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "requested_at": { + "name": "requested_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "heartbeat_at": { + "name": "heartbeat_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "table_run_dispatches_active_idx": { + "name": "table_run_dispatches_active_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "table_run_dispatches_watchdog_idx": { + "name": "table_run_dispatches_watchdog_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "requested_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "table_run_dispatches_governed_active_idx": { + "name": "table_run_dispatches_governed_active_idx", + "columns": [ + { + "expression": "capability_governed_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"table_run_dispatches\".\"status\" IN ('pending', 'dispatching')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "table_run_dispatches_table_id_user_table_definitions_id_fk": { + "name": "table_run_dispatches_table_id_user_table_definitions_id_fk", + "tableFrom": "table_run_dispatches", + "tableTo": "user_table_definitions", + "columnsFrom": ["table_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "table_run_dispatches_workspace_id_workspace_id_fk": { + "name": "table_run_dispatches_workspace_id_workspace_id_fk", + "tableFrom": "table_run_dispatches", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "table_run_dispatches_triggered_by_user_id_user_id_fk": { + "name": "table_run_dispatches_triggered_by_user_id_user_id_fk", + "tableFrom": "table_run_dispatches", + "tableTo": "user", + "columnsFrom": ["triggered_by_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "table_run_dispatches_capability_governed_user_id_user_id_fk": { + "name": "table_run_dispatches_capability_governed_user_id_user_id_fk", + "tableFrom": "table_run_dispatches", + "tableTo": "user", + "columnsFrom": ["capability_governed_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.table_views": { + "name": "table_views", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "table_views_table_created_idx": { + "name": "table_views_table_created_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "table_views_workspace_created_idx": { + "name": "table_views_workspace_created_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "table_views_table_default_unique": { + "name": "table_views_table_default_unique", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "is_default = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "table_views_table_id_user_table_definitions_id_fk": { + "name": "table_views_table_id_user_table_definitions_id_fk", + "tableFrom": "table_views", + "tableTo": "user_table_definitions", + "columnsFrom": ["table_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "table_views_workspace_id_workspace_id_fk": { + "name": "table_views_workspace_id_workspace_id_fk", + "tableFrom": "table_views", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "table_views_created_by_user_id_fk": { + "name": "table_views_created_by_user_id_fk", + "tableFrom": "table_views", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.upload_session": { + "name": "upload_session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "purpose": { + "name": "purpose", + "type": "upload_session_purpose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "method": { + "name": "method", + "type": "upload_session_method", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "storage_context": { + "name": "storage_context", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "final_key": { + "name": "final_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storage_provider": { + "name": "storage_provider", + "type": "upload_session_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "provider_upload_id": { + "name": "provider_upload_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_object_version": { + "name": "provider_object_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_size": { + "name": "file_size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "part_size": { + "name": "part_size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "part_count": { + "name": "part_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "upload_session_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'uploading'" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "processing_lease_id": { + "name": "processing_lease_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "processing_lease_expires_at": { + "name": "processing_lease_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_file_id": { + "name": "completed_file_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "upload_session_token_hash_unique": { + "name": "upload_session_token_hash_unique", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "upload_session_final_key_unique": { + "name": "upload_session_final_key_unique", + "columns": [ + { + "expression": "final_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "upload_session_status_expires_at_idx": { + "name": "upload_session_status_expires_at_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.usage_log": { + "name": "usage_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "usage_log_category", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "usage_log_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "cost": { + "name": "cost", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "event_key": { + "name": "event_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "billing_entity_type": { + "name": "billing_entity_type", + "type": "billing_entity_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "billing_entity_id": { + "name": "billing_entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "billing_period_start": { + "name": "billing_period_start", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "billing_period_end": { + "name": "billing_period_end", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "usage_log_user_created_at_idx": { + "name": "usage_log_user_created_at_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "usage_log_source_idx": { + "name": "usage_log_source_idx", + "columns": [ + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "usage_log_workflow_id_idx": { + "name": "usage_log_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "usage_log_event_key_unique": { + "name": "usage_log_event_key_unique", + "columns": [ + { + "expression": "event_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"usage_log\".\"event_key\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "usage_log_billing_entity_period_idx": { + "name": "usage_log_billing_entity_period_idx", + "columns": [ + { + "expression": "billing_entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_period_start", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_period_end", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"usage_log\".\"billing_entity_type\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "usage_log_billing_period_cost_idx": { + "name": "usage_log_billing_period_cost_idx", + "columns": [ + { + "expression": "billing_entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_period_start", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_period_end", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cost", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"usage_log\".\"billing_entity_type\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "usage_log_billing_entity_created_at_cost_idx": { + "name": "usage_log_billing_entity_created_at_cost_idx", + "columns": [ + { + "expression": "billing_entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cost", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"usage_log\".\"billing_entity_type\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "usage_log_workspace_created_at_idx": { + "name": "usage_log_workspace_created_at_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "usage_log_execution_id_idx": { + "name": "usage_log_execution_id_idx", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "usage_log_user_id_user_id_fk": { + "name": "usage_log_user_id_user_id_fk", + "tableFrom": "usage_log", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "usage_log_workspace_id_workspace_id_fk": { + "name": "usage_log_workspace_id_workspace_id_fk", + "tableFrom": "usage_log", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "usage_log_workflow_id_workflow_id_fk": { + "name": "usage_log_workflow_id_workflow_id_fk", + "tableFrom": "usage_log", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "usage_log_billing_scope_all_or_none": { + "name": "usage_log_billing_scope_all_or_none", + "value": "(\n (\"usage_log\".\"billing_entity_type\" IS NULL AND \"usage_log\".\"billing_entity_id\" IS NULL AND \"usage_log\".\"billing_period_start\" IS NULL AND \"usage_log\".\"billing_period_end\" IS NULL)\n OR\n (\"usage_log\".\"billing_entity_type\" IS NOT NULL AND \"usage_log\".\"billing_entity_id\" IS NOT NULL AND \"usage_log\".\"billing_period_start\" IS NOT NULL AND \"usage_log\".\"billing_period_end\" IS NOT NULL AND \"usage_log\".\"billing_period_start\" < \"usage_log\".\"billing_period_end\")\n )" + } + }, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "normalized_email": { + "name": "normalized_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'user'" + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "suspended_at": { + "name": "suspended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "suspension_source": { + "name": "suspension_source", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "user_email_lower_idx": { + "name": "user_email_lower_idx", + "columns": [ + { + "expression": "lower(btrim(\"email\"))", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": ["email"] + }, + "user_normalized_email_unique": { + "name": "user_normalized_email_unique", + "nullsNotDistinct": false, + "columns": ["normalized_email"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_stats": { + "name": "user_stats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_usage_limit": { + "name": "current_usage_limit", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "default": "'5'" + }, + "usage_limit_updated_at": { + "name": "usage_limit_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "last_period_cost": { + "name": "last_period_cost", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "default": "'0'" + }, + "billed_overage_this_period": { + "name": "billed_overage_this_period", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "credit_balance": { + "name": "credit_balance", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "last_period_copilot_cost": { + "name": "last_period_copilot_cost", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "default": "'0'" + }, + "storage_used_bytes": { + "name": "storage_used_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "billing_blocked": { + "name": "billing_blocked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "billing_blocked_reason": { + "name": "billing_blocked_reason", + "type": "billing_blocked_reason", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "limit_notifications": { + "name": "limit_notifications", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "user_stats_user_id_user_id_fk": { + "name": "user_stats_user_id_user_id_fk", + "tableFrom": "user_stats", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_stats_user_id_unique": { + "name": "user_stats_user_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_table_definitions": { + "name": "user_table_definitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "folder_id": { + "name": "folder_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "schema": { + "name": "schema", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "max_rows": { + "name": "max_rows", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 10000 + }, + "row_count": { + "name": "row_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "rows_version": { + "name": "rows_version", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "schema_locked": { + "name": "schema_locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "insert_locked": { + "name": "insert_locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "update_locked": { + "name": "update_locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "delete_locked": { + "name": "delete_locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "user_table_def_workspace_id_idx": { + "name": "user_table_def_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_table_def_folder_id_idx": { + "name": "user_table_def_folder_id_idx", + "columns": [ + { + "expression": "folder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_table_def_workspace_name_unique": { + "name": "user_table_def_workspace_name_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"user_table_definitions\".\"archived_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_table_def_archived_at_idx": { + "name": "user_table_def_archived_at_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_table_def_workspace_archived_partial_idx": { + "name": "user_table_def_workspace_archived_partial_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"user_table_definitions\".\"archived_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_table_definitions_workspace_id_workspace_id_fk": { + "name": "user_table_definitions_workspace_id_workspace_id_fk", + "tableFrom": "user_table_definitions", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_table_definitions_folder_id_folder_id_fk": { + "name": "user_table_definitions_folder_id_folder_id_fk", + "tableFrom": "user_table_definitions", + "tableTo": "folder", + "columnsFrom": ["folder_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "user_table_definitions_created_by_user_id_fk": { + "name": "user_table_definitions_created_by_user_id_fk", + "tableFrom": "user_table_definitions", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_table_row_changes": { + "name": "user_table_row_changes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "user_table_row_changes_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "row_delta": { + "name": "row_delta", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "user_table_row_changes_table_id_idx": { + "name": "user_table_row_changes_table_id_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_table_row_changes_table_id_user_table_definitions_id_fk": { + "name": "user_table_row_changes_table_id_user_table_definitions_id_fk", + "tableFrom": "user_table_row_changes", + "tableTo": "user_table_definitions", + "columnsFrom": ["table_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_table_row_secret_provenance": { + "name": "user_table_row_secret_provenance", + "schema": "", + "columns": { + "row_id": { + "name": "row_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "content_updated_at": { + "name": "content_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entries": { + "name": "entries", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_table_row_secret_provenance_row_id_user_table_rows_id_fk": { + "name": "user_table_row_secret_provenance_row_id_user_table_rows_id_fk", + "tableFrom": "user_table_row_secret_provenance", + "tableTo": "user_table_rows", + "columnsFrom": ["row_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "user_table_row_secret_provenance_status_check": { + "name": "user_table_row_secret_provenance_status_check", + "value": "\"user_table_row_secret_provenance\".\"status\" IN ('exact', 'unknown')" + } + }, + "isRLSEnabled": false + }, + "public.user_table_rows": { + "name": "user_table_rows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "order_key": { + "name": "order_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret_provenance_version": { + "name": "secret_provenance_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "user_table_rows_tenant_data_gin_idx": { + "name": "user_table_rows_tenant_data_gin_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"data\" jsonb_path_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "user_table_rows_workspace_table_idx": { + "name": "user_table_rows_workspace_table_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_table_rows_table_position_idx": { + "name": "user_table_rows_table_position_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "position", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_table_rows_table_order_key_idx": { + "name": "user_table_rows_table_order_key_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "order_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_table_rows_table_created_id_idx": { + "name": "user_table_rows_table_created_id_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_table_rows_table_id_id_idx": { + "name": "user_table_rows_table_id_id_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_table_rows_table_id_user_table_definitions_id_fk": { + "name": "user_table_rows_table_id_user_table_definitions_id_fk", + "tableFrom": "user_table_rows", + "tableTo": "user_table_definitions", + "columnsFrom": ["table_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_table_rows_workspace_id_workspace_id_fk": { + "name": "user_table_rows_workspace_id_workspace_id_fk", + "tableFrom": "user_table_rows", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_table_rows_created_by_user_id_fk": { + "name": "user_table_rows_created_by_user_id_fk", + "tableFrom": "user_table_rows", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "verification_identifier_idx": { + "name": "verification_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "verification_expires_at_idx": { + "name": "verification_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.waitlist": { + "name": "waitlist", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "waitlist_email_unique": { + "name": "waitlist_email_unique", + "nullsNotDistinct": false, + "columns": ["email"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.webhook": { + "name": "webhook", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deployment_version_id": { + "name": "deployment_version_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registration_status": { + "name": "registration_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registration_generation": { + "name": "registration_generation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "config_fingerprint": { + "name": "config_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prepared_at": { + "name": "prepared_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "block_id": { + "name": "block_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "routing_key": { + "name": "routing_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_config": { + "name": "provider_config", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "failed_count": { + "name": "failed_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "last_failed_at": { + "name": "last_failed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "path_deployment_unique": { + "name": "path_deployment_unique", + "columns": [ + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deployment_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"webhook\".\"archived_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "webhook_workflow_deployment_idx": { + "name": "webhook_workflow_deployment_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deployment_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "webhook_routing_key_active_idx": { + "name": "webhook_routing_key_active_idx", + "columns": [ + { + "expression": "routing_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"webhook\".\"archived_at\" IS NULL AND \"webhook\".\"routing_key\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "webhook_archived_at_partial_idx": { + "name": "webhook_archived_at_partial_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"webhook\".\"archived_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_webhook_on_provider_is_active_workflow_id_deploym_bdeed5468": { + "name": "idx_webhook_on_provider_is_active_workflow_id_deploym_bdeed5468", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "is_active", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deployment_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_webhook_on_workflow_id_block_id_updated_at_desc": { + "name": "idx_webhook_on_workflow_id_block_id_updated_at_desc", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "webhook_active_registration_unique": { + "name": "webhook_active_registration_unique", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"webhook\".\"registration_status\" = 'active' AND \"webhook\".\"block_id\" IS NOT NULL AND \"webhook\".\"archived_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "webhook_candidate_registration_unique": { + "name": "webhook_candidate_registration_unique", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"webhook\".\"registration_status\" = 'candidate' AND \"webhook\".\"block_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "webhook_registration_status_generation_idx": { + "name": "webhook_registration_status_generation_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "registration_status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "registration_generation", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "webhook_workflow_id_workflow_id_fk": { + "name": "webhook_workflow_id_workflow_id_fk", + "tableFrom": "webhook", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "webhook_deployment_version_id_workflow_deployment_version_id_fk": { + "name": "webhook_deployment_version_id_workflow_deployment_version_id_fk", + "tableFrom": "webhook", + "tableTo": "workflow_deployment_version", + "columnsFrom": ["deployment_version_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "webhook_registration_status_check": { + "name": "webhook_registration_status_check", + "value": "\"webhook\".\"registration_status\" IS NULL OR \"webhook\".\"registration_status\" IN ('active', 'candidate', 'retired', 'orphaned')" + }, + "webhook_registration_generation_check": { + "name": "webhook_registration_generation_check", + "value": "\"webhook\".\"registration_generation\" IS NULL OR \"webhook\".\"registration_generation\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.webhook_path_claim": { + "name": "webhook_path_claim", + "schema": "", + "columns": { + "path": { + "name": "path", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "generation": { + "name": "generation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "webhook_path_claim_workflow_idx": { + "name": "webhook_path_claim_workflow_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "webhook_path_claim_workflow_id_workflow_id_fk": { + "name": "webhook_path_claim_workflow_id_workflow_id_fk", + "tableFrom": "webhook_path_claim", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "webhook_path_claim_generation_check": { + "name": "webhook_path_claim_generation_check", + "value": "\"webhook_path_claim\".\"generation\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.workflow": { + "name": "workflow", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "folder_id": { + "name": "folder_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced": { + "name": "last_synced", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "is_deployed": { + "name": "is_deployed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "deployed_at": { + "name": "deployed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "is_public_api": { + "name": "is_public_api", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "locked": { + "name": "locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "fork_sync_excluded": { + "name": "fork_sync_excluded", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "run_count": { + "name": "run_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_run_at": { + "name": "last_run_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "variables": { + "name": "variables", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "workflow_user_id_idx": { + "name": "workflow_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_workspace_id_idx": { + "name": "workflow_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_user_workspace_idx": { + "name": "workflow_user_workspace_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_workspace_folder_name_active_unique": { + "name": "workflow_workspace_folder_name_active_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"folder_id\", '')", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workflow\".\"archived_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_folder_sort_idx": { + "name": "workflow_folder_sort_idx", + "columns": [ + { + "expression": "folder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_active_workspace_sort_idx": { + "name": "workflow_active_workspace_sort_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow\".\"archived_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_archived_at_idx": { + "name": "workflow_archived_at_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_workspace_archived_partial_idx": { + "name": "workflow_workspace_archived_partial_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow\".\"archived_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_user_id_user_id_fk": { + "name": "workflow_user_id_user_id_fk", + "tableFrom": "workflow", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_workspace_id_workspace_id_fk": { + "name": "workflow_workspace_id_workspace_id_fk", + "tableFrom": "workflow", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_folder_id_folder_id_fk": { + "name": "workflow_folder_id_folder_id_fk", + "tableFrom": "workflow", + "tableTo": "folder", + "columnsFrom": ["folder_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_blocks": { + "name": "workflow_blocks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "position_x": { + "name": "position_x", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "position_y": { + "name": "position_y", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "horizontal_handles": { + "name": "horizontal_handles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "is_wide": { + "name": "is_wide", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "advanced_mode": { + "name": "advanced_mode", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "trigger_mode": { + "name": "trigger_mode", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "error_enabled": { + "name": "error_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "retry": { + "name": "retry", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "locked": { + "name": "locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "height": { + "name": "height", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "sub_blocks": { + "name": "sub_blocks", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "outputs": { + "name": "outputs", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_blocks_workflow_id_idx": { + "name": "workflow_blocks_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_blocks_type_idx": { + "name": "workflow_blocks_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_blocks_workflow_id_workflow_id_fk": { + "name": "workflow_blocks_workflow_id_workflow_id_fk", + "tableFrom": "workflow_blocks", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_checkpoints": { + "name": "workflow_checkpoints", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workflow_state": { + "name": "workflow_state", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_checkpoints_user_id_idx": { + "name": "workflow_checkpoints_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_workflow_id_idx": { + "name": "workflow_checkpoints_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_chat_id_idx": { + "name": "workflow_checkpoints_chat_id_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_message_id_idx": { + "name": "workflow_checkpoints_message_id_idx", + "columns": [ + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_user_workflow_idx": { + "name": "workflow_checkpoints_user_workflow_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_workflow_chat_idx": { + "name": "workflow_checkpoints_workflow_chat_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_created_at_idx": { + "name": "workflow_checkpoints_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_chat_created_at_idx": { + "name": "workflow_checkpoints_chat_created_at_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_checkpoints_user_id_user_id_fk": { + "name": "workflow_checkpoints_user_id_user_id_fk", + "tableFrom": "workflow_checkpoints", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_checkpoints_workflow_id_workflow_id_fk": { + "name": "workflow_checkpoints_workflow_id_workflow_id_fk", + "tableFrom": "workflow_checkpoints", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_checkpoints_chat_id_copilot_chats_id_fk": { + "name": "workflow_checkpoints_chat_id_copilot_chats_id_fk", + "tableFrom": "workflow_checkpoints", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_deployment_operation": { + "name": "workflow_deployment_operation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deployment_version_id": { + "name": "deployment_version_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "previous_active_version_id": { + "name": "previous_active_version_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "protocol_version": { + "name": "protocol_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "generation": { + "name": "generation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'preparing'" + }, + "component_readiness": { + "name": "component_readiness", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "request_hash": { + "name": "request_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_deployment_operation_workflow_generation_unique": { + "name": "workflow_deployment_operation_workflow_generation_unique", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "generation", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_deployment_operation_workflow_idempotency_unique": { + "name": "workflow_deployment_operation_workflow_idempotency_unique", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workflow_deployment_operation\".\"idempotency_key\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_deployment_operation_workflow_in_flight_unique": { + "name": "workflow_deployment_operation_workflow_in_flight_unique", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workflow_deployment_operation\".\"status\" IN ('preparing', 'activating')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_deployment_operation_workflow_status_idx": { + "name": "workflow_deployment_operation_workflow_status_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_deployment_operation_deployment_version_idx": { + "name": "workflow_deployment_operation_deployment_version_idx", + "columns": [ + { + "expression": "deployment_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_deployment_operation_workflow_version_generation_idx": { + "name": "workflow_deployment_operation_workflow_version_generation_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deployment_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "generation", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_deployment_operation_workflow_id_workflow_id_fk": { + "name": "workflow_deployment_operation_workflow_id_workflow_id_fk", + "tableFrom": "workflow_deployment_operation", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_deployment_operation_deployment_version_id_workflow_deployment_version_id_fk": { + "name": "workflow_deployment_operation_deployment_version_id_workflow_deployment_version_id_fk", + "tableFrom": "workflow_deployment_operation", + "tableTo": "workflow_deployment_version", + "columnsFrom": ["deployment_version_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_deployment_operation_previous_active_version_id_workflow_deployment_version_id_fk": { + "name": "workflow_deployment_operation_previous_active_version_id_workflow_deployment_version_id_fk", + "tableFrom": "workflow_deployment_operation", + "tableTo": "workflow_deployment_version", + "columnsFrom": ["previous_active_version_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "workflow_deployment_operation_action_check": { + "name": "workflow_deployment_operation_action_check", + "value": "\"workflow_deployment_operation\".\"action\" IN ('deploy', 'activate')" + }, + "workflow_deployment_operation_status_check": { + "name": "workflow_deployment_operation_status_check", + "value": "\"workflow_deployment_operation\".\"status\" IN ('preparing', 'activating', 'active', 'failed', 'superseded')" + }, + "workflow_deployment_operation_generation_check": { + "name": "workflow_deployment_operation_generation_check", + "value": "\"workflow_deployment_operation\".\"generation\" > 0" + }, + "workflow_deployment_operation_protocol_version_check": { + "name": "workflow_deployment_operation_protocol_version_check", + "value": "\"workflow_deployment_operation\".\"protocol_version\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.workflow_deployment_version": { + "name": "workflow_deployment_version", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "workflow_deployment_version_workflow_version_unique": { + "name": "workflow_deployment_version_workflow_version_unique", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_deployment_version_workflow_active_idx": { + "name": "workflow_deployment_version_workflow_active_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "is_active", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_deployment_version_created_at_idx": { + "name": "workflow_deployment_version_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_deployment_version_workflow_id_workflow_id_fk": { + "name": "workflow_deployment_version_workflow_id_workflow_id_fk", + "tableFrom": "workflow_deployment_version", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_edges": { + "name": "workflow_edges", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_block_id": { + "name": "source_block_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_block_id": { + "name": "target_block_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_handle": { + "name": "source_handle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_handle": { + "name": "target_handle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_edges_workflow_id_idx": { + "name": "workflow_edges_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_edges_workflow_source_idx": { + "name": "workflow_edges_workflow_source_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_edges_workflow_target_idx": { + "name": "workflow_edges_workflow_target_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_edges_workflow_id_workflow_id_fk": { + "name": "workflow_edges_workflow_id_workflow_id_fk", + "tableFrom": "workflow_edges", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_edges_source_block_id_workflow_blocks_id_fk": { + "name": "workflow_edges_source_block_id_workflow_blocks_id_fk", + "tableFrom": "workflow_edges", + "tableTo": "workflow_blocks", + "columnsFrom": ["source_block_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_edges_target_block_id_workflow_blocks_id_fk": { + "name": "workflow_edges_target_block_id_workflow_blocks_id_fk", + "tableFrom": "workflow_edges", + "tableTo": "workflow_blocks", + "columnsFrom": ["target_block_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_execution_logs": { + "name": "workflow_execution_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state_snapshot_id": { + "name": "state_snapshot_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deployment_version_id": { + "name": "deployment_version_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "level": { + "name": "level", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'running'" + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "execution_deadline_at": { + "name": "execution_deadline_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "total_duration_ms": { + "name": "total_duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "execution_data": { + "name": "execution_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "cost_total": { + "name": "cost_total", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "models_used": { + "name": "models_used", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "files": { + "name": "files", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_execution_logs_workflow_id_idx": { + "name": "workflow_execution_logs_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_state_snapshot_id_idx": { + "name": "workflow_execution_logs_state_snapshot_id_idx", + "columns": [ + { + "expression": "state_snapshot_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_deployment_version_id_idx": { + "name": "workflow_execution_logs_deployment_version_id_idx", + "columns": [ + { + "expression": "deployment_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_trigger_idx": { + "name": "workflow_execution_logs_trigger_idx", + "columns": [ + { + "expression": "trigger", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_level_idx": { + "name": "workflow_execution_logs_level_idx", + "columns": [ + { + "expression": "level", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_started_at_idx": { + "name": "workflow_execution_logs_started_at_idx", + "columns": [ + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_execution_id_unique": { + "name": "workflow_execution_logs_execution_id_unique", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_workflow_started_at_idx": { + "name": "workflow_execution_logs_workflow_started_at_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_workspace_started_at_idx": { + "name": "workflow_execution_logs_workspace_started_at_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_workspace_activity_idx": { + "name": "workflow_execution_logs_workspace_activity_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "total_duration_ms", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "trigger", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_workspace_started_at_id_desc_idx": { + "name": "workflow_execution_logs_workspace_started_at_id_desc_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"started_at\" DESC NULLS LAST", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "\"id\" DESC", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_workspace_cost_total_idx": { + "name": "workflow_execution_logs_workspace_cost_total_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cost_total", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_models_used_idx": { + "name": "workflow_execution_logs_models_used_idx", + "columns": [ + { + "expression": "models_used", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "workflow_execution_logs_workspace_ended_at_id_idx": { + "name": "workflow_execution_logs_workspace_ended_at_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date_trunc('milliseconds', \"ended_at\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_running_started_at_idx": { + "name": "workflow_execution_logs_running_started_at_idx", + "columns": [ + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "status = 'running'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_running_deadline_idx": { + "name": "workflow_execution_logs_running_deadline_idx", + "columns": [ + { + "expression": "execution_deadline_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow_execution_logs\".\"status\" = 'running' AND \"workflow_execution_logs\".\"execution_deadline_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_redacting_started_at_idx": { + "name": "workflow_execution_logs_redacting_started_at_idx", + "columns": [ + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "status = 'redacting'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_redacting_deadline_idx": { + "name": "workflow_execution_logs_redacting_deadline_idx", + "columns": [ + { + "expression": "execution_deadline_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow_execution_logs\".\"status\" = 'redacting' AND \"workflow_execution_logs\".\"execution_deadline_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_completed_ended_at_idx": { + "name": "workflow_execution_logs_completed_ended_at_idx", + "columns": [ + { + "expression": "ended_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow_execution_logs\".\"status\" = 'completed' AND \"workflow_execution_logs\".\"level\" = 'info' AND \"workflow_execution_logs\".\"ended_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_execution_logs_workflow_id_workflow_id_fk": { + "name": "workflow_execution_logs_workflow_id_workflow_id_fk", + "tableFrom": "workflow_execution_logs", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "workflow_execution_logs_workspace_id_workspace_id_fk": { + "name": "workflow_execution_logs_workspace_id_workspace_id_fk", + "tableFrom": "workflow_execution_logs", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_execution_logs_state_snapshot_id_workflow_execution_snapshots_id_fk": { + "name": "workflow_execution_logs_state_snapshot_id_workflow_execution_snapshots_id_fk", + "tableFrom": "workflow_execution_logs", + "tableTo": "workflow_execution_snapshots", + "columnsFrom": ["state_snapshot_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_execution_logs_deployment_version_id_workflow_deployment_version_id_fk": { + "name": "workflow_execution_logs_deployment_version_id_workflow_deployment_version_id_fk", + "tableFrom": "workflow_execution_logs", + "tableTo": "workflow_deployment_version", + "columnsFrom": ["deployment_version_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_execution_snapshots": { + "name": "workflow_execution_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state_hash": { + "name": "state_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state_data": { + "name": "state_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_snapshots_workflow_id_idx": { + "name": "workflow_snapshots_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_snapshots_hash_idx": { + "name": "workflow_snapshots_hash_idx", + "columns": [ + { + "expression": "state_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_snapshots_workflow_hash_idx": { + "name": "workflow_snapshots_workflow_hash_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "state_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_snapshots_created_at_idx": { + "name": "workflow_snapshots_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_execution_snapshots_workflow_id_workflow_id_fk": { + "name": "workflow_execution_snapshots_workflow_id_workflow_id_fk", + "tableFrom": "workflow_execution_snapshots", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_mcp_server": { + "name": "workflow_mcp_server", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_mcp_server_workspace_id_idx": { + "name": "workflow_mcp_server_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_mcp_server_created_by_idx": { + "name": "workflow_mcp_server_created_by_idx", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_mcp_server_deleted_at_idx": { + "name": "workflow_mcp_server_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_mcp_server_workspace_deleted_partial_idx": { + "name": "workflow_mcp_server_workspace_deleted_partial_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow_mcp_server\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_mcp_server_workspace_id_workspace_id_fk": { + "name": "workflow_mcp_server_workspace_id_workspace_id_fk", + "tableFrom": "workflow_mcp_server", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_mcp_server_created_by_user_id_fk": { + "name": "workflow_mcp_server_created_by_user_id_fk", + "tableFrom": "workflow_mcp_server", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_mcp_tool": { + "name": "workflow_mcp_tool", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "server_id": { + "name": "server_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tool_name": { + "name": "tool_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tool_description": { + "name": "tool_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "parameter_schema": { + "name": "parameter_schema", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "parameter_description_overrides": { + "name": "parameter_description_overrides", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'::json" + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_mcp_tool_server_id_idx": { + "name": "workflow_mcp_tool_server_id_idx", + "columns": [ + { + "expression": "server_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_mcp_tool_workflow_id_idx": { + "name": "workflow_mcp_tool_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_mcp_tool_server_workflow_unique": { + "name": "workflow_mcp_tool_server_workflow_unique", + "columns": [ + { + "expression": "server_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workflow_mcp_tool\".\"archived_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_mcp_tool_archived_at_partial_idx": { + "name": "workflow_mcp_tool_archived_at_partial_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow_mcp_tool\".\"archived_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_mcp_tool_server_id_workflow_mcp_server_id_fk": { + "name": "workflow_mcp_tool_server_id_workflow_mcp_server_id_fk", + "tableFrom": "workflow_mcp_tool", + "tableTo": "workflow_mcp_server", + "columnsFrom": ["server_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_mcp_tool_workflow_id_workflow_id_fk": { + "name": "workflow_mcp_tool_workflow_id_workflow_id_fk", + "tableFrom": "workflow_mcp_tool", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_schedule": { + "name": "workflow_schedule", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deployment_version_id": { + "name": "deployment_version_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deployment_operation_id": { + "name": "deployment_operation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "block_id": { + "name": "block_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cron_expression": { + "name": "cron_expression", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_run_at": { + "name": "next_run_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_ran_at": { + "name": "last_ran_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_queued_at": { + "name": "last_queued_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "trigger_type": { + "name": "trigger_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'UTC'" + }, + "failed_count": { + "name": "failed_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "infra_retry_count": { + "name": "infra_retry_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "last_failed_at": { + "name": "last_failed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "source_type": { + "name": "source_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'workflow'" + }, + "job_title": { + "name": "job_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lifecycle": { + "name": "lifecycle", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'persistent'" + }, + "success_condition": { + "name": "success_condition", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "max_runs": { + "name": "max_runs", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "run_count": { + "name": "run_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "source_chat_id": { + "name": "source_chat_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_task_name": { + "name": "source_task_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_user_id": { + "name": "source_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_workspace_id": { + "name": "source_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret_scope": { + "name": "secret_scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'all'" + }, + "mounted_secrets": { + "name": "mounted_secrets", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "job_history": { + "name": "job_history", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "contexts": { + "name": "contexts", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "excluded_dates": { + "name": "excluded_dates", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "ends_at": { + "name": "ends_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_schedule_workflow_block_deployment_unique": { + "name": "workflow_schedule_workflow_block_deployment_unique", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deployment_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workflow_schedule\".\"archived_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_schedule_workflow_deployment_idx": { + "name": "workflow_schedule_workflow_deployment_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deployment_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_schedule_archived_at_partial_idx": { + "name": "workflow_schedule_archived_at_partial_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow_schedule\".\"archived_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_workflow_schedule_on_source_workspace_id_source_t_c07f3bba6": { + "name": "idx_workflow_schedule_on_source_workspace_id_source_t_c07f3bba6", + "columns": [ + { + "expression": "source_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_schedule_due_workflow_idx": { + "name": "workflow_schedule_due_workflow_idx", + "columns": [ + { + "expression": "next_run_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_queued_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deployment_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow_schedule\".\"archived_at\" IS NULL AND \"workflow_schedule\".\"status\" NOT IN ('disabled', 'completed') AND (\"workflow_schedule\".\"source_type\" = 'workflow' OR \"workflow_schedule\".\"source_type\" IS NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_schedule_due_job_idx": { + "name": "workflow_schedule_due_job_idx", + "columns": [ + { + "expression": "next_run_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_queued_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow_schedule\".\"archived_at\" IS NULL AND \"workflow_schedule\".\"status\" NOT IN ('disabled', 'completed') AND \"workflow_schedule\".\"source_type\" = 'job'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_schedule_workflow_id_workflow_id_fk": { + "name": "workflow_schedule_workflow_id_workflow_id_fk", + "tableFrom": "workflow_schedule", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_schedule_deployment_version_id_workflow_deployment_version_id_fk": { + "name": "workflow_schedule_deployment_version_id_workflow_deployment_version_id_fk", + "tableFrom": "workflow_schedule", + "tableTo": "workflow_deployment_version", + "columnsFrom": ["deployment_version_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_schedule_deployment_operation_id_workflow_deployment_operation_id_fk": { + "name": "workflow_schedule_deployment_operation_id_workflow_deployment_operation_id_fk", + "tableFrom": "workflow_schedule", + "tableTo": "workflow_deployment_operation", + "columnsFrom": ["deployment_operation_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "workflow_schedule_source_user_id_user_id_fk": { + "name": "workflow_schedule_source_user_id_user_id_fk", + "tableFrom": "workflow_schedule", + "tableTo": "user", + "columnsFrom": ["source_user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_schedule_source_workspace_id_workspace_id_fk": { + "name": "workflow_schedule_source_workspace_id_workspace_id_fk", + "tableFrom": "workflow_schedule", + "tableTo": "workspace", + "columnsFrom": ["source_workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_subflows": { + "name": "workflow_subflows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_subflows_workflow_id_idx": { + "name": "workflow_subflows_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_subflows_workflow_type_idx": { + "name": "workflow_subflows_workflow_type_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_subflows_workflow_id_workflow_id_fk": { + "name": "workflow_subflows_workflow_id_workflow_id_fk", + "tableFrom": "workflow_subflows", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_test": { + "name": "workflow_test", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "body_file_id": { + "name": "body_file_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cases": { + "name": "cases", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "source_hash": { + "name": "source_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_test_workspace_name_unique": { + "name": "workflow_test_workspace_name_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workflow_test\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_test_body_file_unique": { + "name": "workflow_test_body_file_unique", + "columns": [ + { + "expression": "body_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_test_workspace_id_workspace_id_fk": { + "name": "workflow_test_workspace_id_workspace_id_fk", + "tableFrom": "workflow_test", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_test_body_file_id_workspace_files_id_fk": { + "name": "workflow_test_body_file_id_workspace_files_id_fk", + "tableFrom": "workflow_test", + "tableTo": "workspace_files", + "columnsFrom": ["body_file_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_test_created_by_user_id_user_id_fk": { + "name": "workflow_test_created_by_user_id_user_id_fk", + "tableFrom": "workflow_test", + "tableTo": "user", + "columnsFrom": ["created_by_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_test_run": { + "name": "workflow_test_run", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "test_id": { + "name": "test_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'running'" + }, + "passed": { + "name": "passed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "failed": { + "name": "failed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "skipped": { + "name": "skipped", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "report": { + "name": "report", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_hash": { + "name": "source_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ran_against": { + "name": "ran_against", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "progress": { + "name": "progress", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "triggered_by_actor": { + "name": "triggered_by_actor", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "triggered_by_user_id": { + "name": "triggered_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "workflow_test_run_test_version_started_idx": { + "name": "workflow_test_run_test_version_started_idx", + "columns": [ + { + "expression": "test_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_test_run_test_id_workflow_test_id_fk": { + "name": "workflow_test_run_test_id_workflow_test_id_fk", + "tableFrom": "workflow_test_run", + "tableTo": "workflow_test", + "columnsFrom": ["test_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_test_run_workspace_id_workspace_id_fk": { + "name": "workflow_test_run_workspace_id_workspace_id_fk", + "tableFrom": "workflow_test_run", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_test_run_triggered_by_user_id_user_id_fk": { + "name": "workflow_test_run_triggered_by_user_id_user_id_fk", + "tableFrom": "workflow_test_run", + "tableTo": "user", + "columnsFrom": ["triggered_by_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "workflow_test_run_version_check": { + "name": "workflow_test_run_version_check", + "value": "\"workflow_test_run\".\"version\" IN ('draft', 'deployed')" + }, + "workflow_test_run_status_check": { + "name": "workflow_test_run_status_check", + "value": "\"workflow_test_run\".\"status\" IN ('running', 'passed', 'failed', 'error')" + }, + "workflow_test_run_completed_check": { + "name": "workflow_test_run_completed_check", + "value": "(\"workflow_test_run\".\"status\" = 'running') = (\"workflow_test_run\".\"completed_at\" IS NULL)" + } + }, + "isRLSEnabled": false + }, + "public.workspace": { + "name": "workspace", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'#33C482'" + }, + "logo_url": { + "name": "logo_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_mode": { + "name": "workspace_mode", + "type": "workspace_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'grandfathered_shared'" + }, + "billed_account_user_id": { + "name": "billed_account_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storage_used_bytes": { + "name": "storage_used_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "allow_personal_api_keys": { + "name": "allow_personal_api_keys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "inbox_enabled": { + "name": "inbox_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "inbox_address": { + "name": "inbox_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "inbox_provider_id": { + "name": "inbox_provider_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "inbox_secret_scope": { + "name": "inbox_secret_scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'all'" + }, + "inbox_mounted_secrets": { + "name": "inbox_mounted_secrets", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "organization_assigned_at": { + "name": "organization_assigned_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "forked_from_workspace_id": { + "name": "forked_from_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fork_sync_new_workflows_excluded": { + "name": "fork_sync_new_workflows_excluded", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_owner_id_idx": { + "name": "workspace_owner_id_idx", + "columns": [ + { + "expression": "owner_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_organization_id_idx": { + "name": "workspace_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_mode_idx": { + "name": "workspace_mode_idx", + "columns": [ + { + "expression": "workspace_mode", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_forked_from_workspace_id_idx": { + "name": "workspace_forked_from_workspace_id_idx", + "columns": [ + { + "expression": "forked_from_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_inbox_provider_id_idx": { + "name": "workspace_inbox_provider_id_idx", + "columns": [ + { + "expression": "inbox_provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workspace\".\"inbox_provider_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_owner_id_user_id_fk": { + "name": "workspace_owner_id_user_id_fk", + "tableFrom": "workspace", + "tableTo": "user", + "columnsFrom": ["owner_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_organization_id_organization_id_fk": { + "name": "workspace_organization_id_organization_id_fk", + "tableFrom": "workspace", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "workspace_billed_account_user_id_user_id_fk": { + "name": "workspace_billed_account_user_id_user_id_fk", + "tableFrom": "workspace", + "tableTo": "user", + "columnsFrom": ["billed_account_user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workspace_forked_from_workspace_id_workspace_id_fk": { + "name": "workspace_forked_from_workspace_id_workspace_id_fk", + "tableFrom": "workspace", + "tableTo": "workspace", + "columnsFrom": ["forked_from_workspace_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "workspace_storage_used_bytes_non_negative": { + "name": "workspace_storage_used_bytes_non_negative", + "value": "\"workspace\".\"storage_used_bytes\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.workspace_byok_keys": { + "name": "workspace_byok_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_api_key": { + "name": "encrypted_api_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_byok_workspace_provider_idx": { + "name": "workspace_byok_workspace_provider_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_byok_keys_workspace_id_workspace_id_fk": { + "name": "workspace_byok_keys_workspace_id_workspace_id_fk", + "tableFrom": "workspace_byok_keys", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_byok_keys_created_by_user_id_fk": { + "name": "workspace_byok_keys_created_by_user_id_fk", + "tableFrom": "workspace_byok_keys", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_environment": { + "name": "workspace_environment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "variables": { + "name": "variables", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_environment_workspace_unique": { + "name": "workspace_environment_workspace_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_environment_workspace_id_workspace_id_fk": { + "name": "workspace_environment_workspace_id_workspace_id_fk", + "tableFrom": "workspace_environment", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_file": { + "name": "workspace_file", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uploaded_by": { + "name": "uploaded_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_file_workspace_id_idx": { + "name": "workspace_file_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_deleted_at_idx": { + "name": "workspace_file_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_workspace_deleted_partial_idx": { + "name": "workspace_file_workspace_deleted_partial_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_file\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_file_workspace_id_workspace_id_fk": { + "name": "workspace_file_workspace_id_workspace_id_fk", + "tableFrom": "workspace_file", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_file_uploaded_by_user_id_fk": { + "name": "workspace_file_uploaded_by_user_id_fk", + "tableFrom": "workspace_file", + "tableTo": "user", + "columnsFrom": ["uploaded_by"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "workspace_file_key_unique": { + "name": "workspace_file_key_unique", + "nullsNotDistinct": false, + "columns": ["key"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_file_collab_state": { + "name": "workspace_file_collab_state", + "schema": "", + "columns": { + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "doc_state": { + "name": "doc_state", + "type": "bytea", + "primaryKey": false, + "notNull": true + }, + "source_hash": { + "name": "source_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workspace_file_collab_state_file_id_workspace_files_id_fk": { + "name": "workspace_file_collab_state_file_id_workspace_files_id_fk", + "tableFrom": "workspace_file_collab_state", + "tableTo": "workspace_files", + "columnsFrom": ["file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_file_search_backfill": { + "name": "workspace_file_search_backfill", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "after_workspace_id": { + "name": "after_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "after_file_id": { + "name": "after_file_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_file_search_build": { + "name": "workspace_file_search_build", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_content_updated_at": { + "name": "source_content_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "workspace_file_search_build_file_idx": { + "name": "workspace_file_search_build_file_idx", + "columns": [ + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_search_build_cleanup_idx": { + "name": "workspace_file_search_build_cleanup_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_file_search_build\".\"expires_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_file_search_chunk": { + "name": "workspace_file_search_chunk", + "schema": "", + "columns": { + "build_id": { + "name": "build_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ordinal": { + "name": "ordinal", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "line_start": { + "name": "line_start", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "fragment": { + "name": "fragment", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "overlap": { + "name": "overlap", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "workspace_file_search_chunk_line_idx": { + "name": "workspace_file_search_chunk_line_idx", + "columns": [ + { + "expression": "build_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "line_start", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "ordinal", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_search_chunk_content_idx": { + "name": "workspace_file_search_chunk_content_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + }, + { + "expression": "content", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": true, + "method": "gin", + "with": { + "fastupdate": "off" + } + } + }, + "foreignKeys": { + "workspace_file_search_chunk_build_id_workspace_file_search_build_id_fk": { + "name": "workspace_file_search_chunk_build_id_workspace_file_search_build_id_fk", + "tableFrom": "workspace_file_search_chunk", + "tableTo": "workspace_file_search_build", + "columnsFrom": ["build_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "workspace_file_search_chunk_pk": { + "name": "workspace_file_search_chunk_pk", + "columns": ["build_id", "ordinal"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "workspace_file_search_chunk_content_size": { + "name": "workspace_file_search_chunk_content_size", + "value": "octet_length(\"workspace_file_search_chunk\".\"content\") <= 8192" + }, + "workspace_file_search_chunk_position": { + "name": "workspace_file_search_chunk_position", + "value": "\"workspace_file_search_chunk\".\"ordinal\" >= 0 AND \"workspace_file_search_chunk\".\"line_start\" > 0 AND \"workspace_file_search_chunk\".\"overlap\" BETWEEN 0 AND 2" + } + }, + "isRLSEnabled": false + }, + "public.workspace_file_search_dispatch_queue": { + "name": "workspace_file_search_dispatch_queue", + "schema": "", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "enqueued_at": { + "name": "enqueued_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_dispatched_at": { + "name": "last_dispatched_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_file_search_dispatch_queue_schedule_idx": { + "name": "workspace_file_search_dispatch_queue_schedule_idx", + "columns": [ + { + "expression": "last_dispatched_at", + "isExpression": false, + "asc": true, + "nulls": "first" + }, + { + "expression": "enqueued_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_file_search_queue_workspace_fk": { + "name": "workspace_file_search_queue_workspace_fk", + "tableFrom": "workspace_file_search_dispatch_queue", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_file_search_index": { + "name": "workspace_file_search_index", + "schema": "", + "columns": { + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_content_updated_at": { + "name": "source_content_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "workspace_file_search_index_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "partial": { + "name": "partial", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "line_count": { + "name": "line_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "indexed_bytes": { + "name": "indexed_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "dispatched_at": { + "name": "dispatched_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_file_search_index_workspace_status_idx": { + "name": "workspace_file_search_index_workspace_status_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_content_updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_search_index_pending_dispatch_idx": { + "name": "workspace_file_search_index_pending_dispatch_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_content_updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_file_search_index\".\"status\" = 'pending' AND \"workspace_file_search_index\".\"dispatched_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_search_index_active_dispatch_idx": { + "name": "workspace_file_search_index_active_dispatch_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "dispatched_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_file_search_index\".\"status\" = 'pending' AND \"workspace_file_search_index\".\"dispatched_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_file_search_index_file_fk": { + "name": "workspace_file_search_index_file_fk", + "tableFrom": "workspace_file_search_index", + "tableTo": "workspace_files", + "columnsFrom": ["file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_file_search_index_workspace_fk": { + "name": "workspace_file_search_index_workspace_fk", + "tableFrom": "workspace_file_search_index", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "workspace_file_search_index_pk": { + "name": "workspace_file_search_index_pk", + "columns": ["file_id", "source_content_updated_at"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_file_search_revision": { + "name": "workspace_file_search_revision", + "schema": "", + "columns": { + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_content_updated_at": { + "name": "source_content_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "workspace_file_search_index_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "build_id": { + "name": "build_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "line_count": { + "name": "line_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "indexed_bytes": { + "name": "indexed_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "chunk_count": { + "name": "chunk_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "dispatched_at": { + "name": "dispatched_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "handoff_expires_at": { + "name": "handoff_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_file_search_revision_workspace_status_idx": { + "name": "workspace_file_search_revision_workspace_status_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_search_revision_build_idx": { + "name": "workspace_file_search_revision_build_idx", + "columns": [ + { + "expression": "build_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_search_revision_pending_idx": { + "name": "workspace_file_search_revision_pending_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_content_updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_file_search_revision\".\"status\" = 'pending' AND \"workspace_file_search_revision\".\"dispatched_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_search_revision_active_idx": { + "name": "workspace_file_search_revision_active_idx", + "columns": [ + { + "expression": "dispatched_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_file_search_revision\".\"status\" = 'pending' AND \"workspace_file_search_revision\".\"dispatched_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_file_search_revision_file_id_workspace_files_id_fk": { + "name": "workspace_file_search_revision_file_id_workspace_files_id_fk", + "tableFrom": "workspace_file_search_revision", + "tableTo": "workspace_files", + "columnsFrom": ["file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_file_search_revision_build_id_workspace_file_search_build_id_fk": { + "name": "workspace_file_search_revision_build_id_workspace_file_search_build_id_fk", + "tableFrom": "workspace_file_search_revision", + "tableTo": "workspace_file_search_build", + "columnsFrom": ["build_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_file_search_segment": { + "name": "workspace_file_search_segment", + "schema": "", + "columns": { + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_content_updated_at": { + "name": "source_content_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "line_number": { + "name": "line_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "segment_number": { + "name": "segment_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "segment_start": { + "name": "segment_start", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "line_length": { + "name": "line_length", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "workspace_file_search_segment_workspace_revision_idx": { + "name": "workspace_file_search_segment_workspace_revision_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_content_updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_search_segment_workspace_content_trgm_idx": { + "name": "workspace_file_search_segment_workspace_content_trgm_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + }, + { + "expression": "content", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "workspace_file_search_segment_file_fk": { + "name": "workspace_file_search_segment_file_fk", + "tableFrom": "workspace_file_search_segment", + "tableTo": "workspace_files", + "columnsFrom": ["file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_file_search_segment_workspace_fk": { + "name": "workspace_file_search_segment_workspace_fk", + "tableFrom": "workspace_file_search_segment", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "workspace_file_search_segment_pk": { + "name": "workspace_file_search_segment_pk", + "columns": ["file_id", "source_content_updated_at", "line_number", "segment_number"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_file_secret_provenance": { + "name": "workspace_file_secret_provenance", + "schema": "", + "columns": { + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "content_updated_at": { + "name": "content_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entries": { + "name": "entries", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workspace_file_secret_provenance_file_id_workspace_files_id_fk": { + "name": "workspace_file_secret_provenance_file_id_workspace_files_id_fk", + "tableFrom": "workspace_file_secret_provenance", + "tableTo": "workspace_files", + "columnsFrom": ["file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "workspace_file_secret_provenance_status_check": { + "name": "workspace_file_secret_provenance_status_check", + "value": "\"workspace_file_secret_provenance\".\"status\" IN ('exact', 'unknown', 'unrecorded')" + } + }, + "isRLSEnabled": false + }, + "public.workspace_file_version": { + "name": "workspace_file_version", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size_bytes": { + "name": "size_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "superseded_at": { + "name": "superseded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "workspace_file_version_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "author_user_ids": { + "name": "author_user_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "restored_from_version": { + "name": "restored_from_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "secret_provenance_status": { + "name": "secret_provenance_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret_provenance_entries": { + "name": "secret_provenance_entries", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_file_version_file_version_unique": { + "name": "workspace_file_version_file_version_unique", + "columns": [ + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_version_key_unique": { + "name": "workspace_file_version_key_unique", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_version_workspace_id_idx": { + "name": "workspace_file_version_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_version_workspace_superseded_idx": { + "name": "workspace_file_version_workspace_superseded_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "superseded_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_file_version\".\"superseded_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_file_version_file_id_workspace_files_id_fk": { + "name": "workspace_file_version_file_id_workspace_files_id_fk", + "tableFrom": "workspace_file_version", + "tableTo": "workspace_files", + "columnsFrom": ["file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_file_version_workspace_id_workspace_id_fk": { + "name": "workspace_file_version_workspace_id_workspace_id_fk", + "tableFrom": "workspace_file_version", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "workspace_file_version_provenance_status_check": { + "name": "workspace_file_version_provenance_status_check", + "value": "\"workspace_file_version\".\"secret_provenance_status\" IS NULL OR \"workspace_file_version\".\"secret_provenance_status\" IN ('exact', 'unknown', 'unrecorded')" + } + }, + "isRLSEnabled": false + }, + "public.workspace_files": { + "name": "workspace_files", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "folder_id": { + "name": "folder_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "context": { + "name": "context", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "original_name": { + "name": "original_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size_bytes": { + "name": "size_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "content_updated_at": { + "name": "content_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "date_trunc('milliseconds', now())" + }, + "secret_provenance_version": { + "name": "secret_provenance_version", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "workspace_files_key_active_unique": { + "name": "workspace_files_key_active_unique", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workspace_files\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_workspace_folder_name_active_unique": { + "name": "workspace_files_workspace_folder_name_active_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"folder_id\", '')", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "original_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workspace_files\".\"deleted_at\" IS NULL AND \"workspace_files\".\"context\" = 'workspace' AND \"workspace_files\".\"workspace_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_workspace_active_keyset_idx": { + "name": "workspace_files_workspace_active_keyset_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_files\".\"deleted_at\" IS NULL AND \"workspace_files\".\"context\" = 'workspace' AND \"workspace_files\".\"workspace_id\" IS NOT NULL", + "concurrently": true, + "method": "btree", + "with": {} + }, + "workspace_files_chat_display_name_unique": { + "name": "workspace_files_chat_display_name_unique", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "display_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workspace_files\".\"context\" = 'mothership' AND \"workspace_files\".\"chat_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_organization_id_idx": { + "name": "workspace_files_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_key_idx": { + "name": "workspace_files_key_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_user_id_idx": { + "name": "workspace_files_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_workspace_id_idx": { + "name": "workspace_files_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_folder_id_idx": { + "name": "workspace_files_folder_id_idx", + "columns": [ + { + "expression": "folder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_context_idx": { + "name": "workspace_files_context_idx", + "columns": [ + { + "expression": "context", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_chat_id_idx": { + "name": "workspace_files_chat_id_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_deleted_at_idx": { + "name": "workspace_files_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_workspace_deleted_partial_idx": { + "name": "workspace_files_workspace_deleted_partial_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_files\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_files_user_id_user_id_fk": { + "name": "workspace_files_user_id_user_id_fk", + "tableFrom": "workspace_files", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_files_workspace_id_workspace_id_fk": { + "name": "workspace_files_workspace_id_workspace_id_fk", + "tableFrom": "workspace_files", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_files_organization_id_organization_id_fk": { + "name": "workspace_files_organization_id_organization_id_fk", + "tableFrom": "workspace_files", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_files_folder_id_folder_id_fk": { + "name": "workspace_files_folder_id_folder_id_fk", + "tableFrom": "workspace_files", + "tableTo": "folder", + "columnsFrom": ["folder_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "workspace_files_chat_id_copilot_chats_id_fk": { + "name": "workspace_files_chat_id_copilot_chats_id_fk", + "tableFrom": "workspace_files", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "workspace_files_organization_binding_check": { + "name": "workspace_files_organization_binding_check", + "value": "\"workspace_files\".\"organization_id\" IS NULL OR (\"workspace_files\".\"workspace_id\" IS NULL AND \"workspace_files\".\"context\" = 'knowledge-base' AND \"workspace_files\".\"folder_id\" IS NULL AND \"workspace_files\".\"chat_id\" IS NULL)" + } + }, + "isRLSEnabled": false + }, + "public.workspace_fork_block_map": { + "name": "workspace_fork_block_map", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "child_workspace_id": { + "name": "child_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_workflow_id": { + "name": "parent_workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_block_id": { + "name": "parent_block_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "child_workflow_id": { + "name": "child_workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "child_block_id": { + "name": "child_block_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_fork_block_map_child_ws_parent_unique": { + "name": "workspace_fork_block_map_child_ws_parent_unique", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_block_map_child_ws_child_unique": { + "name": "workspace_fork_block_map_child_ws_child_unique", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "child_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_block_map_child_ws_parent_wf_idx": { + "name": "workspace_fork_block_map_child_ws_parent_wf_idx", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_block_map_child_ws_child_wf_idx": { + "name": "workspace_fork_block_map_child_ws_child_wf_idx", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "child_workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_fork_block_map_child_workspace_id_workspace_id_fk": { + "name": "workspace_fork_block_map_child_workspace_id_workspace_id_fk", + "tableFrom": "workspace_fork_block_map", + "tableTo": "workspace", + "columnsFrom": ["child_workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_fork_dependent_value": { + "name": "workspace_fork_dependent_value", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "child_workspace_id": { + "name": "child_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_workflow_id": { + "name": "target_workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_block_id": { + "name": "target_block_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sub_block_key": { + "name": "sub_block_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_fork_dependent_value_child_ws_wf_idx": { + "name": "workspace_fork_dependent_value_child_ws_wf_idx", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_dependent_value_field_unique": { + "name": "workspace_fork_dependent_value_field_unique", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sub_block_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_fork_dependent_value_child_workspace_id_workspace_id_fk": { + "name": "workspace_fork_dependent_value_child_workspace_id_workspace_id_fk", + "tableFrom": "workspace_fork_dependent_value", + "tableTo": "workspace", + "columnsFrom": ["child_workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_fork_promote_run": { + "name": "workspace_fork_promote_run", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "child_workspace_id": { + "name": "child_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_workspace_id": { + "name": "source_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_workspace_id": { + "name": "target_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "direction": { + "name": "direction", + "type": "workspace_fork_promote_direction", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "snapshot": { + "name": "snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_fork_promote_run_child_ws_target_unique": { + "name": "workspace_fork_promote_run_child_ws_target_unique", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_promote_run_target_ws_idx": { + "name": "workspace_fork_promote_run_target_ws_idx", + "columns": [ + { + "expression": "target_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_fork_promote_run_child_workspace_id_workspace_id_fk": { + "name": "workspace_fork_promote_run_child_workspace_id_workspace_id_fk", + "tableFrom": "workspace_fork_promote_run", + "tableTo": "workspace", + "columnsFrom": ["child_workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_fork_promote_run_created_by_user_id_fk": { + "name": "workspace_fork_promote_run_created_by_user_id_fk", + "tableFrom": "workspace_fork_promote_run", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_fork_resource_map": { + "name": "workspace_fork_resource_map", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "child_workspace_id": { + "name": "child_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_type": { + "name": "resource_type", + "type": "workspace_fork_resource_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "parent_resource_id": { + "name": "parent_resource_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "child_resource_id": { + "name": "child_resource_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_fork_resource_map_child_ws_idx": { + "name": "workspace_fork_resource_map_child_ws_idx", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_resource_map_child_ws_type_idx": { + "name": "workspace_fork_resource_map_child_ws_type_idx", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_resource_map_child_type_parent_unique": { + "name": "workspace_fork_resource_map_child_type_parent_unique", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_fork_resource_map_child_workspace_id_workspace_id_fk": { + "name": "workspace_fork_resource_map_child_workspace_id_workspace_id_fk", + "tableFrom": "workspace_fork_resource_map", + "tableTo": "workspace", + "columnsFrom": ["child_workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_fork_resource_map_created_by_user_id_fk": { + "name": "workspace_fork_resource_map_created_by_user_id_fk", + "tableFrom": "workspace_fork_resource_map", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_fork_workflow_sync": { + "name": "workspace_fork_workflow_sync", + "schema": "", + "columns": { + "deployment_operation_id": { + "name": "deployment_operation_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "child_workspace_id": { + "name": "child_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_workflow_id": { + "name": "source_workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_workflow_id": { + "name": "target_workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_deployment_version_id": { + "name": "source_deployment_version_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sequence": { + "name": "sequence", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "identity": { + "type": "always", + "name": "workspace_fork_workflow_sync_sequence_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "promote_run_id": { + "name": "promote_run_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "activated_at": { + "name": "activated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "rollback_operation_id": { + "name": "rollback_operation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rolled_back_at": { + "name": "rolled_back_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "workspace_fork_workflow_sync_baseline_idx": { + "name": "workspace_fork_workflow_sync_baseline_idx", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sequence", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_fork_workflow_sync\".\"activated_at\" IS NOT NULL AND \"workspace_fork_workflow_sync\".\"rolled_back_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_workflow_sync_run_idx": { + "name": "workspace_fork_workflow_sync_run_idx", + "columns": [ + { + "expression": "promote_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_workflow_sync_rollback_idx": { + "name": "workspace_fork_workflow_sync_rollback_idx", + "columns": [ + { + "expression": "rollback_operation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_fork_workflow_sync\".\"rollback_operation_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_workflow_sync_source_idx": { + "name": "workspace_fork_workflow_sync_source_idx", + "columns": [ + { + "expression": "source_workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_workflow_sync_target_idx": { + "name": "workspace_fork_workflow_sync_target_idx", + "columns": [ + { + "expression": "target_workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_workflow_sync_child_workspace_idx": { + "name": "workspace_fork_workflow_sync_child_workspace_idx", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_fork_workflow_sync_child_workspace_id_workspace_id_fk": { + "name": "workspace_fork_workflow_sync_child_workspace_id_workspace_id_fk", + "tableFrom": "workspace_fork_workflow_sync", + "tableTo": "workspace", + "columnsFrom": ["child_workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_fork_workflow_sync_source_workflow_id_workflow_id_fk": { + "name": "workspace_fork_workflow_sync_source_workflow_id_workflow_id_fk", + "tableFrom": "workspace_fork_workflow_sync", + "tableTo": "workflow", + "columnsFrom": ["source_workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_fork_workflow_sync_target_workflow_id_workflow_id_fk": { + "name": "workspace_fork_workflow_sync_target_workflow_id_workflow_id_fk", + "tableFrom": "workspace_fork_workflow_sync", + "tableTo": "workflow", + "columnsFrom": ["target_workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_operation_receipt": { + "name": "workspace_operation_receipt", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "request_hash": { + "name": "request_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "report": { + "name": "report", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_operation_receipt_request_unique": { + "name": "workspace_operation_receipt_request_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_operation_receipt_workspace_created_idx": { + "name": "workspace_operation_receipt_workspace_created_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_operation_receipt_workspace_id_workspace_id_fk": { + "name": "workspace_operation_receipt_workspace_id_workspace_id_fk", + "tableFrom": "workspace_operation_receipt", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_sandbox": { + "name": "workspace_sandbox", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "sandbox_language", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "dependencies": { + "name": "dependencies", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "cli_tools": { + "name": "cli_tools", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "system_packages": { + "name": "system_packages", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "spec_hash": { + "name": "spec_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_sandbox_workspace_name_unique": { + "name": "workspace_sandbox_workspace_name_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_sandbox_workspace_idx": { + "name": "workspace_sandbox_workspace_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_sandbox_spec_hash_idx": { + "name": "workspace_sandbox_spec_hash_idx", + "columns": [ + { + "expression": "spec_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_sandbox_workspace_id_workspace_id_fk": { + "name": "workspace_sandbox_workspace_id_workspace_id_fk", + "tableFrom": "workspace_sandbox", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_sandbox_created_by_user_id_fk": { + "name": "workspace_sandbox_created_by_user_id_fk", + "tableFrom": "workspace_sandbox", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_visit": { + "name": "workspace_visit", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "visited_at": { + "name": "visited_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_visit_workspace_idx": { + "name": "workspace_visit_workspace_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_visit_user_id_user_id_fk": { + "name": "workspace_visit_user_id_user_id_fk", + "tableFrom": "workspace_visit", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_visit_workspace_id_workspace_id_fk": { + "name": "workspace_visit_workspace_id_workspace_id_fk", + "tableFrom": "workspace_visit", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "workspace_visit_user_id_workspace_id_pk": { + "name": "workspace_visit_user_id_workspace_id_pk", + "columns": ["user_id", "workspace_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.academy_cert_status": { + "name": "academy_cert_status", + "schema": "public", + "values": ["active", "revoked", "expired"] + }, + "public.background_work_kind": { + "name": "background_work_kind", + "schema": "public", + "values": ["deployment_side_effects", "fork_content_copy", "fork_sync", "fork_rollback"] + }, + "public.background_work_status_value": { + "name": "background_work_status_value", + "schema": "public", + "values": ["pending", "processing", "completed", "completed_with_warnings", "failed"] + }, + "public.billing_blocked_reason": { + "name": "billing_blocked_reason", + "schema": "public", + "values": ["payment_failed", "dispute"] + }, + "public.billing_entity_type": { + "name": "billing_entity_type", + "schema": "public", + "values": ["user", "organization"] + }, + "public.chat_type": { + "name": "chat_type", + "schema": "public", + "values": ["mothership", "copilot"] + }, + "public.copilot_async_tool_status": { + "name": "copilot_async_tool_status", + "schema": "public", + "values": ["pending", "running", "completed", "failed", "cancelled", "delivered"] + }, + "public.copilot_run_status": { + "name": "copilot_run_status", + "schema": "public", + "values": ["active", "paused_waiting_for_tool", "resuming", "complete", "error", "cancelled"] + }, + "public.copilot_tool_permission_decision": { + "name": "copilot_tool_permission_decision", + "schema": "public", + "values": ["allow", "allow_chat", "always_allow", "skip"] + }, + "public.credential_group_enrollment_status": { + "name": "credential_group_enrollment_status", + "schema": "public", + "values": ["invited", "delivery_failed", "in_progress", "completed", "revoked"] + }, + "public.credential_group_status": { + "name": "credential_group_status", + "schema": "public", + "values": ["active", "disabled"] + }, + "public.credential_member_role": { + "name": "credential_member_role", + "schema": "public", + "values": ["admin", "member"] + }, + "public.credential_member_status": { + "name": "credential_member_status", + "schema": "public", + "values": ["active", "pending", "revoked"] + }, + "public.credential_type": { + "name": "credential_type", + "schema": "public", + "values": [ + "oauth", + "managed_oauth", + "managed_mcp", + "env_workspace", + "env_personal", + "service_account", + "personal_token" + ] + }, + "public.data_drain_cadence": { + "name": "data_drain_cadence", + "schema": "public", + "values": ["hourly", "daily"] + }, + "public.data_drain_destination": { + "name": "data_drain_destination", + "schema": "public", + "values": ["s3", "gcs", "azure_blob", "datadog", "bigquery", "snowflake", "webhook"] + }, + "public.data_drain_run_status": { + "name": "data_drain_run_status", + "schema": "public", + "values": ["running", "success", "failed"] + }, + "public.data_drain_run_trigger": { + "name": "data_drain_run_trigger", + "schema": "public", + "values": ["cron", "manual"] + }, + "public.data_drain_source": { + "name": "data_drain_source", + "schema": "public", + "values": ["workflow_logs", "job_logs", "audit_logs", "copilot_chats", "copilot_runs"] + }, + "public.execution_large_value_reference_source": { + "name": "execution_large_value_reference_source", + "schema": "public", + "values": ["execution_log", "paused_snapshot"] + }, + "public.folder_resource_type": { + "name": "folder_resource_type", + "schema": "public", + "values": ["workflow", "file", "knowledge_base", "table"] + }, + "public.invitation_kind": { + "name": "invitation_kind", + "schema": "public", + "values": ["organization", "workspace"] + }, + "public.invitation_membership_intent": { + "name": "invitation_membership_intent", + "schema": "public", + "values": ["internal", "external"] + }, + "public.invitation_status": { + "name": "invitation_status", + "schema": "public", + "values": ["pending", "accepted", "rejected", "cancelled", "expired"] + }, + "public.managed_oauth_credential_status": { + "name": "managed_oauth_credential_status", + "schema": "public", + "values": ["active", "needs_reauth", "revoked"] + }, + "public.permission_type": { + "name": "permission_type", + "schema": "public", + "values": ["admin", "write", "read"] + }, + "public.sandbox_image_status": { + "name": "sandbox_image_status", + "schema": "public", + "values": ["pending", "building", "ready", "failed"] + }, + "public.sandbox_language": { + "name": "sandbox_language", + "schema": "public", + "values": ["javascript", "python"] + }, + "public.secret_usage_scope": { + "name": "secret_usage_scope", + "schema": "public", + "values": ["workspace", "personal"] + }, + "public.secret_usage_source": { + "name": "secret_usage_source", + "schema": "public", + "values": ["workflow", "copilot", "mcp"] + }, + "public.upload_session_method": { + "name": "upload_session_method", + "schema": "public", + "values": ["put", "multipart"] + }, + "public.upload_session_provider": { + "name": "upload_session_provider", + "schema": "public", + "values": ["local", "s3", "blob", "gcs"] + }, + "public.upload_session_purpose": { + "name": "upload_session_purpose", + "schema": "public", + "values": [ + "workspace_file", + "table_import", + "knowledge_document", + "profile_picture", + "workspace_logo", + "organization_logo", + "mothership_attachment", + "execution_attachment" + ] + }, + "public.upload_session_status": { + "name": "upload_session_status", + "schema": "public", + "values": [ + "uploading", + "completing", + "finalizing", + "completed", + "aborting", + "aborted", + "failed", + "expired" + ] + }, + "public.usage_log_category": { + "name": "usage_log_category", + "schema": "public", + "values": ["model", "fixed", "tool", "model_unbilled"] + }, + "public.usage_log_source": { + "name": "usage_log_source", + "schema": "public", + "values": [ + "workflow", + "wand", + "copilot", + "workspace-chat", + "mcp_copilot", + "mothership_block", + "knowledge-base", + "voice-input", + "enrichment", + "voice-output", + "api-tool", + "workflow-test" + ] + }, + "public.workspace_file_search_index_status": { + "name": "workspace_file_search_index_status", + "schema": "public", + "values": ["pending", "ready", "skipped", "failed"] + }, + "public.workspace_file_version_source": { + "name": "workspace_file_version_source", + "schema": "public", + "values": ["upload", "user", "api", "copilot", "workflow", "collab", "revert", "unknown"] + }, + "public.workspace_fork_promote_direction": { + "name": "workspace_fork_promote_direction", + "schema": "public", + "values": ["push", "pull"] + }, + "public.workspace_fork_resource_type": { + "name": "workspace_fork_resource_type", + "schema": "public", + "values": [ + "workflow", + "oauth_credential", + "service_account_credential", + "env_var", + "table", + "knowledge_base", + "knowledge_document", + "file", + "file_folder", + "mcp_server", + "workflow_mcp_server", + "custom_block", + "custom_tool", + "skill", + "sandbox" + ] + }, + "public.workspace_mode": { + "name": "workspace_mode", + "schema": "public", + "values": ["personal", "organization", "grandfathered_shared"] + } + }, + "schemas": {}, + "sequences": { + "public.copilot_async_tool_calls_persist_seq": { + "name": "copilot_async_tool_calls_persist_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/packages/db/migrations/meta/_journal.json b/packages/db/migrations/meta/_journal.json index b58b39d3b77..e418879d0b2 100644 --- a/packages/db/migrations/meta/_journal.json +++ b/packages/db/migrations/meta/_journal.json @@ -2794,6 +2794,13 @@ "when": 1791313130367, "tag": "0399_tool_call_persist_seq", "breakpoints": true + }, + { + "idx": 400, + "version": "7", + "when": 1791399734934, + "tag": "0400_workflow_tests", + "breakpoints": true } ] } diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 94965b05fcc..0f01906c56d 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -2474,6 +2474,97 @@ export const dashboard = pgTable( }) ) +/** + * A workflow test file: one concern, read as `tests/.test.js`. `tests create` sets the + * name, title, and description; the source is a workspace file with `context = 'test'`, so it + * keeps versions and Sim's file edits, and `cases` is what its last accepted write declared. + */ +export const workflowTest = pgTable( + 'workflow_test', + { + id: text('id').primaryKey(), + workspaceId: text('workspace_id') + .notNull() + .references(() => workspace.id, { onDelete: 'cascade' }), + name: text('name').notNull(), + title: text('title').notNull(), + description: text('description'), + bodyFileId: text('body_file_id') + .notNull() + .references(() => workspaceFiles.id), + /** Every `describe > it` path in the file with its mode (`run`, `only`, `skip`). */ + cases: jsonb('cases').notNull().default('[]'), + /** sha256 of the source `cases` was read from; a run reporting another hash is stale. */ + sourceHash: text('source_hash').notNull(), + createdByUserId: text('created_by_user_id').references(() => user.id, { + onDelete: 'set null', + }), + deletedAt: timestamp('deleted_at'), + createdAt: timestamp('created_at').notNull().defaultNow(), + updatedAt: timestamp('updated_at').notNull().defaultNow(), + }, + (table) => ({ + workspaceNameUnique: uniqueIndex('workflow_test_workspace_name_unique') + .on(table.workspaceId, table.name) + .where(sql`${table.deletedAt} IS NULL`), + bodyFileUnique: uniqueIndex('workflow_test_body_file_unique').on(table.bodyFileId), + }) +) + +/** One run of a test file against the draft or deployed workflows. */ +export const workflowTestRun = pgTable( + 'workflow_test_run', + { + id: text('id').primaryKey(), + testId: text('test_id') + .notNull() + .references(() => workflowTest.id, { onDelete: 'cascade' }), + workspaceId: text('workspace_id') + .notNull() + .references(() => workspace.id, { onDelete: 'cascade' }), + version: text('version').notNull(), + status: text('status').notNull().default('running'), + passed: integer('passed').notNull().default(0), + failed: integer('failed').notNull().default(0), + skipped: integer('skipped').notNull().default(0), + /** The sandbox report: every case's checks, judge reasons, logs, and executions. */ + report: jsonb('report'), + /** Why the file produced no report: it did not load, or the run itself failed. */ + error: text('error'), + /** sha256 of the source this run executed; null when it failed before reading the file. */ + sourceHash: text('source_hash'), + /** `{ workflowId, deploymentVersionId }` for each workflow the run executed; null draft ids. */ + ranAgainst: jsonb('ran_against'), + /** Each case's status (`running`, `pass`, `fail`, `skip`) by `describe > it` path, as it runs. */ + progress: jsonb('progress'), + triggeredByActor: jsonb('triggered_by_actor').notNull(), + triggeredByUserId: text('triggered_by_user_id').references(() => user.id, { + onDelete: 'set null', + }), + startedAt: timestamp('started_at').notNull().defaultNow(), + completedAt: timestamp('completed_at'), + }, + (table) => ({ + testVersionStartedIdx: index('workflow_test_run_test_version_started_idx').on( + table.testId, + table.version, + table.startedAt + ), + versionCheck: check( + 'workflow_test_run_version_check', + sql`${table.version} IN ('draft', 'deployed')` + ), + statusCheck: check( + 'workflow_test_run_status_check', + sql`${table.status} IN ('running', 'passed', 'failed', 'error')` + ), + completedCheck: check( + 'workflow_test_run_completed_check', + sql`(${table.status} = 'running') = (${table.completedAt} IS NULL)` + ), + }) +) + export const workspaceFiles = pgTable( 'workspace_files', { @@ -5351,6 +5442,7 @@ export const usageLogSourceEnum = pgEnum('usage_log_source', [ 'enrichment', 'voice-output', 'api-tool', + 'workflow-test', ]) /** Content-free organization Search activity, independent of billable model usage. */ diff --git a/packages/testing/src/mocks/schema-tables.generated.ts b/packages/testing/src/mocks/schema-tables.generated.ts index b8546c5282b..e3b5854bdc6 100644 --- a/packages/testing/src/mocks/schema-tables.generated.ts +++ b/packages/testing/src/mocks/schema-tables.generated.ts @@ -599,6 +599,39 @@ export const GENERATED_SCHEMA_TABLES = { 'createdAt', 'updatedAt', ], + workflowTest: [ + 'id', + 'workspaceId', + 'name', + 'title', + 'description', + 'bodyFileId', + 'cases', + 'sourceHash', + 'createdByUserId', + 'deletedAt', + 'createdAt', + 'updatedAt', + ], + workflowTestRun: [ + 'id', + 'testId', + 'workspaceId', + 'version', + 'status', + 'passed', + 'failed', + 'skipped', + 'report', + 'error', + 'sourceHash', + 'ranAgainst', + 'progress', + 'triggeredByActor', + 'triggeredByUserId', + 'startedAt', + 'completedAt', + ], workspaceFiles: [ 'id', 'key', @@ -2150,6 +2183,7 @@ export const GENERATED_SCHEMA_ENUMS = { 'enrichment', 'voice-output', 'api-tool', + 'workflow-test', ], credentialTypeEnum: [ 'oauth', diff --git a/scripts/check-tool-registry-boundary.baseline.json b/scripts/check-tool-registry-boundary.baseline.json index 869afd33ed9..7b11b3de840 100644 --- a/scripts/check-tool-registry-boundary.baseline.json +++ b/scripts/check-tool-registry-boundary.baseline.json @@ -6,66 +6,66 @@ }, "entries": { "app/api/v2/blocks/[blockId]/route.ts": { - "modules": 1751, + "modules": 1759, "gateways": { "apps/sim/triggers/index.ts": 536, "apps/sim/triggers/registry.ts": 534, - "apps/sim/lib/api/server/routes/index.ts": 519, - "apps/sim/lib/api/server/routes/internal-json-route.ts": 463, - "apps/sim/lib/auth/index.ts": 449, + "apps/sim/lib/api/server/routes/index.ts": 527, + "apps/sim/lib/api/server/routes/internal-json-route.ts": 471, + "apps/sim/lib/auth/index.ts": 457, "apps/sim/blocks/registry.ts": 370, "apps/sim/lib/webhooks/providers/index.ts": 124, "apps/sim/lib/webhooks/providers/registry.ts": 122 } }, "app/api/v2/blocks/route.ts": { - "modules": 1748, + "modules": 1756, "gateways": { "apps/sim/blocks/registry.ts": 910, "apps/sim/triggers/index.ts": 536, "apps/sim/triggers/registry.ts": 534, - "apps/sim/lib/api/server/routes/index.ts": 512, - "apps/sim/lib/api/server/routes/internal-json-route.ts": 464, - "apps/sim/lib/auth/index.ts": 450, + "apps/sim/lib/api/server/routes/index.ts": 520, + "apps/sim/lib/api/server/routes/internal-json-route.ts": 472, + "apps/sim/lib/auth/index.ts": 458, "apps/sim/lib/webhooks/providers/index.ts": 125, "apps/sim/lib/webhooks/providers/registry.ts": 122 } }, "app/api/v2/connector-types/route.ts": { - "modules": 1817, + "modules": 1825, "gateways": { "apps/sim/blocks/registry.ts": 911, "apps/sim/triggers/index.ts": 536, "apps/sim/triggers/registry.ts": 534, - "apps/sim/lib/api/server/routes/index.ts": 510, - "apps/sim/lib/api/server/routes/internal-json-route.ts": 462, - "apps/sim/lib/auth/index.ts": 448, + "apps/sim/lib/api/server/routes/index.ts": 518, + "apps/sim/lib/api/server/routes/internal-json-route.ts": 470, + "apps/sim/lib/auth/index.ts": 456, "apps/sim/lib/webhooks/providers/index.ts": 125, "apps/sim/lib/webhooks/providers/registry.ts": 122 } }, "app/api/v2/tools/[toolId]/route.ts": { - "modules": 1746, + "modules": 1754, "gateways": { "apps/sim/blocks/registry.ts": 910, "apps/sim/triggers/index.ts": 536, "apps/sim/triggers/registry.ts": 534, - "apps/sim/lib/api/server/routes/index.ts": 520, - "apps/sim/lib/api/server/routes/internal-json-route.ts": 464, - "apps/sim/lib/auth/index.ts": 450, + "apps/sim/lib/api/server/routes/index.ts": 528, + "apps/sim/lib/api/server/routes/internal-json-route.ts": 472, + "apps/sim/lib/auth/index.ts": 458, "apps/sim/lib/webhooks/providers/index.ts": 125, "apps/sim/lib/webhooks/providers/registry.ts": 122 } }, "app/api/v2/tools/route.ts": { - "modules": 1747, + "modules": 1755, "gateways": { "apps/sim/blocks/registry.ts": 910, "apps/sim/triggers/index.ts": 536, "apps/sim/triggers/registry.ts": 534, - "apps/sim/lib/api/server/routes/index.ts": 512, - "apps/sim/lib/api/server/routes/internal-json-route.ts": 464, - "apps/sim/lib/auth/index.ts": 450, + "apps/sim/lib/api/server/routes/index.ts": 520, + "apps/sim/lib/api/server/routes/internal-json-route.ts": 472, + "apps/sim/lib/auth/index.ts": 458, "apps/sim/lib/webhooks/providers/index.ts": 125, "apps/sim/lib/webhooks/providers/registry.ts": 122 } @@ -87,13 +87,13 @@ "gateways": {} }, "app/workspace/[workspaceId]/chat/[chatId]/page.tsx": { - "modules": 3417, + "modules": 3433, "gateways": { - "apps/sim/app/workspace/[workspaceId]/home/home.tsx": 1704, - "apps/sim/app/workspace/[workspaceId]/home/components/chat-resource-panel.tsx": 1055, - "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx": 1051, - "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/index.ts": 718, - "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/index.ts": 715, + "apps/sim/app/workspace/[workspaceId]/home/home.tsx": 1712, + "apps/sim/app/workspace/[workspaceId]/home/components/chat-resource-panel.tsx": 1060, + "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx": 1056, + "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/index.ts": 723, + "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/index.ts": 720, "apps/sim/triggers/registry.ts": 534, "apps/sim/blocks/registry.ts": 339, "apps/sim/lib/auth/index.ts": 290 @@ -129,7 +129,7 @@ "gateways": {} }, "app/workspace/[workspaceId]/files/[fileId]/page.tsx": { - "modules": 2366, + "modules": 2374, "gateways": { "apps/sim/triggers/registry.ts": 534, "apps/sim/app/workspace/[workspaceId]/files/files.tsx": 513, @@ -157,7 +157,7 @@ "gateways": {} }, "app/workspace/[workspaceId]/files/page.tsx": { - "modules": 2365, + "modules": 2373, "gateways": { "apps/sim/triggers/registry.ts": 534, "apps/sim/app/workspace/[workspaceId]/files/files.tsx": 514, @@ -178,13 +178,13 @@ "gateways": {} }, "app/workspace/[workspaceId]/home/page.tsx": { - "modules": 3417, + "modules": 3433, "gateways": { - "apps/sim/app/workspace/[workspaceId]/home/home.tsx": 1704, - "apps/sim/app/workspace/[workspaceId]/home/components/chat-resource-panel.tsx": 1055, - "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx": 1051, - "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/index.ts": 718, - "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/index.ts": 715, + "apps/sim/app/workspace/[workspaceId]/home/home.tsx": 1712, + "apps/sim/app/workspace/[workspaceId]/home/components/chat-resource-panel.tsx": 1060, + "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx": 1056, + "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/index.ts": 723, + "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/index.ts": 720, "apps/sim/triggers/registry.ts": 534, "apps/sim/blocks/registry.ts": 339, "apps/sim/lib/auth/index.ts": 290 @@ -279,7 +279,7 @@ "gateways": {} }, "app/workspace/[workspaceId]/knowledge/page.tsx": { - "modules": 2528, + "modules": 2536, "gateways": { "apps/sim/triggers/registry.ts": 534, "apps/sim/app/workspace/[workspaceId]/knowledge/prefetch.ts": 441, @@ -292,10 +292,10 @@ } }, "app/workspace/[workspaceId]/layout.tsx": { - "modules": 2381, + "modules": 2390, "gateways": { "apps/sim/triggers/registry.ts": 534, - "apps/sim/lib/auth/index.ts": 431, + "apps/sim/lib/auth/index.ts": 437, "apps/sim/blocks/registry.ts": 357, "apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx": 279, "apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/index.ts": 151, @@ -342,12 +342,12 @@ "gateways": {} }, "app/workspace/[workspaceId]/settings/[section]/page.tsx": { - "modules": 2911, + "modules": 2922, "gateways": { - "apps/sim/app/workspace/[workspaceId]/settings/[section]/settings.tsx": 1149, - "apps/sim/app/workspace/[workspaceId]/settings/section-warmers.ts": 958, + "apps/sim/app/workspace/[workspaceId]/settings/[section]/settings.tsx": 1152, + "apps/sim/app/workspace/[workspaceId]/settings/section-warmers.ts": 960, "apps/sim/triggers/registry.ts": 534, - "apps/sim/lib/auth/index.ts": 413, + "apps/sim/lib/auth/index.ts": 420, "apps/sim/ee/workspace-forking/components/forks.tsx": 405, "apps/sim/ee/workspace-forking/components/fork-sync-detail-view/fork-sync-detail-view.tsx": 397, "apps/sim/ee/workspace-forking/components/fork-sync/fork-sync-view.tsx": 389, @@ -363,16 +363,16 @@ "gateways": {} }, "app/workspace/[workspaceId]/settings/billing/credit-usage/page.tsx": { - "modules": 1700, + "modules": 1708, "gateways": { - "apps/sim/lib/auth/index.ts": 1544, + "apps/sim/lib/auth/index.ts": 1552, "apps/sim/blocks/registry.ts": 914, "apps/sim/blocks/registry-maps.ts": 911, "apps/sim/triggers/index.ts": 536, "apps/sim/triggers/registry.ts": 534, "apps/sim/lib/webhooks/providers/index.ts": 127, "apps/sim/lib/webhooks/providers/registry.ts": 124, - "apps/sim/lib/uploads/utils/file-utils.server.ts": 54 + "apps/sim/lib/uploads/utils/file-utils.server.ts": 59 } }, "app/workspace/[workspaceId]/settings/error.tsx": { @@ -380,9 +380,9 @@ "gateways": {} }, "app/workspace/[workspaceId]/settings/layout.tsx": { - "modules": 2269, + "modules": 2273, "gateways": { - "apps/sim/app/workspace/[workspaceId]/settings/section-warmers.ts": 2232, + "apps/sim/app/workspace/[workspaceId]/settings/section-warmers.ts": 2236, "apps/sim/triggers/registry.ts": 574, "apps/sim/ee/workspace-forking/components/forks.tsx": 409, "apps/sim/ee/workspace-forking/components/fork-sync-detail-view/fork-sync-detail-view.tsx": 401, @@ -431,16 +431,16 @@ "gateways": {} }, "app/workspace/[workspaceId]/settings/usage/events/page.tsx": { - "modules": 1707, + "modules": 1715, "gateways": { - "apps/sim/lib/auth/index.ts": 1544, + "apps/sim/lib/auth/index.ts": 1552, "apps/sim/blocks/registry.ts": 914, "apps/sim/blocks/registry-maps.ts": 911, "apps/sim/triggers/index.ts": 536, "apps/sim/triggers/registry.ts": 534, "apps/sim/lib/webhooks/providers/index.ts": 127, "apps/sim/lib/webhooks/providers/registry.ts": 124, - "apps/sim/lib/uploads/utils/file-utils.server.ts": 54 + "apps/sim/lib/uploads/utils/file-utils.server.ts": 59 } }, "app/workspace/[workspaceId]/skills/[skillId]/page.tsx": { @@ -516,16 +516,63 @@ "gateways": {} }, "app/workspace/[workspaceId]/tables/page.tsx": { - "modules": 2068, + "modules": 2077, "gateways": { "apps/sim/triggers/registry.ts": 534, - "apps/sim/lib/auth/index.ts": 459, + "apps/sim/lib/auth/index.ts": 467, "apps/sim/blocks/registry.ts": 365, "apps/sim/app/workspace/[workspaceId]/tables/tables.tsx": 205, "apps/sim/lib/webhooks/providers/index.ts": 124, "apps/sim/lib/webhooks/providers/registry.ts": 122, - "apps/sim/app/workspace/[workspaceId]/tables/prefetch.ts": 102, - "apps/sim/app/workspace/[workspaceId]/lib/authorize-resource-prefetch.ts": 54 + "apps/sim/app/workspace/[workspaceId]/tables/prefetch.ts": 103, + "apps/sim/lib/uploads/utils/file-utils.server.ts": 57 + } + }, + "app/workspace/[workspaceId]/tests/[name]/loading.tsx": { + "modules": 15, + "gateways": {} + }, + "app/workspace/[workspaceId]/tests/[name]/page.tsx": { + "modules": 1921, + "gateways": { + "apps/sim/app/workspace/[workspaceId]/tests/components/index.ts": 1920, + "apps/sim/app/workspace/[workspaceId]/tests/components/test-detail.tsx": 1917, + "apps/sim/triggers/registry.ts": 574, + "apps/sim/blocks/registry.ts": 357, + "apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx": 337, + "apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/index.ts": 289, + "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/index.ts": 281, + "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx": 207 + } + }, + "app/workspace/[workspaceId]/tests/layout.tsx": { + "modules": 1921, + "gateways": { + "apps/sim/app/workspace/[workspaceId]/tests/components/index.ts": 1920, + "apps/sim/app/workspace/[workspaceId]/tests/components/test-detail.tsx": 1917, + "apps/sim/triggers/registry.ts": 574, + "apps/sim/blocks/registry.ts": 357, + "apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx": 337, + "apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/index.ts": 289, + "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/index.ts": 281, + "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx": 207 + } + }, + "app/workspace/[workspaceId]/tests/loading.tsx": { + "modules": 15, + "gateways": {} + }, + "app/workspace/[workspaceId]/tests/page.tsx": { + "modules": 1247, + "gateways": { + "apps/sim/app/workspace/[workspaceId]/tests/tests.tsx": 1231, + "apps/sim/app/workspace/[workspaceId]/providers/workspace-permissions-provider.tsx": 1090, + "apps/sim/triggers/registry.ts": 574, + "apps/sim/blocks/registry.ts": 383, + "apps/sim/blocks/registry-maps.ts": 380, + "apps/sim/lib/api/contracts/index.ts": 60, + "apps/sim/triggers/plane/index.ts": 46, + "apps/sim/stores/workflows/registry/store.ts": 39 } }, "app/workspace/[workspaceId]/upgrade/page.tsx": { @@ -544,11 +591,11 @@ "gateways": {} }, "app/workspace/[workspaceId]/w/[workflowId]/page.tsx": { - "modules": 2402, + "modules": 2406, "gateways": { - "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx": 2401, - "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/index.ts": 665, - "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/index.ts": 617, + "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx": 2405, + "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/index.ts": 668, + "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/index.ts": 620, "apps/sim/triggers/registry.ts": 574, "apps/sim/blocks/registry.ts": 357, "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/index.ts": 290, @@ -557,10 +604,10 @@ } }, "app/workspace/[workspaceId]/w/page.tsx": { - "modules": 2383, + "modules": 2387, "gateways": { - "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/index.ts": 1111, - "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/index.ts": 833, + "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/index.ts": 1115, + "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/index.ts": 836, "apps/sim/triggers/registry.ts": 574, "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/index.ts": 436, "apps/sim/blocks/registry.ts": 357, @@ -583,16 +630,16 @@ } }, "app/workspace/page.tsx": { - "modules": 1199, + "modules": 1200, "gateways": { - "apps/sim/lib/auth/stale-session-recovery.ts": 1079, + "apps/sim/lib/auth/stale-session-recovery.ts": 1080, + "apps/sim/stores/reset-all-stores.ts": 1077, "apps/sim/triggers/index.ts": 576, "apps/sim/triggers/registry.ts": 574, "apps/sim/blocks/registry.ts": 384, "apps/sim/blocks/registry-maps.ts": 381, "apps/sim/lib/api/contracts/index.ts": 55, - "apps/sim/triggers/plane/index.ts": 46, - "apps/sim/stores/workflows/registry/store.ts": 45 + "apps/sim/triggers/plane/index.ts": 46 } }, "lib/catalog/projection/block-detail.ts": { diff --git a/scripts/check-unused-exports.baseline.json b/scripts/check-unused-exports.baseline.json index cf291b4465e..32f5d1813c4 100644 --- a/scripts/check-unused-exports.baseline.json +++ b/scripts/check-unused-exports.baseline.json @@ -4545,7 +4545,6 @@ "apps/sim/lib/execution/remote-sandbox/workspace-sandboxes.ts#isSandboxNameTaken", "apps/sim/lib/execution/remote-sandbox/workspace-sandboxes.ts#isWorkspaceSandboxNameConflictError", "apps/sim/lib/execution/remote-sandbox/workspace-sandboxes.ts#scheduleSandboxBuild", - "apps/sim/lib/execution/sandbox/run-task.ts#SandboxUserCodeError", "apps/sim/lib/file-parsers/errors.ts#FILE_PARSER_ERROR_CODES", "apps/sim/lib/file-parsers/index.ts#parseFile", "apps/sim/lib/file-parsers/json-parser.ts#stripJsonComments", From 780331ebb1c386c0e8f9fdb9c89d34692d2b0184 Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Wed, 7 Oct 2026 18:20:28 -0700 Subject: [PATCH 03/14] feat(tests): pick a run from a dropdown and open what each run ran against The test page shows one run at a time, chosen from a run picker with status dots and Draft / Out of date chips. Case statuses use the Badge status chip. Each ran-against entry records one execution, so a draft row opens the workflow snapshot from that run. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EHGBgHrtePpi7KMrEfy41R --- .../tests/components/test-dashboard.tsx | 325 +++++++----------- .../tests/components/test-run-picker.tsx | 103 ++++++ apps/sim/lib/api/contracts/workflow-tests.ts | 1 + .../lib/workflow-tests/application/tests.ts | 3 + apps/sim/lib/workflow-tests/repository.ts | 24 +- apps/sim/stores/workflow-tests/store.ts | 30 ++ 6 files changed, 273 insertions(+), 213 deletions(-) create mode 100644 apps/sim/app/workspace/[workspaceId]/tests/components/test-run-picker.tsx create mode 100644 apps/sim/stores/workflow-tests/store.ts diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx b/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx index 08379776865..76af0fc29f3 100644 --- a/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx @@ -3,7 +3,6 @@ import { lazy, type ReactNode, Suspense, useEffect, useState } from 'react' import { Badge, - Chip, cn, DashboardMetric, Table, @@ -13,11 +12,13 @@ import { TableHeader, TableRow, } from '@sim/emcn' -import { CircleCheck, CircleX, Loader, Minus } from '@sim/emcn/icons' +import { Loader } from '@sim/emcn/icons' import { formatDuration, formatRelativeTime } from '@sim/utils/formatting' import type { WorkflowTestDetail } from '@/lib/api/contracts/workflow-tests' import { SettingsSection } from '@/app/workspace/[workspaceId]/settings/components/settings-section/settings-section' +import { TestRunPicker } from '@/app/workspace/[workspaceId]/tests/components/test-run-picker' import { useWorkflowTestRun } from '@/hooks/queries/workflow-tests' +import { testRunSelectionKey, useTestRunSelectionStore } from '@/stores/workflow-tests/store' const WorkflowVersionPreview = lazy(() => import('@/app/workspace/[workspaceId]/tests/components/workflow-version-preview').then( @@ -25,45 +26,26 @@ const WorkflowVersionPreview = lazy(() => ) ) +const ExecutionSnapshot = lazy(() => + import( + '@/app/workspace/[workspaceId]/logs/components/log-details/components/execution-snapshot/execution-snapshot' + ).then((module) => ({ default: module.ExecutionSnapshot })) +) + type RunDetail = NonNullable -type Run = WorkflowTestDetail['history'][number] type CaseResult = NonNullable['tests'][number] type CaseState = 'queued' | 'running' | 'pass' | 'fail' | 'skip' | 'none' -const RUNS_SHOWN = 5 - -const RUN_BADGE: Record = { - passed: 'green', - failed: 'red', - error: 'red', - running: 'amber', -} - -const CASE_STATE: Record = { - queued: { - label: 'Queued', - icon: , - }, - running: { - label: 'Running', - icon: , - }, - pass: { - label: 'Passed', - icon: , - }, - fail: { - label: 'Failed', - icon: , - }, - skip: { - label: 'Skipped', - icon: , - }, - none: { - label: 'Not run', - icon: , - }, +const CASE_STATE: Record< + CaseState, + { label: string; variant: 'green' | 'red' | 'amber' | 'gray' } +> = { + queued: { label: 'Queued', variant: 'gray' }, + running: { label: 'Running', variant: 'amber' }, + pass: { label: 'Passed', variant: 'green' }, + fail: { label: 'Failed', variant: 'red' }, + skip: { label: 'Skipped', variant: 'gray' }, + none: { label: 'Not run', variant: 'gray' }, } interface TestDashboardProps { @@ -73,11 +55,14 @@ interface TestDashboardProps { } /** - * How the test is doing: totals and freshness for one run (the latest unless a row in Runs is - * chosen), what failed in it, and every recent run. A latest run still going shows live. + * How the test is doing in one run (the latest unless the run picker chose another): totals, + * freshness, every case, and what it ran against. A latest run still going shows live. */ export function TestDashboard({ workspaceId, name, detail }: TestDashboardProps) { - const [selectedRunId, setSelectedRunId] = useState(null) + const selectedRunId = + useTestRunSelectionStore( + (state) => state.selectedRunIds[testRunSelectionKey(workspaceId, name)] + ) ?? null const latestRunning = detail.latestRun?.status === 'running' const viewedRunId = latestRunning ? null : selectedRunId const selected = useWorkflowTestRun(workspaceId, name, viewedRunId) @@ -106,6 +91,9 @@ export function TestDashboard({ workspaceId, name, detail }: TestDashboardProps)
+
+ +
@@ -135,54 +123,40 @@ export function TestDashboard({ workspaceId, name, detail }: TestDashboardProps)
- - {cases.length === 0 ? ( -

No tests yet

- ) : ( - - - - Test - Status - Duration + {cases.length === 0 ? ( +

No tests yet

+ ) : ( +
+ + + Test + Status + Duration + + + + {cases.map((testCase) => ( + + + {testCase.name} + {testCase.state === 'fail' && testCase.result && ( +

+ {failureReason(testCase.result)} +

+ )} +
+ + + {CASE_STATE[testCase.state].label} + + + + {testCase.result ? formatDuration(testCase.result.durationMs) : '—'} +
- - - {cases.map((testCase) => ( - - - {testCase.name} - {testCase.state === 'fail' && testCase.result && ( -

- {failureReason(testCase.result)} -

- )} -
- - - {CASE_STATE[testCase.state].icon} - {CASE_STATE[testCase.state].label} - - - - {testCase.result ? formatDuration(testCase.result.durationMs) : '—'} - -
- ))} -
-
- )} -
- - {detail.history.length > 0 && ( - setSelectedRunId(runId === detail.latestRun?.id ? null : runId)} - /> + ))} + + )} {!running && run && run.ranAgainst.length > 0 && ( @@ -219,8 +193,12 @@ function freshnessLine(run: RunDetail | null): { lead: ReactNode; text: string } if (!run) return { lead: null, text: 'Not run yet' } if (run.status === 'running') { return { - lead: , - text: 'Running against the latest workflows…', + lead: ( + + Running + + ), + text: 'Against the latest workflows', } } const when = formatRelativeTime(run.completedAt ?? run.startedAt) @@ -257,32 +235,40 @@ interface WorkflowsSectionProps { workflows: RunDetail['ranAgainst'] } +type Previewing = + | { kind: 'version'; workflowId: string; version: number; title: string } + | { kind: 'snapshot'; executionId: string } + /** * Each workflow the run executed and the version it ran, marked when that version is no longer - * live. A deployed version opens on a read-only canvas. + * live. A deployed version opens on a read-only canvas; a draft opens as it was when the run used it. */ function WorkflowsSection({ workflows }: WorkflowsSectionProps) { - const [previewing, setPreviewing] = useState<{ - workflowId: string - version: number - title: string - } | null>(null) + const [previewing, setPreviewing] = useState(null) return ( {workflows.map((workflow) => { - const label = `${workflow.name ?? 'Deleted workflow'}${ - workflow.draft ? ' · Draft' : workflow.version ? ` · v${workflow.version}` : '' - }` + const name = workflow.name ?? 'Deleted workflow' const version = workflow.version const preview = - workflow.name !== null && !workflow.draft && version !== null - ? () => setPreviewing({ workflowId: workflow.workflowId, version, title: label }) - : undefined + workflow.name === null + ? undefined + : workflow.draft + ? () => setPreviewing({ kind: 'snapshot', executionId: workflow.executionId }) + : version !== null + ? () => + setPreviewing({ + kind: 'version', + workflowId: workflow.workflowId, + version, + title: `${name} · v${version}`, + }) + : undefined return ( { @@ -295,7 +281,20 @@ function WorkflowsSection({ workflows }: WorkflowsSectionProps) { preview && 'cursor-pointer transition-colors hover:bg-[var(--surface-active)]' )} > - {label} + + + {name} + {workflow.draft ? ( + + Draft + + ) : ( + version !== null && ( + v{version} + ) + )} + + {workflow.stale && ( @@ -308,118 +307,30 @@ function WorkflowsSection({ workflows }: WorkflowsSectionProps) { })}
- {previewing && ( + {previewing?.kind === 'version' && ( - setPreviewing(null)} /> + setPreviewing(null)} + /> )} -
- ) -} - -interface RunsSectionProps { - runs: Run[] - latestRunId: string | null - selectedRunId: string | null - onSelect: (runId: string) => void -} - -function RunsSection({ runs, latestRunId, selectedRunId, onSelect }: RunsSectionProps) { - const [showAll, setShowAll] = useState(false) - const shown = showAll ? runs : runs.slice(0, RUNS_SHOWN) - return ( - onSelect(latestRunId)}>View latest - ) : null - } - > - - - {shown.map((run) => ( - onSelect(run.id)} - /> - ))} - -
- {runs.length > RUNS_SHOWN && ( - setShowAll((value) => !value)}> - {showAll ? 'Show fewer' : `Show all ${runs.length}`} - + {previewing?.kind === 'snapshot' && ( + + setPreviewing(null)} + /> + )}
) } -interface RunRowProps { - run: Run - latest: boolean - selected: boolean - onSelect: () => void -} - -/** One run: how it went, how long it took, and when. Opens its results on the page. */ -function RunRow({ run, latest, selected, onSelect }: RunRowProps) { - const result = - run.status === 'passed' || run.status === 'failed' - ? `${run.passed} of ${run.passed + run.failed} passed` - : run.status === 'error' - ? 'Didn’t finish' - : 'Running' - const durationMs = run.completedAt - ? Date.parse(run.completedAt) - Date.parse(run.startedAt) - : null - return ( - { - if (event.key === 'Enter' || event.key === ' ') { - event.preventDefault() - onSelect() - } - }} - className={cn( - 'cursor-pointer transition-colors hover:bg-[var(--surface-active)]', - selected && 'bg-[var(--surface-active)]', - !run.current && run.status !== 'running' && 'text-[var(--text-muted)]' - )} - > - - - - {result} - - {latest && ( - - Latest - - )} - {run.version === 'draft' && ( - - Draft - - )} - - - - {durationMs === null ? '—' : formatDuration(durationMs)} - - - {formatRelativeTime(run.completedAt ?? run.startedAt)} - - - ) -} - /** A case's name under its file's one top-level describe. */ function caseName(path: string[]): string { return path.slice(1).join(' › ') diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/test-run-picker.tsx b/apps/sim/app/workspace/[workspaceId]/tests/components/test-run-picker.tsx new file mode 100644 index 00000000000..e6829344275 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/test-run-picker.tsx @@ -0,0 +1,103 @@ +'use client' + +import { + Badge, + Chip, + ChipChevronDown, + cn, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuItemLabel, + DropdownMenuTrigger, +} from '@sim/emcn' +import { Check } from '@sim/emcn/icons' +import { formatRelativeTime } from '@sim/utils/formatting' +import type { WorkflowTestDetail } from '@/lib/api/contracts/workflow-tests' +import { testRunSelectionKey, useTestRunSelectionStore } from '@/stores/workflow-tests/store' + +type Run = WorkflowTestDetail['history'][number] + +interface TestRunPickerProps { + workspaceId: string + name: string + detail: WorkflowTestDetail +} + +/** Chooses which run the test's results show; the latest unless another is picked. */ +export function TestRunPicker({ workspaceId, name, detail }: TestRunPickerProps) { + const key = testRunSelectionKey(workspaceId, name) + const selectedRunId = useTestRunSelectionStore((state) => state.selectedRunIds[key]) + const selectRun = useTestRunSelectionStore((state) => state.selectRun) + const runs = detail.history + const latestId = detail.latestRun?.id + const shownId = detail.latestRun?.status === 'running' ? latestId : (selectedRunId ?? latestId) + const shown = runs.find((run) => run.id === shownId) + if (!shown) return null + + return ( + + + } rightAdornment={}> + {runLabel(shown, shown.id === latestId)} + + + + {runs.map((run) => ( + selectRun(key, run.id === latestId ? null : run.id)} + > + + + + {runResult(run)} + {run.version === 'draft' && ( + + Draft + + )} + {!run.current && ( + + Out of date + + )} + + {run.id === shownId && ( + + )} + + ))} + + + ) +} + +function runLabel(run: Run, latest: boolean): string { + const when = formatRelativeTime(run.completedAt ?? run.startedAt) + return latest ? `Latest · ${when}` : when +} + +function runResult(run: Run): string { + if (run.status === 'passed' || run.status === 'failed') { + return `${run.passed}/${run.passed + run.failed}` + } + return run.status === 'error' ? 'Didn’t finish' : 'Running' +} + +const RUN_DOT: Record = { + passed: 'bg-[var(--badge-success-text)]', + running: 'bg-[var(--badge-amber-text)]', + failed: 'bg-[var(--badge-error-text)]', + error: 'bg-[var(--badge-error-text)]', +} + +interface RunDotProps { + run: Run +} + +function RunDot({ run }: RunDotProps) { + return ( + + ) +} diff --git a/apps/sim/lib/api/contracts/workflow-tests.ts b/apps/sim/lib/api/contracts/workflow-tests.ts index 395d7223c42..03429383454 100644 --- a/apps/sim/lib/api/contracts/workflow-tests.ts +++ b/apps/sim/lib/api/contracts/workflow-tests.ts @@ -32,6 +32,7 @@ const runSummarySchema = z.object({ draft: z.boolean(), liveVersion: z.number().int().nullable(), stale: z.boolean(), + executionId: z.string(), }) ), current: z.boolean(), diff --git a/apps/sim/lib/workflow-tests/application/tests.ts b/apps/sim/lib/workflow-tests/application/tests.ts index 9e514d798b0..ce89920e767 100644 --- a/apps/sim/lib/workflow-tests/application/tests.ts +++ b/apps/sim/lib/workflow-tests/application/tests.ts @@ -67,6 +67,8 @@ interface WorkflowTestRanAgainst { liveVersion: number | null /** The workflow was redeployed or deleted after this run. */ stale: boolean + /** One execution of it in this run, whose snapshot is the workflow as it ran. */ + executionId: string } type RunRow = Omit @@ -96,6 +98,7 @@ function presentRanAgainst(row: RunRow, facts: DeploymentFacts): WorkflowTestRan draft: deploymentId === null, liveVersion: live?.version ?? null, stale: name === null || (deploymentId !== null && live?.id !== deploymentId), + executionId: entry.executionId, } }) } diff --git a/apps/sim/lib/workflow-tests/repository.ts b/apps/sim/lib/workflow-tests/repository.ts index d95fe948719..3284174ac4b 100644 --- a/apps/sim/lib/workflow-tests/repository.ts +++ b/apps/sim/lib/workflow-tests/repository.ts @@ -107,10 +107,11 @@ export async function insertWorkflowTestRun(values: { await db.insert(workflowTestRun).values({ ...values, status: 'running' }) } -/** A workflow a run executed and the deployment it ran; `null` is the draft. */ +/** A workflow a run executed, the deployment it ran (`null` is the draft), and one execution of it. */ export interface RanAgainstEntry { workflowId: string deploymentVersionId: string | null + executionId: string } /** The workflows and deployments these executions ran, from their execution logs. */ @@ -120,10 +121,14 @@ export async function readExecutedDeployments( ): Promise { if (executionIds.length === 0) return [] const rows = await db - .selectDistinct({ - workflowId: workflowExecutionLogs.workflowId, - deploymentVersionId: workflowExecutionLogs.deploymentVersionId, - }) + .selectDistinctOn( + [workflowExecutionLogs.workflowId, workflowExecutionLogs.deploymentVersionId], + { + workflowId: workflowExecutionLogs.workflowId, + deploymentVersionId: workflowExecutionLogs.deploymentVersionId, + executionId: workflowExecutionLogs.executionId, + } + ) .from(workflowExecutionLogs) .where( and( @@ -131,9 +136,16 @@ export async function readExecutedDeployments( eq(workflowExecutionLogs.workspaceId, workspaceId) ) ) + .orderBy(workflowExecutionLogs.workflowId, workflowExecutionLogs.deploymentVersionId) return rows.flatMap((row) => row.workflowId - ? [{ workflowId: row.workflowId, deploymentVersionId: row.deploymentVersionId }] + ? [ + { + workflowId: row.workflowId, + deploymentVersionId: row.deploymentVersionId, + executionId: row.executionId, + }, + ] : [] ) } diff --git a/apps/sim/stores/workflow-tests/store.ts b/apps/sim/stores/workflow-tests/store.ts new file mode 100644 index 00000000000..164461fc2b9 --- /dev/null +++ b/apps/sim/stores/workflow-tests/store.ts @@ -0,0 +1,30 @@ +import { create } from 'zustand' +import { devtools } from 'zustand/middleware' + +/** + * Which run each open test shows. The run picker and the results it controls render in + * different places (the page header and its preview, or a chat tab's actions and its + * content), so the choice lives here. A missing entry means the latest run. + */ +interface TestRunSelectionState { + selectedRunIds: Record + selectRun: (testKey: string, runId: string | null) => void +} + +export const useTestRunSelectionStore = create()( + devtools( + (set) => ({ + selectedRunIds: {}, + selectRun: (testKey, runId) => + set((state) => { + const { [testKey]: _previous, ...rest } = state.selectedRunIds + return { selectedRunIds: runId === null ? rest : { ...rest, [testKey]: runId } } + }), + }), + { name: 'test-run-selection' } + ) +) + +export function testRunSelectionKey(workspaceId: string, name: string): string { + return `${workspaceId}:${name}` +} From 2519405b936b8af43cb58a2b50504f85d02f19a9 Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Wed, 7 Oct 2026 18:44:52 -0700 Subject: [PATCH 04/14] fix(tests): type errors and tests broken by workflow tests - Narrow the test principal to the kinds workflow_tests.run admits before handing it to executeWorkflow. - Select progress with the latest-run rows, guard file upsert ids in the tab filter, and set the sandbox Event polyfills through Reflect. - Cover the tests tool in the management tool contract, expect content writes to reach test files, and stub test availability in the payload test. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EHGBgHrtePpi7KMrEfy41R --- .../tests/components/test-dashboard.tsx | 7 ++- .../mothership-management-tools.test.ts | 9 ++++ .../sandbox/bundles/event-polyfills.ts | 52 +++++++++++-------- apps/sim/lib/internal/file/operations.test.ts | 18 +++---- .../mothership/agent-cli/resource-effects.ts | 13 ++--- apps/sim/lib/mothership/chat/payload.test.ts | 3 ++ apps/sim/lib/workflow-tests/repository.ts | 1 + .../application/run-workflow-for-test.ts | 21 +++++++- 8 files changed, 81 insertions(+), 43 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx b/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx index 76af0fc29f3..40c371e5950 100644 --- a/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx @@ -79,7 +79,7 @@ export function TestDashboard({ workspaceId, name, detail }: TestDashboardProps) const cases = casePaths.map((path) => { const key = path.join(' > ') const result = results.get(key) - const state: CaseState = running ? (progress[key] ?? 'queued') : (result?.status ?? 'none') + const state = caseState(running ? progress[key] : result?.status, running) return { key, name: caseName(path), state, result } }) const finished = cases.filter((c) => c.state !== 'queued' && c.state !== 'running').length @@ -331,6 +331,11 @@ function WorkflowsSection({ workflows }: WorkflowsSectionProps) { ) } +/** A case with no status yet is queued while the run goes, and not run once it ended. */ +function caseState(status: CaseState | undefined, running: boolean): CaseState { + return status ?? (running ? 'queued' : 'none') +} + /** A case's name under its file's one top-level describe. */ function caseName(path: string[]): string { return path.slice(1).join(' › ') diff --git a/apps/sim/lib/api/contracts/mothership-management-tools.test.ts b/apps/sim/lib/api/contracts/mothership-management-tools.test.ts index e64a8040214..acfa532ffca 100644 --- a/apps/sim/lib/api/contracts/mothership-management-tools.test.ts +++ b/apps/sim/lib/api/contracts/mothership-management-tools.test.ts @@ -37,6 +37,15 @@ const examples = { { action: 'set', content: 'title: Support\nblocks: []' }, { action: 'set', content: 'title: Support', expectedRevision: 'r1' }, ], + tests: [ + { action: 'create', name: 'triage', title: 'Triage posts the firing alarm' }, + { action: 'update', name: 'triage', description: null }, + { action: 'list', version: 'draft' }, + { action: 'get', name: 'triage', version: 'deployed' }, + { action: 'run', version: 'draft' }, + { action: 'run', version: 'draft', names: ['triage'], only: ['Triage > posts a summary'] }, + { action: 'delete', name: 'triage' }, + ], } describe('management tool provider contract', () => { diff --git a/apps/sim/lib/execution/sandbox/bundles/event-polyfills.ts b/apps/sim/lib/execution/sandbox/bundles/event-polyfills.ts index a7a092df241..1eb21f4dbfc 100644 --- a/apps/sim/lib/execution/sandbox/bundles/event-polyfills.ts +++ b/apps/sim/lib/execution/sandbox/bundles/event-polyfills.ts @@ -5,34 +5,40 @@ type Listener = (event: { type: string }) => void -const g: typeof globalThis & { Event?: unknown; EventTarget?: unknown } = globalThis - -if (typeof g.Event === 'undefined') { - g.Event = class Event { - readonly type: string - constructor(type: string) { - this.type = type +if (!('Event' in globalThis)) { + Reflect.set( + globalThis, + 'Event', + class Event { + readonly type: string + constructor(type: string) { + this.type = type + } } - } + ) } -if (typeof g.EventTarget === 'undefined') { - g.EventTarget = class EventTarget { - readonly #listeners = new Map>() +if (!('EventTarget' in globalThis)) { + Reflect.set( + globalThis, + 'EventTarget', + class EventTarget { + readonly #listeners = new Map>() - addEventListener(type: string, listener: Listener): void { - const listeners = this.#listeners.get(type) ?? new Set() - listeners.add(listener) - this.#listeners.set(type, listeners) - } + addEventListener(type: string, listener: Listener): void { + const listeners = this.#listeners.get(type) ?? new Set() + listeners.add(listener) + this.#listeners.set(type, listeners) + } - removeEventListener(type: string, listener: Listener): void { - this.#listeners.get(type)?.delete(listener) - } + removeEventListener(type: string, listener: Listener): void { + this.#listeners.get(type)?.delete(listener) + } - dispatchEvent(event: { type: string }): boolean { - for (const listener of this.#listeners.get(event.type) ?? []) listener(event) - return true + dispatchEvent(event: { type: string }): boolean { + for (const listener of this.#listeners.get(event.type) ?? []) listener(event) + return true + } } - } + ) } diff --git a/apps/sim/lib/internal/file/operations.test.ts b/apps/sim/lib/internal/file/operations.test.ts index 8488db23333..db2d5519787 100644 --- a/apps/sim/lib/internal/file/operations.test.ts +++ b/apps/sim/lib/internal/file/operations.test.ts @@ -443,11 +443,9 @@ describe('file manage folder wiring', () => { ) expect(response.status).toBe(200) - expect(mockResolveWorkspaceFileReference).toHaveBeenCalledWith( - 'workspace-1', - 'a-self', - undefined - ) + expect(mockResolveWorkspaceFileReference).toHaveBeenCalledWith('workspace-1', 'a-self', { + includeTestFiles: true, + }) }) it('refuses an ambiguous name instead of editing an arbitrary file', async () => { @@ -484,11 +482,9 @@ describe('file manage folder wiring', () => { }) ) - expect(mockResolveWorkspaceFileReference).toHaveBeenCalledWith( - 'workspace-1', - 'a-self', - undefined - ) + expect(mockResolveWorkspaceFileReference).toHaveBeenCalledWith('workspace-1', 'a-self', { + includeTestFiles: true, + }) }) }) @@ -582,7 +578,7 @@ describe('file manage folder wiring', () => { expect(mockResolveWorkspaceFileReference).toHaveBeenCalledWith( 'workspace-1', 'notes-in-reports', - undefined + { includeTestFiles: true } ) }) diff --git a/apps/sim/lib/mothership/agent-cli/resource-effects.ts b/apps/sim/lib/mothership/agent-cli/resource-effects.ts index f38fa1d3603..fc914682308 100644 --- a/apps/sim/lib/mothership/agent-cli/resource-effects.ts +++ b/apps/sim/lib/mothership/agent-cli/resource-effects.ts @@ -342,12 +342,13 @@ function matchParams(pattern: string, pathname: string): Record /** Only workspace files and chat uploads open as file tabs; other rows open as their owner. */ async function onlyFileTabs(changes: ResourceChange[]): Promise { - const isFileUpsert = (change: ResourceChange) => - change.op === 'upsert' && change.resource.type === 'file' - const nonTabIds = await findNonTabFileIds( - changes.flatMap((change) => (isFileUpsert(change) ? [change.resource.id] : [])) - ) - return changes.filter((change) => !isFileUpsert(change) || !nonTabIds.has(change.resource.id)) + const fileUpsertId = (change: ResourceChange) => + change.op === 'upsert' && change.resource.type === 'file' ? change.resource.id : undefined + const nonTabIds = await findNonTabFileIds(changes.flatMap((change) => fileUpsertId(change) ?? [])) + return changes.filter((change) => { + const id = fileUpsertId(change) + return id === undefined || !nonTabIds.has(id) + }) } /** The observer adds no request and never reads provider responses or download bodies. */ diff --git a/apps/sim/lib/mothership/chat/payload.test.ts b/apps/sim/lib/mothership/chat/payload.test.ts index 509f5cde55e..905cf076884 100644 --- a/apps/sim/lib/mothership/chat/payload.test.ts +++ b/apps/sim/lib/mothership/chat/payload.test.ts @@ -54,6 +54,9 @@ vi.mock('@/lib/mothership/chat/workspace-inventory', () => ({ vi.mock('@/lib/dashboards/application/availability', () => ({ readDashboardAvailability: { execute: mockDashboardAvailability }, })) +vi.mock('@/lib/workflow-tests/application/availability', () => ({ + readWorkflowTestAvailability: { execute: vi.fn(async () => false) }, +})) vi.mock('@/lib/billing/core/subscription', () => billingSubscriptionMock) const mockGetHighestPrioritySubscription = billingSubscriptionMockFns.mockGetHighestPrioritySubscription diff --git a/apps/sim/lib/workflow-tests/repository.ts b/apps/sim/lib/workflow-tests/repository.ts index 3284174ac4b..eec2fc9bdff 100644 --- a/apps/sim/lib/workflow-tests/repository.ts +++ b/apps/sim/lib/workflow-tests/repository.ts @@ -282,6 +282,7 @@ export async function listRecentWorkflowTestRuns( triggeredByUserId: workflowTestRun.triggeredByUserId, sourceHash: workflowTestRun.sourceHash, ranAgainst: workflowTestRun.ranAgainst, + progress: workflowTestRun.progress, startedAt: workflowTestRun.startedAt, completedAt: workflowTestRun.completedAt, }) diff --git a/apps/sim/lib/workflows/application/run-workflow-for-test.ts b/apps/sim/lib/workflows/application/run-workflow-for-test.ts index d766f4d34bc..b4b9a45f501 100644 --- a/apps/sim/lib/workflows/application/run-workflow-for-test.ts +++ b/apps/sim/lib/workflows/application/run-workflow-for-test.ts @@ -1,4 +1,8 @@ -import { type Principal, requirePrincipalSubjectUserId } from '@sim/auth/principal' +import { + type Principal, + requirePrincipalSubjectUserId, + type WorkflowExecutionPrincipal, +} from '@sim/auth/principal' import { generateId } from '@sim/utils/id' import { resolveBillingAttribution } from '@/lib/billing/core/billing-attribution' import { checkExecutionUsageLimits } from '@/lib/billing/core/usage-gate-cache' @@ -47,7 +51,7 @@ export async function runWorkflowForTest( workflowId: input.workflowId, assertedWorkspaceId: input.workspaceId, }) - const principal = input.principal + const principal = requireTestPrincipal(input.principal) const useDraftState = input.version === 'draft' const prepared = await resolveTriggerExecution({ input: { @@ -101,3 +105,16 @@ export async function runWorkflowForTest( throw error } } + +/** The principal kinds `workflow_tests.run` admits; anything else reaching here is a wiring bug. */ +function requireTestPrincipal(principal: Principal): WorkflowExecutionPrincipal { + if ( + principal.kind === 'session' || + principal.kind === 'personal_api_key' || + principal.kind === 'oauth_access_token' + ) { + return principal + } + if (principal.kind === 'delegated' && principal.serviceId === 'copilot') return principal + throw new Error(`A ${principal.kind} principal cannot run workflow tests`) +} From cd14f90fcf4890e3ff6d8ebbb0194b38b1cbecce Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Wed, 7 Oct 2026 19:50:29 -0700 Subject: [PATCH 05/14] fix(tests): address review on redaction, staleness, and the harness - Redact each run's resolved secrets from what returns to the sandbox (output, errors, mocked tool inputs); mocked tools get only declared params. - Custom blocks no longer receive the consumer's test hooks. - Draft runs go stale when the draft changes; children a run calls are recorded in ran-against. - Test cases commit in the same transaction as the source file write. - Harness: runWorkflow is rejected in suite hooks, a timed-out case stops the file, and expect.assertions/hasAssertions are supported. - Insert run rows in one statement and start each run's clock with its file; check bans before each workflow run; restrict owned-file access to Copilot delegation; validate names in the tool contract. - Delete soft-deletes the test file and removes the chat tab; a finished run shows its own cases; polling at 3s on a separate read bucket; list error state; store reset; tests stay in the org Add Resource picker. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EHGBgHrtePpi7KMrEfy41R --- .../api/workspaces/[id]/tests/[name]/route.ts | 6 +- .../[id]/tests/[name]/runs/[runId]/route.ts | 6 +- .../app/api/workspaces/[id]/tests/route.ts | 6 +- .../organization-resource-inventory.tsx | 6 +- .../mothership-view/mothership-view.tsx | 4 +- .../plus-menu-dropdown/plus-menu-dropdown.tsx | 1 + .../tests/components/test-dashboard.tsx | 17 +- .../workspace/[workspaceId]/tests/tests.tsx | 9 +- apps/sim/executor/execution/executor.ts | 1 + apps/sim/executor/execution/types.ts | 7 +- .../handlers/workflow/workflow-handler.ts | 5 +- apps/sim/hooks/queries/workflow-tests.ts | 4 +- .../sim/lib/api/contracts/mothership-tests.ts | 9 +- apps/sim/lib/api/contracts/workflow-tests.ts | 2 +- .../mothership/agent-cli/resource-effects.ts | 17 +- apps/sim/lib/mothership/tools/server/tests.ts | 5 +- .../workspace/workspace-file-manager.ts | 3 + .../workflow-tests/application/run-tests.ts | 23 ++- .../lib/workflow-tests/application/tests.ts | 21 ++- apps/sim/lib/workflow-tests/paths.ts | 3 +- apps/sim/lib/workflow-tests/repository.ts | 153 +++++++++++++++--- apps/sim/lib/workflow-tests/run-test-file.ts | 27 ++-- apps/sim/lib/workflow-tests/session.ts | 62 +++++-- apps/sim/lib/workflow-tests/source-write.ts | 5 +- .../application/run-workflow-for-test.ts | 5 + .../edit-workspace-file-content.ts | 2 +- .../application/file-versions.ts | 2 +- .../application/owned-file-content.ts | 10 +- .../update-workspace-file-content.ts | 2 +- .../application/workspace-file-context.ts | 21 ++- apps/sim/providers/runtime-context.ts | 32 ++-- apps/sim/sandbox-tasks/workflow-test.ts | 39 ++++- apps/sim/stores/workflow-tests/store.ts | 2 + 33 files changed, 399 insertions(+), 118 deletions(-) diff --git a/apps/sim/app/api/workspaces/[id]/tests/[name]/route.ts b/apps/sim/app/api/workspaces/[id]/tests/[name]/route.ts index 75e5217aa41..cb48afe6924 100644 --- a/apps/sim/app/api/workspaces/[id]/tests/[name]/route.ts +++ b/apps/sim/app/api/workspaces/[id]/tests/[name]/route.ts @@ -12,7 +12,11 @@ export const GET = defineInternalJsonRoute({ contract: getWorkflowTestContract, auth: internalSessionAuth, operation: workflowTestOperations.read, - rateLimit: internalRateLimits.user({ bucketName: 'workflow-tests' }), + rateLimit: internalRateLimits.user({ + bucketName: 'workflow-tests-read', + // A running test polls these every 3s from each open page and chat tab. + config: { maxTokens: 240, refillRate: 120, refillIntervalMs: 60_000 }, + }), errorPolicy: internalOrchestrationErrorPolicy, mapInput: ({ params, query }) => ({ workspaceId: params.id, diff --git a/apps/sim/app/api/workspaces/[id]/tests/[name]/runs/[runId]/route.ts b/apps/sim/app/api/workspaces/[id]/tests/[name]/runs/[runId]/route.ts index bf72a3df245..b9c82caedbb 100644 --- a/apps/sim/app/api/workspaces/[id]/tests/[name]/runs/[runId]/route.ts +++ b/apps/sim/app/api/workspaces/[id]/tests/[name]/runs/[runId]/route.ts @@ -12,7 +12,11 @@ export const GET = defineInternalJsonRoute({ contract: getWorkflowTestRunContract, auth: internalSessionAuth, operation: workflowTestOperations.read, - rateLimit: internalRateLimits.user({ bucketName: 'workflow-tests' }), + rateLimit: internalRateLimits.user({ + bucketName: 'workflow-tests-read', + // A running test polls these every 3s from each open page and chat tab. + config: { maxTokens: 240, refillRate: 120, refillIntervalMs: 60_000 }, + }), errorPolicy: internalOrchestrationErrorPolicy, mapInput: ({ params }) => ({ workspaceId: params.id, diff --git a/apps/sim/app/api/workspaces/[id]/tests/route.ts b/apps/sim/app/api/workspaces/[id]/tests/route.ts index 0b4ff17fde7..598a6b4fc57 100644 --- a/apps/sim/app/api/workspaces/[id]/tests/route.ts +++ b/apps/sim/app/api/workspaces/[id]/tests/route.ts @@ -12,7 +12,11 @@ export const GET = defineInternalJsonRoute({ contract: listWorkflowTestsContract, auth: internalSessionAuth, operation: workflowTestOperations.read, - rateLimit: internalRateLimits.user({ bucketName: 'workflow-tests' }), + rateLimit: internalRateLimits.user({ + bucketName: 'workflow-tests-read', + // A running test polls these every 3s from each open page and chat tab. + config: { maxTokens: 240, refillRate: 120, refillIntervalMs: 60_000 }, + }), errorPolicy: internalOrchestrationErrorPolicy, mapInput: ({ params, query }) => ({ workspaceId: params.id, version: query.version }), useCase: listWorkflowTests, diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/organization-resource-inventory.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/organization-resource-inventory.tsx index 8973edf0fcf..9ed7bc98ba1 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/organization-resource-inventory.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/organization-resource-inventory.tsx @@ -1,21 +1,23 @@ import { useEffect } from 'react' import { type AvailableResources, - COMPOSER_EXCLUDED_TYPES, useAvailableResources, } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/available-resources' +import type { MothershipResourceType } from '@/app/workspace/[workspaceId]/home/types' /** Reuses each workspace's canonical inventory without introducing another picker. */ export function OrganizationResourceInventory({ workspaceId, + excludeTypes, onChange, }: { workspaceId: string + excludeTypes?: readonly MothershipResourceType[] onChange: (workspaceId: string, inventory: AvailableResources) => void }) { const inventory = useAvailableResources(workspaceId, { includeFolderMentions: true, - excludeTypes: COMPOSER_EXCLUDED_TYPES, + excludeTypes, }) useEffect(() => { onChange(workspaceId, inventory) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx index c5c61207c10..dbcc1f2f202 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx @@ -221,8 +221,8 @@ export const MothershipView = memo( // A Sim page is locked to its rendered view (the pdf model — the raw // source is not a mode this surface offers), so no toggle either. activeFile?.type !== SIM_PAGE_CONTENT_TYPE - // A test shows its source, its results, or both, like an HTML file. - const isActivePreviewable = isActiveFilePreviewable || (canEdit && active?.type === 'test') + // A test shows its source, its results, or both, like an HTML file; the viewer enforces edits. + const isActivePreviewable = isActiveFilePreviewable || active?.type === 'test' return ( ))} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx b/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx index 40c371e5950..43256c1cfe3 100644 --- a/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx @@ -72,10 +72,11 @@ export function TestDashboard({ workspaceId, name, detail }: TestDashboardProps) const progress = run?.progress ?? {} const results = new Map() for (const result of run?.report?.tests ?? []) results.set(result.path.join(' > '), result) - /** An earlier run lists the cases it ran; otherwise the file's current cases, as they stand. */ - const casePaths = viewedRunId - ? (run?.report?.tests ?? []).map((result) => result.path) - : detail.test.cases.map((testCase) => testCase.path) + /** A finished run lists the cases it ran; a running or missing one, the file's current cases. */ + const casePaths = + !running && run?.report + ? run.report.tests.map((result) => result.path) + : detail.test.cases.map((testCase) => testCase.path) const cases = casePaths.map((path) => { const key = path.join(' > ') const result = results.get(key) @@ -251,12 +252,14 @@ function WorkflowsSection({ workflows }: WorkflowsSectionProps) { {workflows.map((workflow) => { const name = workflow.name ?? 'Deleted workflow' - const version = workflow.version + const { executionId, version } = workflow const preview = workflow.name === null ? undefined : workflow.draft - ? () => setPreviewing({ kind: 'snapshot', executionId: workflow.executionId }) + ? executionId === null + ? undefined + : () => setPreviewing({ kind: 'snapshot', executionId }) : version !== null ? () => setPreviewing({ @@ -268,7 +271,7 @@ function WorkflowsSection({ workflows }: WorkflowsSectionProps) { : undefined return ( { diff --git a/apps/sim/app/workspace/[workspaceId]/tests/tests.tsx b/apps/sim/app/workspace/[workspaceId]/tests/tests.tsx index 8654b807822..4960ec0ba57 100644 --- a/apps/sim/app/workspace/[workspaceId]/tests/tests.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tests/tests.tsx @@ -1,7 +1,7 @@ 'use client' import { useMemo } from 'react' -import { toast } from '@sim/emcn' +import { Chip, toast } from '@sim/emcn' import { PlayOutline, ShieldCheck } from '@sim/emcn/icons' import { useRouter } from 'next/navigation' import { useQueryStates } from 'nuqs' @@ -149,7 +149,12 @@ export function Tests({ workspaceId }: TestsProps) { rows={rows} onRowClick={(name) => router.push(`/workspace/${workspaceId}/tests/${name}`)} emptyState={ - debouncedSearch.trim() && query.data?.length ? ( + query.error ? ( +
+

{query.error.message}

+ query.refetch()}>Retry +
+ ) : debouncedSearch.trim() && query.data?.length ? ( + /** `resolvedSecretTraceRegistry` redacts what this run sends back to the test. */ + enterWorkflow(workflow: { + workflowId: string + blocks: TestWorkflowBlock[] + resolvedSecretTraceRegistry: ResolvedSecretTraceRegistry | undefined + }): Promise /** Whether this block's handler is replaced; ids are workflow block ids, never clone ids. */ mocksBlock(blockId: string): boolean resolveMock(call: MockedBlockCall, abortSignal?: AbortSignal): Promise diff --git a/apps/sim/executor/handlers/workflow/workflow-handler.ts b/apps/sim/executor/handlers/workflow/workflow-handler.ts index 1e77461ab47..321ecab58b7 100644 --- a/apps/sim/executor/handlers/workflow/workflow-handler.ts +++ b/apps/sim/executor/handlers/workflow/workflow-handler.ts @@ -888,8 +888,9 @@ export class WorkflowBlockHandler implements BlockHandler { // Propagate in-flight block-output redaction into child workflows so // nested blocks mask outputs too (recurses: each child forwards it). piiBlockOutputRedaction: ctx.piiBlockOutputRedaction, - // A workflow test's mocks and spies reach blocks in every child it runs. - testHooks: ctx.testHooks, + // A workflow test's mocks and spies reach every child it runs, except a custom + // block's: that workflow is the publisher's, so the test mocks the block whole. + testHooks: isCustomBlock ? undefined : ctx.testHooks, callChain: childCallChain, // A custom block's own session markers and the parent's live stream, fanned // out together — see `childCallbacks` above for why this is not two spreads. diff --git a/apps/sim/hooks/queries/workflow-tests.ts b/apps/sim/hooks/queries/workflow-tests.ts index 97dab6c5ed3..cc0cd865d7a 100644 --- a/apps/sim/hooks/queries/workflow-tests.ts +++ b/apps/sim/hooks/queries/workflow-tests.ts @@ -10,8 +10,8 @@ import { export type WorkflowTestVersion = 'draft' | 'deployed' const WORKFLOW_TEST_STALE_TIME = 15_000 -/** While a run is in flight, so its result appears when it lands. */ -const WORKFLOW_TEST_RUNNING_REFETCH_INTERVAL = 1_000 +/** While a run is in flight; 20 reads a minute stays inside the routes' per-user limit of 30. */ +const WORKFLOW_TEST_RUNNING_REFETCH_INTERVAL = 3_000 export const workflowTestKeys = { all: ['workflow-tests'] as const, diff --git a/apps/sim/lib/api/contracts/mothership-tests.ts b/apps/sim/lib/api/contracts/mothership-tests.ts index d5526940523..06eff8f89f6 100644 --- a/apps/sim/lib/api/contracts/mothership-tests.ts +++ b/apps/sim/lib/api/contracts/mothership-tests.ts @@ -1,11 +1,16 @@ import { z } from 'zod' const scope = z.object({ workspaceId: z.string().min(1).max(100).optional() }) +/** A test file name: `tests/.test.js`. */ +export const TEST_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,79}$/ + const nameSchema = z .string() .trim() - .min(1) - .max(80) + .regex( + TEST_NAME_PATTERN, + 'Use a lowercase name of letters, numbers, and dashes, like billing-routing' + ) .describe('Test file name: tests/.test.js, lowercase letters, numbers, and dashes.') const versionSchema = z .enum(['draft', 'deployed']) diff --git a/apps/sim/lib/api/contracts/workflow-tests.ts b/apps/sim/lib/api/contracts/workflow-tests.ts index 03429383454..8863ff6823b 100644 --- a/apps/sim/lib/api/contracts/workflow-tests.ts +++ b/apps/sim/lib/api/contracts/workflow-tests.ts @@ -32,7 +32,7 @@ const runSummarySchema = z.object({ draft: z.boolean(), liveVersion: z.number().int().nullable(), stale: z.boolean(), - executionId: z.string(), + executionId: z.string().nullable(), }) ), current: z.boolean(), diff --git a/apps/sim/lib/mothership/agent-cli/resource-effects.ts b/apps/sim/lib/mothership/agent-cli/resource-effects.ts index fc914682308..b397022a51c 100644 --- a/apps/sim/lib/mothership/agent-cli/resource-effects.ts +++ b/apps/sim/lib/mothership/agent-cli/resource-effects.ts @@ -1,3 +1,4 @@ +import { createLogger } from '@sim/logger' import type { z } from 'zod' import type { AnyApiRouteContract } from '@/lib/api/contracts/types' import * as credentials from '@/lib/api/contracts/v2/credentials' @@ -18,6 +19,8 @@ import type { ResourceAddress, ResourceChange } from '@/lib/mothership/generated import { findNonTabFileIds } from '@/lib/mothership/resources/file-tabs' import { encodeVfsPathSegments } from '@/lib/vfs/path' +const logger = createLogger('ResourceEffects') + interface EffectContext { params: Record body?: unknown @@ -340,11 +343,21 @@ function matchParams(pattern: string, pathname: string): Record return params } -/** Only workspace files and chat uploads open as file tabs; other rows open as their owner. */ +/** + * Only workspace files and chat uploads open as file tabs; other rows open as their owner. The + * request already committed, so a failed lookup opens no file tab rather than failing it. + */ async function onlyFileTabs(changes: ResourceChange[]): Promise { const fileUpsertId = (change: ResourceChange) => change.op === 'upsert' && change.resource.type === 'file' ? change.resource.id : undefined - const nonTabIds = await findNonTabFileIds(changes.flatMap((change) => fileUpsertId(change) ?? [])) + const ids = changes.flatMap((change) => fileUpsertId(change) ?? []) + let nonTabIds: Set + try { + nonTabIds = await findNonTabFileIds(ids) + } catch (error) { + logger.error('Could not check which files open as tabs; opening none', { error }) + nonTabIds = new Set(ids) + } return changes.filter((change) => { const id = fileUpsertId(change) return id === undefined || !nonTabIds.has(id) diff --git a/apps/sim/lib/mothership/tools/server/tests.ts b/apps/sim/lib/mothership/tools/server/tests.ts index f34517fbc55..5b066ae2105 100644 --- a/apps/sim/lib/mothership/tools/server/tests.ts +++ b/apps/sim/lib/mothership/tools/server/tests.ts @@ -164,7 +164,10 @@ export const testsServerTool: BaseServerTool = { workspaceId, name: input.name, }) - return { deleted: input.name } + const resources: ResourceChange[] = [ + { op: 'remove', resource: { type: 'test', workspaceId, id: input.name } }, + ] + return { deleted: input.name, resources } } } }, diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts index e31b139f458..b25f2f445b0 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts @@ -2100,6 +2100,8 @@ export async function updateWorkspaceFileContent( * An omitted policy is classified as unknown rather than inheriting provenance across new bytes. */ secretProvenancePolicy?: WorkspaceFileSecretProvenancePolicy + /** Records what the file's owner derives from these bytes, atomically with the bytes. */ + commitOwner?: (tx: DbOrTx) => Promise } ): Promise { if (options.collabDocState && !options.expectedUpdatedAt) { @@ -2234,6 +2236,7 @@ export async function updateWorkspaceFileContent( if (!updatedFile) { throw new OrchestrationError('not_found', 'File not found or could not be updated') } + await options.commitOwner?.(tx) const nextProvenance = await applyWorkspaceFileSecretProvenancePolicyInTx( tx, diff --git a/apps/sim/lib/workflow-tests/application/run-tests.ts b/apps/sim/lib/workflow-tests/application/run-tests.ts index 11568a3c411..d5c1b3de382 100644 --- a/apps/sim/lib/workflow-tests/application/run-tests.ts +++ b/apps/sim/lib/workflow-tests/application/run-tests.ts @@ -19,9 +19,10 @@ import { testFilePath } from '@/lib/workflow-tests/paths' import { completeWorkflowTestRun, failWorkflowTestRun, - insertWorkflowTestRun, + insertWorkflowTestRuns, listLiveWorkflowTests, - readExecutedDeployments, + markWorkflowTestRunStarted, + readRanAgainst, recordWorkflowTestProgress, type WorkflowTestRow, } from '@/lib/workflow-tests/repository' @@ -46,6 +47,7 @@ async function executeTestRun(params: { const { runId, test, principal, version } = params let sourceHash: string | null = null try { + await markWorkflowTestRunStarted(runId) const file = await getWorkspaceFile(test.workspaceId, test.bodyFileId, { includeTestFiles: true, }) @@ -53,7 +55,7 @@ async function executeTestRun(params: { const buffer = await fetchWorkspaceFileBuffer(file, { maxBytes: MAX_TEST_SOURCE_BYTES }) const source = buffer.toString('utf-8') sourceHash = testSourceHash(source) - const report = await runWorkflowTestFile({ + const { report, enteredWorkflowIds } = await runWorkflowTestFile({ principal, workspaceId: test.workspaceId, source, @@ -65,7 +67,12 @@ async function executeTestRun(params: { const executionIds = report.tests.flatMap((result) => result.executions.map((execution) => execution.executionId) ) - const ranAgainst = await readExecutedDeployments(executionIds, test.workspaceId) + const ranAgainst = await readRanAgainst({ + executionIds, + enteredWorkflowIds, + workspaceId: test.workspaceId, + version, + }) await completeWorkflowTestRun(runId, report, sourceHash, ranAgainst) } catch (error) { logger.warn('Workflow test run failed', { @@ -113,16 +120,16 @@ export const startWorkflowTestRuns = defineAuthorizedWorkspaceUseCase({ const actor = toPrincipalActor(principal) const runs = tests.map((test) => ({ runId: generateId(), test })) - for (const { runId, test } of runs) { - await insertWorkflowTestRun({ + await insertWorkflowTestRuns( + runs.map(({ runId, test }) => ({ id: runId, testId: test.id, workspaceId: context.workspaceId, version: input.version, triggeredByActor: actor, triggeredByUserId: resolvePrincipalSubjectUserId(principal) ?? null, - }) - } + })) + ) void (async () => { for (const { runId, test } of runs) { await executeTestRun({ diff --git a/apps/sim/lib/workflow-tests/application/tests.ts b/apps/sim/lib/workflow-tests/application/tests.ts index ce89920e767..038ecabb4a2 100644 --- a/apps/sim/lib/workflow-tests/application/tests.ts +++ b/apps/sim/lib/workflow-tests/application/tests.ts @@ -65,10 +65,10 @@ interface WorkflowTestRanAgainst { version: number | null draft: boolean liveVersion: number | null - /** The workflow was redeployed or deleted after this run. */ + /** The workflow was redeployed, its draft edited, or it was deleted after this run. */ stale: boolean - /** One execution of it in this run, whose snapshot is the workflow as it ran. */ - executionId: string + /** A `runWorkflow()` execution of it, whose snapshot is the workflow as it ran; null for a child. */ + executionId: string | null } type RunRow = Omit @@ -97,12 +97,23 @@ function presentRanAgainst(row: RunRow, facts: DeploymentFacts): WorkflowTestRan version: deploymentId === null ? null : (facts.versions.get(deploymentId) ?? null), draft: deploymentId === null, liveVersion: live?.version ?? null, - stale: name === null || (deploymentId !== null && live?.id !== deploymentId), + stale: + name === null || + (deploymentId === null ? draftEdited(entry, facts) : live?.id !== deploymentId), executionId: entry.executionId, } }) } +function draftEdited(entry: RanAgainstEntry, facts: DeploymentFacts): boolean { + const updatedAt = facts.updatedAt.get(entry.workflowId) + return ( + entry.draftUpdatedAt === null || + updatedAt === undefined || + updatedAt.getTime() > Date.parse(entry.draftUpdatedAt) + ) +} + function runStatus(row: Omit): WorkflowTestRunSummary['status'] { if (row.status !== 'running') return row.status as WorkflowTestRunSummary['status'] return Date.now() - row.startedAt.getTime() > ABANDONED_RUN_MS ? 'error' : 'running' @@ -360,7 +371,7 @@ export const deleteWorkflowTest = defineAuthorizedWorkspaceUseCase({ authorizationOptions, authorizeResource: ({ context }) => requireWorkflowTestsEnabled(context.workspaceOrganizationId), async execute({ context }) { - await softDeleteWorkflowTest(context.test.id) + await softDeleteWorkflowTest(context.test) return { deleted: true } }, }) diff --git a/apps/sim/lib/workflow-tests/paths.ts b/apps/sim/lib/workflow-tests/paths.ts index 37db40bf021..9a824a5f765 100644 --- a/apps/sim/lib/workflow-tests/paths.ts +++ b/apps/sim/lib/workflow-tests/paths.ts @@ -1,3 +1,4 @@ +import { TEST_NAME_PATTERN } from '@/lib/api/contracts/mothership-tests' import { OrchestrationError } from '@/lib/core/orchestration/types' import { decodeVfsPathSegments } from '@/lib/mothership/vfs/path-utils' @@ -5,8 +6,6 @@ import { decodeVfsPathSegments } from '@/lib/mothership/vfs/path-utils' const TEST_FILE_PREFIX = 'tests/' export const TEST_FILE_SUFFIX = '.test.js' -const TEST_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,79}$/ - export function testFilePath(name: string): string { return `${TEST_FILE_PREFIX}${name}${TEST_FILE_SUFFIX}` } diff --git a/apps/sim/lib/workflow-tests/repository.ts b/apps/sim/lib/workflow-tests/repository.ts index eec2fc9bdff..5ea1d280954 100644 --- a/apps/sim/lib/workflow-tests/repository.ts +++ b/apps/sim/lib/workflow-tests/repository.ts @@ -5,6 +5,7 @@ import { workflowExecutionLogs, workflowTest, workflowTestRun, + workspaceFiles, } from '@sim/db/schema' import { and, desc, eq, inArray, isNull, sql } from 'drizzle-orm' import type { DbOrTx, DbTransaction } from '@/lib/db/types' @@ -89,36 +90,136 @@ export async function listLiveWorkflowTests(workspaceId: string): Promise { - await db - .update(workflowTest) - .set({ deletedAt: new Date(), updatedAt: new Date() }) - .where(eq(workflowTest.id, testId)) -} - -export async function insertWorkflowTestRun(values: { +/** Deletes a test with its source file; soft-delete cleanup later releases the file's storage. */ +export async function softDeleteWorkflowTest(test: { id: string - testId: string - workspaceId: string - version: WorkflowTestVersion - triggeredByActor: unknown - triggeredByUserId: string | null + bodyFileId: string }): Promise { - await db.insert(workflowTestRun).values({ ...values, status: 'running' }) + const now = new Date() + await db.transaction(async (tx) => { + await tx + .update(workflowTest) + .set({ deletedAt: now, updatedAt: now }) + .where(eq(workflowTest.id, test.id)) + await tx + .update(workspaceFiles) + .set({ deletedAt: now, updatedAt: now }) + .where( + and( + eq(workspaceFiles.id, test.bodyFileId), + eq(workspaceFiles.context, 'test'), + isNull(workspaceFiles.deletedAt) + ) + ) + }) +} + +/** Every file's run row in one statement, so a failure leaves none of them running. */ +export async function insertWorkflowTestRuns( + rows: Array<{ + id: string + testId: string + workspaceId: string + version: WorkflowTestVersion + triggeredByActor: unknown + triggeredByUserId: string | null + }> +): Promise { + await db.insert(workflowTestRun).values(rows.map((row) => ({ ...row, status: 'running' }))) } -/** A workflow a run executed, the deployment it ran (`null` is the draft), and one execution of it. */ +/** Files run one after another, so a run's clock starts when its file does, not when it queued. */ +export async function markWorkflowTestRunStarted(runId: string): Promise { + await db + .update(workflowTestRun) + .set({ startedAt: new Date() }) + .where(eq(workflowTestRun.id, runId)) +} + +/** A workflow a run executed and the deployment it ran; `null` is the draft. */ export interface RanAgainstEntry { workflowId: string deploymentVersionId: string | null - executionId: string + /** A `runWorkflow()` execution of it, whose snapshot is the workflow as it ran; null for a child. */ + executionId: string | null + /** For a draft, the workflow's `updatedAt` when the run ended; a later edit makes the run stale. */ + draftUpdatedAt: string | null +} + +type ExecutedWorkflow = Omit + +/** + * Every workflow a run executed: the ones `runWorkflow()` started, from their execution logs, and + * the children they called, which run inside their parent's execution and so log nothing of their + * own. A child ran the same version as its run: its live deployment, or its draft. + */ +export async function readRanAgainst(params: { + executionIds: string[] + enteredWorkflowIds: string[] + workspaceId: string + version: WorkflowTestVersion +}): Promise { + const started = await readExecutedDeployments(params.executionIds, params.workspaceId) + const startedIds = new Set(started.map((entry) => entry.workflowId)) + const childIds = params.enteredWorkflowIds.filter((id) => !startedIds.has(id)) + const children = await readChildVersions(childIds, params.version) + const executed = [...started, ...children] + const drafts = executed.filter((entry) => entry.deploymentVersionId === null) + const updatedAt = new Map( + drafts.length === 0 + ? [] + : ( + await db + .select({ id: workflow.id, updatedAt: workflow.updatedAt }) + .from(workflow) + .where( + inArray( + workflow.id, + drafts.map((entry) => entry.workflowId) + ) + ) + ).map((row) => [row.id, row.updatedAt.toISOString()]) + ) + return executed.map((entry) => ({ + ...entry, + draftUpdatedAt: + entry.deploymentVersionId === null ? (updatedAt.get(entry.workflowId) ?? null) : null, + })) +} + +async function readChildVersions( + workflowIds: string[], + version: WorkflowTestVersion +): Promise { + if (workflowIds.length === 0) return [] + if (version === 'draft') { + return workflowIds.map((workflowId) => ({ + workflowId, + deploymentVersionId: null, + executionId: null, + })) + } + const active = await db + .select({ workflowId: workflowDeploymentVersion.workflowId, id: workflowDeploymentVersion.id }) + .from(workflowDeploymentVersion) + .where( + and( + inArray(workflowDeploymentVersion.workflowId, workflowIds), + eq(workflowDeploymentVersion.isActive, true) + ) + ) + return active.map((row) => ({ + workflowId: row.workflowId, + deploymentVersionId: row.id, + executionId: null, + })) } /** The workflows and deployments these executions ran, from their execution logs. */ -export async function readExecutedDeployments( +async function readExecutedDeployments( executionIds: string[], workspaceId: string -): Promise { +): Promise { if (executionIds.length === 0) return [] const rows = await db .selectDistinctOn( @@ -152,6 +253,8 @@ export async function readExecutedDeployments( export interface DeploymentFacts { names: Map + /** When each workflow last changed, draft edits included. */ + updatedAt: Map /** Active deployment per workflow. */ active: Map /** Version number of every deployment asked about. */ @@ -163,11 +266,16 @@ export async function readDeploymentFacts( workflowIds: string[], deploymentVersionIds: string[] ): Promise { - const facts: DeploymentFacts = { names: new Map(), active: new Map(), versions: new Map() } + const facts: DeploymentFacts = { + names: new Map(), + updatedAt: new Map(), + active: new Map(), + versions: new Map(), + } if (workflowIds.length === 0) return facts const [workflows, active, versions] = await Promise.all([ db - .select({ id: workflow.id, name: workflow.name }) + .select({ id: workflow.id, name: workflow.name, updatedAt: workflow.updatedAt }) .from(workflow) .where(and(inArray(workflow.id, workflowIds), isNull(workflow.archivedAt))), db @@ -190,7 +298,10 @@ export async function readDeploymentFacts( .from(workflowDeploymentVersion) .where(inArray(workflowDeploymentVersion.id, deploymentVersionIds)), ]) - for (const row of workflows) facts.names.set(row.id, row.name) + for (const row of workflows) { + facts.names.set(row.id, row.name) + facts.updatedAt.set(row.id, row.updatedAt) + } for (const row of active) facts.active.set(row.workflowId, { id: row.id, version: row.version }) for (const row of versions) facts.versions.set(row.id, row.version) return facts diff --git a/apps/sim/lib/workflow-tests/run-test-file.ts b/apps/sim/lib/workflow-tests/run-test-file.ts index 4ba22b65cd0..15ac944c457 100644 --- a/apps/sim/lib/workflow-tests/run-test-file.ts +++ b/apps/sim/lib/workflow-tests/run-test-file.ts @@ -30,25 +30,28 @@ export interface RunWorkflowTestFileInput { * Runs one test file in the sandbox. Each `runWorkflow()` it makes becomes a real workflow * run whose mocked blocks are answered by the file's own mocks. */ -export async function runWorkflowTestFile(input: RunWorkflowTestFileInput): Promise { +export async function runWorkflowTestFile( + input: RunWorkflowTestFileInput +): Promise<{ report: TestReport; enteredWorkflowIds: string[] }> { const requestId = generateShortId(12) - openWorkflowTestSession( - requestId, - new WorkflowTestSession({ - workspaceId: input.workspaceId, - principal: input.principal, - version: input.version, - only: input.only, - onProgress: input.onProgress, - }) - ) + const session = new WorkflowTestSession({ + workspaceId: input.workspaceId, + principal: input.principal, + version: input.version, + only: input.only, + onProgress: input.onProgress, + }) + openWorkflowTestSession(requestId, session) try { const bytes = await runSandboxTask( 'workflow-test-run', { workspaceId: input.workspaceId, code: input.source, codeFilename: input.filePath }, { requestId, ownerKey: `workspace:${input.workspaceId}`, signal: input.signal } ) - return testReportSchema.parse(JSON.parse(Buffer.from(bytes).toString('utf-8'))) + return { + report: testReportSchema.parse(JSON.parse(Buffer.from(bytes).toString('utf-8'))), + enteredWorkflowIds: [...session.enteredWorkflowIds], + } } finally { closeWorkflowTestSession(requestId) } diff --git a/apps/sim/lib/workflow-tests/session.ts b/apps/sim/lib/workflow-tests/session.ts index 131c8e67ed0..1e132b05511 100644 --- a/apps/sim/lib/workflow-tests/session.ts +++ b/apps/sim/lib/workflow-tests/session.ts @@ -1,7 +1,8 @@ import { type Principal, requirePrincipalSubjectUserId } from '@sim/auth/principal' +import { toStringOrNull } from '@sim/utils/coerce' import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' -import { isRecordLike } from '@sim/utils/object' +import { isRecordLike, toRecord } from '@sim/utils/object' import type { BlockState } from '@sim/workflow-types/workflow' import { OrchestrationError } from '@/lib/core/orchestration/types' import { type JudgeVerdict, judgeRubric } from '@/lib/workflow-tests/judge' @@ -29,6 +30,8 @@ import { normalizeName } from '@/executor/constants' import type { TestWorkflowBlock } from '@/executor/execution/types' import type { ExecutionResult } from '@/executor/types' import { hasExecutionResult, readAttemptedExecutionId } from '@/executor/utils/errors' +import { projectResolvedSecretModelContent } from '@/executor/utils/resolved-secret-content-projection' +import type { ResolvedSecretTraceRegistry } from '@/executor/utils/resolved-secret-trace-registry' import { getToolOutputsMetadata } from '@/tools/metadata-outputs' import { hasToolId } from '@/tools/tool-ids' @@ -87,6 +90,15 @@ async function loadWorkflowBlocks( return state.blocks } +/** + * A child's block names, read only to tell a mock of an unreached block from a misspelled one. + * The draft names them whether or not the child is deployed or ever runs; a deleted child names + * none, and running it fails in the executor if the run reaches it. + */ +async function loadChildBlockNames(workflowId: string): Promise> { + return (await loadWorkflowFromNormalizedTables(workflowId))?.blocks ?? {} +} + /** * The trigger block a run starts from: the one the test names, or the workflow's only one. * A workflow with several triggers must be told which, by name. @@ -175,10 +187,15 @@ class RunMatcher implements MockMatcher { /** Workflows whose block names are known: every one that ran, and the children they call. */ private readonly knownWorkflows = new Set() + /** Whether the tested workflow started; secrets resolve only after it has. */ + entered = false + /** The tested workflow's registry, which redacts its output before the test sees it. */ + outputRegistry: ResolvedSecretTraceRegistry | undefined constructor( targets: TestTarget[], - private readonly loadChildBlocks: (workflowId: string) => Promise> + private readonly loadChildBlocks: (workflowId: string) => Promise>, + private readonly onEnter: (workflowId: string) => void ) { for (const target of targets) if (target.kind === 'tool') assertKnownTool(target.tool) this.targets = [...targets].sort( @@ -189,10 +206,17 @@ class RunMatcher implements MockMatcher { async enterWorkflow({ workflowId, blocks, + resolvedSecretTraceRegistry, }: { workflowId: string blocks: TestWorkflowBlock[] + resolvedSecretTraceRegistry: ResolvedSecretTraceRegistry | undefined }): Promise { + if (!this.entered) { + this.entered = true + this.outputRegistry = resolvedSecretTraceRegistry + } + this.onEnter(workflowId) const workflowName = await this.workflowName(workflowId) for (const block of blocks) this.blocks.set(block.id, block) await this.learnWorkflow( @@ -317,7 +341,11 @@ class TestWorkflowRun { (error: unknown) => { const executionId = readAttemptedExecutionId(error) ?? '' if (hasExecutionResult(error)) return this.toOutcome(executionId, error.executionResult) - return { executionId, output: null, error: getErrorMessage(error), logs: [] } + return { + executionId, + ...this.redact({ output: null, error: getErrorMessage(error) }), + logs: [], + } } ) void this.outcome.finally(() => this.channel.close(new Error('The workflow run finished'))) @@ -330,12 +358,24 @@ class TestWorkflowRun { : result.success ? undefined : (result.error ?? 'The workflow run failed') - return { - executionId, - output: result.output, - ...(error ? { error } : {}), - logs: result.logs ?? [], + const redacted = this.redact({ output: result.output, error }) + return { executionId, ...redacted, logs: result.logs ?? [] } + } + + /** What crosses into the sandbox, with this run's resolved secrets replaced; fails closed. */ + private redact(value: { output: unknown; error: string | undefined }): { + output: unknown + error?: string + } { + if (!this.matcher.entered) + return { output: null, ...(value.error ? { error: value.error } : {}) } + const projection = projectResolvedSecretModelContent(value, this.matcher.outputRegistry) + if (!projection.safe) { + return { output: null, error: 'The workflow result could not be redacted of its secrets' } } + const projected = toRecord(projection.value) + const error = toStringOrNull(projected.error) + return { output: projected.output ?? null, ...(error ? { error } : {}) } } async next(): Promise { @@ -426,6 +466,8 @@ class TestWorkflowRun { */ export class WorkflowTestSession { private readonly runs = new Map() + /** Every workflow this file's runs executed, children included. */ + readonly enteredWorkflowIds = new Set() constructor(readonly config: WorkflowTestSessionConfig) {} @@ -439,8 +481,8 @@ export class WorkflowTestSession { const workflowId = await resolveWorkflowId(workspaceId, args.workflow) const blocks = await loadWorkflowBlocks(workflowId, workspaceId, version) const triggerBlockId = chooseTrigger(args.workflow, blocks, args.trigger) - const matcher = new RunMatcher(args.targets, (childId) => - loadWorkflowBlocks(childId, workspaceId, version) + const matcher = new RunMatcher(args.targets, loadChildBlockNames, (id) => + this.enteredWorkflowIds.add(id) ) const channel = createMockChannel(matcher) const abort = new AbortController() diff --git a/apps/sim/lib/workflow-tests/source-write.ts b/apps/sim/lib/workflow-tests/source-write.ts index 097fcd2463d..212e5aeef2f 100644 --- a/apps/sim/lib/workflow-tests/source-write.ts +++ b/apps/sim/lib/workflow-tests/source-write.ts @@ -1,5 +1,6 @@ import { isUtf8 } from 'node:buffer' import { OrchestrationError } from '@/lib/core/orchestration/types' +import type { DbOrTx } from '@/lib/db/types' import { readWorkflowTestCases, testSourceHash } from '@/lib/workflow-tests/definition' import { getLiveWorkflowTestByBodyFileId, @@ -25,7 +26,7 @@ export async function prepareWorkflowTestSourceWrite(input: { fileId: string workspaceId: string content: Buffer -}): Promise<() => Promise> { +}): Promise<(tx: DbOrTx) => Promise> { const test = await getLiveWorkflowTestByBodyFileId(input.fileId) if (!test) throw new OrchestrationError('not_found', 'File not found') const source = decodeTestSource(input.content) @@ -34,5 +35,5 @@ export async function prepareWorkflowTestSourceWrite(input: { name: test.name, source, }) - return () => updateWorkflowTestCases(test.id, cases, testSourceHash(source)) + return (tx) => updateWorkflowTestCases(test.id, cases, testSourceHash(source), tx) } diff --git a/apps/sim/lib/workflows/application/run-workflow-for-test.ts b/apps/sim/lib/workflows/application/run-workflow-for-test.ts index b4b9a45f501..ff56ae05f5f 100644 --- a/apps/sim/lib/workflows/application/run-workflow-for-test.ts +++ b/apps/sim/lib/workflows/application/run-workflow-for-test.ts @@ -4,6 +4,7 @@ import { type WorkflowExecutionPrincipal, } from '@sim/auth/principal' import { generateId } from '@sim/utils/id' +import { getActivelyBannedUserIds } from '@/lib/auth/ban' import { resolveBillingAttribution } from '@/lib/billing/core/billing-attribution' import { checkExecutionUsageLimits } from '@/lib/billing/core/usage-gate-cache' import { OrchestrationError } from '@/lib/core/orchestration/types' @@ -66,6 +67,10 @@ export async function runWorkflowForTest( }) // actorless-unsupported: tests run only for session, personal-key, OAuth, or delegated principals; workflow_tests.run admits no executor or workspace-key caller. const actorUserId = requirePrincipalSubjectUserId(principal) + // A suite outlives the request that started it, so a ban mid-suite stops the next run. + if ((await getActivelyBannedUserIds([actorUserId])).length > 0) { + throw new OrchestrationError('forbidden', 'This account is suspended') + } const executionId = generateId() const billingAttribution = await resolveBillingAttribution({ actorUserId, diff --git a/apps/sim/lib/workspace-files/application/edit-workspace-file-content.ts b/apps/sim/lib/workspace-files/application/edit-workspace-file-content.ts index f22c58728c6..84c06aa01b5 100644 --- a/apps/sim/lib/workspace-files/application/edit-workspace-file-content.ts +++ b/apps/sim/lib/workspace-files/application/edit-workspace-file-content.ts @@ -201,6 +201,7 @@ export const editWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ * The caller's own revision when it sent one, so the guard covers everything since * the content it read — not merely since this use case loaded the file. */ + commitOwner, expectedUpdatedAt: input.expectedRevision ? parseWorkspaceFileRevision(input.expectedRevision, context.fileId) : file.contentUpdatedAt, @@ -222,7 +223,6 @@ export const editWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ throw error } - await commitOwner() logger.info('Edited workspace file content', { workspaceId: context.workspaceId, fileId: context.fileId, diff --git a/apps/sim/lib/workspace-files/application/file-versions.ts b/apps/sim/lib/workspace-files/application/file-versions.ts index e2ab6748dd4..4dc23ce42a8 100644 --- a/apps/sim/lib/workspace-files/application/file-versions.ts +++ b/apps/sim/lib/workspace-files/application/file-versions.ts @@ -339,6 +339,7 @@ async function executeRevertWorkspaceFileVersion({ }), expectedUpdatedAt: expectedContentAt ?? file.contentUpdatedAt ?? file.updatedAt, secretProvenancePolicy: { mode: 'reinstate', snapshot: provenance }, + commitOwner, } ) } catch (error) { @@ -348,7 +349,6 @@ async function executeRevertWorkspaceFileVersion({ throw error } - await commitOwner() logger.info('Reverted workspace file to a previous version', { workspaceId: context.workspaceId, fileId: context.fileId, diff --git a/apps/sim/lib/workspace-files/application/owned-file-content.ts b/apps/sim/lib/workspace-files/application/owned-file-content.ts index b5d8b9c525a..a0002e8f1ec 100644 --- a/apps/sim/lib/workspace-files/application/owned-file-content.ts +++ b/apps/sim/lib/workspace-files/application/owned-file-content.ts @@ -1,15 +1,17 @@ +import type { DbOrTx } from '@/lib/db/types' import type { ActiveWorkspaceFileContext } from '@/lib/uploads/contexts/workspace/workspace-file-manager' /** * Checks content headed for a file another resource owns before it is stored, and returns - * what to run once it is. A test file's source must load and declare one concern, and its - * test then records the new title and cases; every other file passes unchanged. + * the step the file write runs in its transaction. A test file's source must load and declare + * one concern, and its test then records the new cases with the bytes; every other file has + * no step. */ export async function prepareOwnedFileContentWrite( context: Pick, content: Buffer -): Promise<() => Promise> { - if (context.fileContext !== 'test') return async () => {} +): Promise<((tx: DbOrTx) => Promise) | undefined> { + if (context.fileContext !== 'test') return undefined const { prepareWorkflowTestSourceWrite } = await import('@/lib/workflow-tests/source-write') return prepareWorkflowTestSourceWrite({ fileId: context.fileId, diff --git a/apps/sim/lib/workspace-files/application/update-workspace-file-content.ts b/apps/sim/lib/workspace-files/application/update-workspace-file-content.ts index 105f364c4ab..6385bb119b9 100644 --- a/apps/sim/lib/workspace-files/application/update-workspace-file-content.ts +++ b/apps/sim/lib/workspace-files/application/update-workspace-file-content.ts @@ -78,6 +78,7 @@ async function updateAuthorizedWorkspaceFileContent({ version: resolveWorkspaceFileVersionWrite(principal), ...(expectedUpdatedAt ? { expectedUpdatedAt } : {}), syncLiveDoc: input.syncLiveDoc, + commitOwner, secretProvenancePolicy: { ...(input.provenanceMode === 'preserve' ? { mode: 'preserve' as const } @@ -95,7 +96,6 @@ async function updateAuthorizedWorkspaceFileContent({ throw error } - await commitOwner() logger.info('Updated workspace file content', { workspaceId: canonical.workspaceId, fileId: canonical.fileId, diff --git a/apps/sim/lib/workspace-files/application/workspace-file-context.ts b/apps/sim/lib/workspace-files/application/workspace-file-context.ts index e15d9af0f77..4d6ed81c17f 100644 --- a/apps/sim/lib/workspace-files/application/workspace-file-context.ts +++ b/apps/sim/lib/workspace-files/application/workspace-file-context.ts @@ -25,13 +25,18 @@ export interface WorkspaceFileContextInput { ownedFilePrincipal?: Principal } -/** Principals the test operations admit; workspace API keys and system callers never reach one. */ -const OWNED_FILE_PRINCIPAL_KINDS = new Set([ - 'session', - 'personal_api_key', - 'oauth_access_token', - 'delegated', -]) +/** + * The principals the test operations admit: people and Copilot acting for one. Workspace API + * keys, system callers, and the executor's delegation never reach a test file. + */ +function isOwnedFilePrincipal(principal: Principal): boolean { + if (principal.kind === 'delegated') return principal.serviceId === 'copilot' + return ( + principal.kind === 'session' || + principal.kind === 'personal_api_key' || + principal.kind === 'oauth_access_token' + ) +} /** * A test file follows its test: it needs a live test and admits only the test operations' @@ -42,7 +47,7 @@ export async function assertOwnedFileAccess( context: ActiveWorkspaceFileContext ): Promise { if (context.fileContext !== 'test') return - if (!OWNED_FILE_PRINCIPAL_KINDS.has(principal.kind)) { + if (!isOwnedFilePrincipal(principal)) { throw new OrchestrationError('not_found', 'File not found') } if (!(await getLiveWorkflowTestByBodyFileId(context.fileId))) { diff --git a/apps/sim/providers/runtime-context.ts b/apps/sim/providers/runtime-context.ts index 05c5f67680f..7e04250bb97 100644 --- a/apps/sim/providers/runtime-context.ts +++ b/apps/sim/providers/runtime-context.ts @@ -1,7 +1,7 @@ import { AsyncLocalStorage } from 'node:async_hooks' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' -import { isRecordLike, omit } from '@sim/utils/object' +import { isRecordLike, omit, toRecord } from '@sim/utils/object' import { durableSecretProvenanceFromRegistry, importDurableSecretProvenance, @@ -19,6 +19,7 @@ import { } from '@/executor/constants' import type { ExecutionTestHooks, MockedToolCall } from '@/executor/execution/types' import type { ExecutionContext } from '@/executor/types' +import { projectResolvedSecretModelContent } from '@/executor/utils/resolved-secret-content-projection' import type { ResolvedSecretTraceRegistry } from '@/executor/utils/resolved-secret-trace-registry' import { stripCloneSuffixes } from '@/executor/utils/subflow-node-id-codec' import { getPreparedProviderToolInputProvenance } from '@/providers/tool-input-provenance' @@ -132,11 +133,11 @@ function modelSuppliedToolInput( toolId: string, params: Record ): Record { - const declared = getToolParams(toolId) + const declared = getToolParams(toolId) ?? {} const input: Record = {} for (const [key, value] of Object.entries(params)) { - const visibility = declared?.[key]?.visibility - if (key !== '_context' && visibility !== 'user-only' && visibility !== 'hidden') { + const visibility = declared[key]?.visibility + if (Object.hasOwn(declared, key) && visibility !== 'user-only' && visibility !== 'hidden') { input[key] = value } } @@ -152,6 +153,7 @@ async function resolveMockedTool( try { return { success: true, output: await testHooks.resolveToolMock(call, abortSignal) } } catch (error) { + if (abortSignal?.aborted) throw error return { success: false, output: {}, error: getErrorMessage(error) } } } @@ -268,15 +270,21 @@ export async function executeProviderTool( !memoryRetrieval && testHooks.mocksTool(callerBlockId, executionToolId) ) { - const response = await resolveMockedTool( - testHooks, - { - blockId: callerBlockId, - toolId: executionToolId, - input: modelSuppliedToolInput(executionToolId, params), - }, - callerContext?.abortSignal + const input = projectResolvedSecretModelContent( + modelSuppliedToolInput(executionToolId, params), + registry ) + const response: ToolResponse = input.safe + ? await resolveMockedTool( + testHooks, + { blockId: callerBlockId, toolId: executionToolId, input: toRecord(input.value) }, + callerContext?.abortSignal + ) + : { + success: false, + output: {}, + error: `Mocked tool "${executionToolId}" has inputs whose secrets cannot be redacted`, + } return { rawResponse: response, modelResponse: response } } diff --git a/apps/sim/sandbox-tasks/workflow-test.ts b/apps/sim/sandbox-tasks/workflow-test.ts index 24eb81701bb..f7dad23c242 100644 --- a/apps/sim/sandbox-tasks/workflow-test.ts +++ b/apps/sim/sandbox-tasks/workflow-test.ts @@ -158,6 +158,22 @@ const HARNESS = ` return track(assertion, check); }; Object.setPrototypeOf(expect, baseExpect); + expect.assertions = (count) => { + if (!currentTest) throw new Error('expect.assertions() can only be called inside a test'); + if (!Number.isInteger(count) || count < 0) throw new Error('expect.assertions() needs a whole number'); + currentTest.expectedAssertions = count; + }; + expect.hasAssertions = () => { + if (!currentTest) throw new Error('expect.hasAssertions() can only be called inside a test'); + currentTest.expectsAssertions = true; + }; + function checkAssertionCount(test) { + const made = test.checks.length; + if (test.expectedAssertions !== null && made !== test.expectedAssertions) { + throw new Error('Expected ' + test.expectedAssertions + ' assertions to be called but received ' + made); + } + if (test.expectsAssertions && made === 0) throw new Error('Expected at least one assertion to be called but received none'); + } baseExpect.extend({ async toMatchRubric(received, rubric, options) { @@ -275,6 +291,7 @@ const HARNESS = ` async function runWorkflow(workflow, input, options) { requireRunning('runWorkflow()'); + if (!currentTest) throw new Error('runWorkflow() runs only inside a test, beforeEach, or afterEach; beforeAll and afterAll have no test to record it on'); requireName('runWorkflow()', workflow); if (options !== undefined) { const extra = options === null || typeof options !== 'object' ? ['options'] : Object.keys(options).filter((key) => key !== 'trigger'); @@ -322,11 +339,18 @@ const HARNESS = ` }; } - function withTimeout(promise, ms, label) { + /** A timed-out case keeps running in the isolate, so every later case is reported unrun. */ + let timedOutTest = null; + function withTimeout(promise, ms, label, onTimeout) { let timer; return Promise.race([ promise, - new Promise((_, reject) => { timer = setTimeout(() => reject(new Error(label + ' timed out after ' + ms + 'ms')), ms); }), + new Promise((_, reject) => { + timer = setTimeout(() => { + onTimeout(); + reject(new Error(label + ' timed out after ' + ms + 'ms')); + }, ms); + }), ]).finally(() => clearTimeout(timer)); } @@ -361,11 +385,17 @@ const HARNESS = ` async function runOne(suite, test, onlyNames) { const path = [...suitePath(suite), test.name]; - const result = { path, status: 'pass', durationMs: 0, checks: [], executions: [], logs: [], runIds: [], matchedKeys: new Set(), activeCheck: null }; + const result = { path, status: 'pass', durationMs: 0, checks: [], executions: [], logs: [], runIds: [], matchedKeys: new Set(), activeCheck: null, expectedAssertions: null, expectsAssertions: false }; if (!willRun(suite, test, onlyNames)) { result.status = 'skip'; return result; } + if (timedOutTest) { + result.status = 'fail'; + result.error = { name: 'Error', message: 'Not run: "' + timedOutTest + '" timed out and may still be running' }; + await call('testProgress', { path, status: result.status }); + return result; + } await call('testProgress', { path, status: 'running' }); const startedAt = Date.now(); currentTest = result; @@ -377,12 +407,13 @@ const HARNESS = ` } finally { for (const s of suiteChain(suite).reverse()) await runHooks(s.hooks.afterEach); } + checkAssertionCount(result); const unmatched = [...registrations.values()].filter((r) => r.test === result && result.runIds.length > 0 && !result.matchedKeys.has(r.key)); if (unmatched.length > 0) { const names = unmatched.map(describeRegistration); throw new Error(names.join(', ') + ' matched no block in the workflows this test ran'); } - })(), test.timeoutMs, 'Test'); + })(), test.timeoutMs, 'Test', () => { timedOutTest = path.join(' > '); }); } catch (error) { result.status = 'fail'; const owned = result.checks.find((check) => check.status === 'fail'); diff --git a/apps/sim/stores/workflow-tests/store.ts b/apps/sim/stores/workflow-tests/store.ts index 164461fc2b9..4e5837f0e3e 100644 --- a/apps/sim/stores/workflow-tests/store.ts +++ b/apps/sim/stores/workflow-tests/store.ts @@ -9,6 +9,7 @@ import { devtools } from 'zustand/middleware' interface TestRunSelectionState { selectedRunIds: Record selectRun: (testKey: string, runId: string | null) => void + reset: () => void } export const useTestRunSelectionStore = create()( @@ -20,6 +21,7 @@ export const useTestRunSelectionStore = create()( const { [testKey]: _previous, ...rest } = state.selectedRunIds return { selectedRunIds: runId === null ? rest : { ...rest, [testKey]: runId } } }), + reset: () => set({ selectedRunIds: {} }), }), { name: 'test-run-selection' } ) From 83a1daf1fce0fa8af63490714b65a354df215873 Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Thu, 8 Oct 2026 11:02:06 -0700 Subject: [PATCH 06/14] fix(tests): reserve run slots, await pending assertions, keep dynamic tool args - Each test workflow run reserves and releases an execution slot. - A case waits for assertions it did not await and fails if one fails. - Mocked MCP and custom tools keep the arguments their schema declares. - A closed session refuses starts still awaiting their lookups. - Stable refresh callback; scroll fade on the results pane. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EHGBgHrtePpi7KMrEfy41R --- .../tests/components/test-dashboard.tsx | 13 ++++++- apps/sim/hooks/queries/workflow-tests.ts | 5 ++- apps/sim/lib/workflow-tests/session.ts | 4 ++ .../application/run-workflow-for-test.ts | 37 +++++++++++++++++++ apps/sim/providers/runtime-context.ts | 17 ++++++--- apps/sim/sandbox-tasks/workflow-test.ts | 18 ++++++--- 6 files changed, 78 insertions(+), 16 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx b/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx index 43256c1cfe3..673e7c50d73 100644 --- a/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx @@ -1,16 +1,19 @@ 'use client' -import { lazy, type ReactNode, Suspense, useEffect, useState } from 'react' +import { lazy, type ReactNode, Suspense, useEffect, useRef, useState } from 'react' import { Badge, cn, DashboardMetric, + scrollFadeAttributes, + scrollFadeClass, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, + useScrollEdges, } from '@sim/emcn' import { Loader } from '@sim/emcn/icons' import { formatDuration, formatRelativeTime } from '@sim/utils/formatting' @@ -59,6 +62,8 @@ interface TestDashboardProps { * freshness, every case, and what it ran against. A latest run still going shows live. */ export function TestDashboard({ workspaceId, name, detail }: TestDashboardProps) { + const scrollRef = useRef(null) + const edges = useScrollEdges(scrollRef) const selectedRunId = useTestRunSelectionStore( (state) => state.selectedRunIds[testRunSelectionKey(workspaceId, name)] @@ -89,7 +94,11 @@ export function TestDashboard({ workspaceId, name, detail }: TestDashboardProps) const durationMs = run?.report?.tests.reduce((total, result) => total + result.durationMs, 0) return ( -
+
diff --git a/apps/sim/hooks/queries/workflow-tests.ts b/apps/sim/hooks/queries/workflow-tests.ts index cc0cd865d7a..d1575165558 100644 --- a/apps/sim/hooks/queries/workflow-tests.ts +++ b/apps/sim/hooks/queries/workflow-tests.ts @@ -1,3 +1,4 @@ +import { useCallback } from 'react' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { requestJson } from '@/lib/api/client/request' import { @@ -83,10 +84,10 @@ export function useWorkflowTestRun(workspaceId: string, name: string, runId: str /** Refetches one test after its file changes, so its cases follow the edit. */ export function useRefreshWorkflowTest(workspaceId: string, name: string) { const queryClient = useQueryClient() - return () => { + return useCallback(() => { queryClient.invalidateQueries({ queryKey: workflowTestKeys.lists() }) queryClient.invalidateQueries({ queryKey: workflowTestKeys.test(workspaceId, name) }) - } + }, [queryClient, workspaceId, name]) } export function useRunWorkflowTests(workspaceId: string) { diff --git a/apps/sim/lib/workflow-tests/session.ts b/apps/sim/lib/workflow-tests/session.ts index 1e132b05511..0b0fe2b09c6 100644 --- a/apps/sim/lib/workflow-tests/session.ts +++ b/apps/sim/lib/workflow-tests/session.ts @@ -468,6 +468,8 @@ export class WorkflowTestSession { private readonly runs = new Map() /** Every workflow this file's runs executed, children included. */ readonly enteredWorkflowIds = new Set() + /** Set when the sandbox run ends; a start still awaiting its lookups then starts nothing. */ + private closed = false constructor(readonly config: WorkflowTestSessionConfig) {} @@ -481,6 +483,7 @@ export class WorkflowTestSession { const workflowId = await resolveWorkflowId(workspaceId, args.workflow) const blocks = await loadWorkflowBlocks(workflowId, workspaceId, version) const triggerBlockId = chooseTrigger(args.workflow, blocks, args.trigger) + if (this.closed) throw new Error('This workflow test run has ended') const matcher = new RunMatcher(args.targets, loadChildBlockNames, (id) => this.enteredWorkflowIds.add(id) ) @@ -527,6 +530,7 @@ export class WorkflowTestSession { } close(): void { + this.closed = true this.cancel([...this.runs.keys()]) } diff --git a/apps/sim/lib/workflows/application/run-workflow-for-test.ts b/apps/sim/lib/workflows/application/run-workflow-for-test.ts index ff56ae05f5f..e6d81c96528 100644 --- a/apps/sim/lib/workflows/application/run-workflow-for-test.ts +++ b/apps/sim/lib/workflows/application/run-workflow-for-test.ts @@ -5,8 +5,13 @@ import { } from '@sim/auth/principal' import { generateId } from '@sim/utils/id' import { getActivelyBannedUserIds } from '@/lib/auth/ban' +import { + releaseExecutionSlot, + reserveExecutionSlot, +} from '@/lib/billing/calculations/usage-reservation' import { resolveBillingAttribution } from '@/lib/billing/core/billing-attribution' import { checkExecutionUsageLimits } from '@/lib/billing/core/usage-gate-cache' +import { getExecutionTimeout, RESERVATION_TTL_BUFFER_MS } from '@/lib/core/execution-limits/types' import { OrchestrationError } from '@/lib/core/orchestration/types' import { generateRequestId } from '@/lib/core/utils/request' import { resolveActiveWorkflowApplicationContext } from '@/lib/workflows/application/context' @@ -80,6 +85,36 @@ export async function runWorkflowForTest( if (usage.isExceeded) { throw new OrchestrationError('forbidden', usage.message ?? 'Usage limit exceeded') } + if (usage.payerUsage) { + const plan = billingAttribution.payerSubscription?.plan + const reservation = await reserveExecutionSlot({ + billingEntity: billingAttribution.billingEntity, + reservationId: executionId, + plan, + enterpriseConcurrencyLimit: billingAttribution.payerSubscription?.enterpriseConcurrencyLimit, + currentUsage: usage.payerUsage.currentUsage, + limit: usage.payerUsage.limit, + expiresAt: Date.now() + getExecutionTimeout(plan, 'sync') + RESERVATION_TTL_BUFFER_MS, + ...(billingAttribution.organizationId && + usage.memberUsage?.limit !== null && + usage.memberUsage?.limit !== undefined + ? { + member: { + organizationId: billingAttribution.organizationId, + actorUserId: billingAttribution.actorUserId, + currentUsage: usage.memberUsage.currentUsage, + limit: usage.memberUsage.limit, + }, + } + : {}), + }) + if (!reservation.reserved) { + throw new OrchestrationError( + 'forbidden', + `The workflow cannot start now (${reservation.reason}); wait for running executions to finish` + ) + } + } try { const result = await executeWorkflow( { @@ -108,6 +143,8 @@ export async function runWorkflowForTest( } catch (error) { attachAttemptedExecutionId(error, executionId) throw error + } finally { + await releaseExecutionSlot(executionId) } } diff --git a/apps/sim/providers/runtime-context.ts b/apps/sim/providers/runtime-context.ts index 7e04250bb97..5b7459e35d6 100644 --- a/apps/sim/providers/runtime-context.ts +++ b/apps/sim/providers/runtime-context.ts @@ -1,7 +1,7 @@ import { AsyncLocalStorage } from 'node:async_hooks' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' -import { isRecordLike, omit, toRecord } from '@sim/utils/object' +import { isRecordLike, omit, toRecord, toRecordOrNull } from '@sim/utils/object' import { durableSecretProvenanceFromRegistry, importDurableSecretProvenance, @@ -133,13 +133,18 @@ function modelSuppliedToolInput( toolId: string, params: Record ): Record { - const declared = getToolParams(toolId) ?? {} + const declared = getToolParams(toolId) + // MCP and custom tools declare their arguments only in the schema the model was given. + const dynamicArgs = declared + ? undefined + : toRecord(toRecordOrNull(params._toolSchema)?.properties ?? {}) const input: Record = {} for (const [key, value] of Object.entries(params)) { - const visibility = declared[key]?.visibility - if (Object.hasOwn(declared, key) && visibility !== 'user-only' && visibility !== 'hidden') { - input[key] = value - } + const visibility = declared?.[key]?.visibility + const isArgument = declared + ? Object.hasOwn(declared, key) + : Object.hasOwn(dynamicArgs ?? {}, key) + if (isArgument && visibility !== 'user-only' && visibility !== 'hidden') input[key] = value } return input } diff --git a/apps/sim/sandbox-tasks/workflow-test.ts b/apps/sim/sandbox-tasks/workflow-test.ts index f7dad23c242..9cad58837a3 100644 --- a/apps/sim/sandbox-tasks/workflow-test.ts +++ b/apps/sim/sandbox-tasks/workflow-test.ts @@ -115,7 +115,7 @@ const HARNESS = ` check.status = status; if (error) Object.assign(check, describeError(error)); } - function track(assertion, check) { + function track(assertion, check, test) { return new Proxy(assertion, { get(target, key) { let value; @@ -135,16 +135,18 @@ const HARNESS = ` throw error; } if (result && typeof result.then === 'function') { - return result.then( + const settled = result.then( (resolved) => { settle(check, 'pass'); return resolved; }, (error) => { settle(check, 'fail', error); throw error; } ); + test.pendingAssertions.push(settled.catch(() => {})); + return settled; } settle(check, 'pass'); - return result && typeof result === 'object' && result.__flags ? track(result, check) : result; + return result && typeof result === 'object' && result.__flags ? track(result, check, test) : result; }; } - if (value && typeof value === 'object' && value.__flags) return track(value, check); + if (value && typeof value === 'object' && value.__flags) return track(value, check, test); return value; }, }); @@ -155,7 +157,7 @@ const HARNESS = ` const check = { status: 'pending', ...(callSite(new Error().stack) || {}) }; currentTest.checks.push(check); currentTest.activeCheck = check; - return track(assertion, check); + return track(assertion, check, currentTest); }; Object.setPrototypeOf(expect, baseExpect); expect.assertions = (count) => { @@ -385,7 +387,7 @@ const HARNESS = ` async function runOne(suite, test, onlyNames) { const path = [...suitePath(suite), test.name]; - const result = { path, status: 'pass', durationMs: 0, checks: [], executions: [], logs: [], runIds: [], matchedKeys: new Set(), activeCheck: null, expectedAssertions: null, expectsAssertions: false }; + const result = { path, status: 'pass', durationMs: 0, checks: [], executions: [], logs: [], runIds: [], matchedKeys: new Set(), activeCheck: null, expectedAssertions: null, expectsAssertions: false, pendingAssertions: [] }; if (!willRun(suite, test, onlyNames)) { result.status = 'skip'; return result; @@ -407,6 +409,10 @@ const HARNESS = ` } finally { for (const s of suiteChain(suite).reverse()) await runHooks(s.hooks.afterEach); } + await Promise.all(result.pendingAssertions); + if (result.checks.some((check) => check.status === 'fail')) { + throw new Error('An assertion failed after its test returned; await expect(...).resolves, .rejects, and toMatchRubric'); + } checkAssertionCount(result); const unmatched = [...registrations.values()].filter((r) => r.test === result && result.runIds.length > 0 && !result.matchedKeys.has(r.key)); if (unmatched.length > 0) { From b5ed03ed0b3417d1afd772e0972654c3c5c5e253 Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Thu, 8 Oct 2026 11:25:32 -0700 Subject: [PATCH 07/14] fix(tests): keep test sources out of file tabs, refresh after runs, reopen tests - File-edit tool results mark a non-tab file `fileTab: false`, and the browser skips promoting it. - Idle test pages poll every 15s so runs started elsewhere appear; a Mothership run returns its tests as resource changes. - open_resource accepts test resources through an authorized read. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EHGBgHrtePpi7KMrEfy41R --- .../home/hooks/stream/handle-tool-event.ts | 17 +++++++++------- apps/sim/hooks/queries/workflow-tests.ts | 8 +++++--- .../contracts/mothership-resource-tools.ts | 4 ++-- .../sim/lib/mothership/resources/file-tabs.ts | 20 +++++++++++++++++++ .../mothership/tools/server/open-resource.ts | 10 ++++++++++ .../sim/lib/mothership/tools/server/router.ts | 5 +++-- apps/sim/lib/mothership/tools/server/tests.ts | 5 +++++ .../lib/workflow-tests/application/tests.ts | 1 + apps/sim/lib/workflow-tests/repository.ts | 12 +++++++++-- 9 files changed, 66 insertions(+), 16 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/home/hooks/stream/handle-tool-event.ts b/apps/sim/app/workspace/[workspaceId]/home/hooks/stream/handle-tool-event.ts index 9e8e50663b0..c62b2cb548a 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/hooks/stream/handle-tool-event.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/hooks/stream/handle-tool-event.ts @@ -1,5 +1,6 @@ import { isCurrentBrowserToolName } from '@sim/browser-protocol' import { isTerminalToolName } from '@sim/terminal-protocol' +import { toRecordOrNull } from '@sim/utils/object' import { isDesktopApp } from '@/lib/desktop' import { MothershipStreamV1ToolPhase, @@ -97,12 +98,12 @@ function runToolResultSideEffects(ctx: StreamLoopContext, node: ToolNode, replay invalidateResourceQueries(deps.queryClient, deps.workspaceId, resource.type, resource.id) } - if (!replay && (name === ApplyFileEdit.id || name === PrepareFileEdit.id) && isSuccess) { - const out = output as Record | undefined - const editData = - out && typeof out.data === 'object' && out.data !== null - ? (out.data as Record) - : undefined + const isFileEdit = name === ApplyFileEdit.id || name === PrepareFileEdit.id + const editData = isFileEdit ? toRecordOrNull(toRecordOrNull(output)?.data) : null + /** A file another resource owns (a test's source) never opens as a file tab. */ + const editedFileIsTab = editData?.fileTab !== false + + if (!replay && isFileEdit && isSuccess && editedFileIsTab) { const editedFileId = (typeof editData?.id === 'string' ? editData.id : undefined) ?? deps.previewSessionRef.current?.fileId @@ -139,7 +140,9 @@ function runToolResultSideEffects(ctx: StreamLoopContext, node: ToolNode, replay if (name === PrepareFileEdit.id) { deps.removePreviewSessionImmediate(node.id) } - const fileResource = extractedResources.find((r) => r.type === 'file') + const fileResource = editedFileIsTab + ? extractedResources.find((r) => r.type === 'file') + : undefined if (fileResource) { deps.promoteFileResource(fileResource.id, fileResource.title) deps.onResourceEventRef.current?.( diff --git a/apps/sim/hooks/queries/workflow-tests.ts b/apps/sim/hooks/queries/workflow-tests.ts index d1575165558..a48a826c0f5 100644 --- a/apps/sim/hooks/queries/workflow-tests.ts +++ b/apps/sim/hooks/queries/workflow-tests.ts @@ -11,8 +11,10 @@ import { export type WorkflowTestVersion = 'draft' | 'deployed' const WORKFLOW_TEST_STALE_TIME = 15_000 -/** While a run is in flight; 20 reads a minute stays inside the routes' per-user limit of 30. */ +/** While a run is in flight, so its progress and result appear as they land. */ const WORKFLOW_TEST_RUNNING_REFETCH_INTERVAL = 3_000 +/** Otherwise, so a run started elsewhere (Sim, another person, the API) shows up. */ +const WORKFLOW_TEST_IDLE_REFETCH_INTERVAL = 15_000 export const workflowTestKeys = { all: ['workflow-tests'] as const, @@ -43,7 +45,7 @@ export function useWorkflowTests(workspaceId: string, options?: { enabled?: bool refetchInterval: (query) => query.state.data?.tests.some((test) => test.status === 'running') ? WORKFLOW_TEST_RUNNING_REFETCH_INTERVAL - : false, + : WORKFLOW_TEST_IDLE_REFETCH_INTERVAL, }) } @@ -62,7 +64,7 @@ export function useWorkflowTest(workspaceId: string, name: string) { refetchInterval: (query) => query.state.data?.latestRun?.status === 'running' ? WORKFLOW_TEST_RUNNING_REFETCH_INTERVAL - : false, + : WORKFLOW_TEST_IDLE_REFETCH_INTERVAL, }) } diff --git a/apps/sim/lib/api/contracts/mothership-resource-tools.ts b/apps/sim/lib/api/contracts/mothership-resource-tools.ts index c7187963192..d4224c0724b 100644 --- a/apps/sim/lib/api/contracts/mothership-resource-tools.ts +++ b/apps/sim/lib/api/contracts/mothership-resource-tools.ts @@ -10,7 +10,7 @@ export const openResourceInputSchema = z.strictObject({ resources: z .array( z.strictObject({ - type: z.enum(['workflow', 'table', 'knowledgebase', 'file', 'dashboard', 'log']), + type: z.enum(['workflow', 'table', 'knowledgebase', 'file', 'dashboard', 'log', 'test']), id: z.string().trim().min(1), viewId: z .string() @@ -26,7 +26,7 @@ export const openResourceInputSchema = z.strictObject({ export const openResourceOutputSchema = z.object({ resources: z.array( z.object({ - type: z.enum(['workflow', 'table', 'knowledgebase', 'file', 'dashboard', 'log']), + type: z.enum(['workflow', 'table', 'knowledgebase', 'file', 'dashboard', 'log', 'test']), id: z.string(), title: z.string(), viewId: z.string().optional(), diff --git a/apps/sim/lib/mothership/resources/file-tabs.ts b/apps/sim/lib/mothership/resources/file-tabs.ts index e7f54a81d01..e5ec9c1b636 100644 --- a/apps/sim/lib/mothership/resources/file-tabs.ts +++ b/apps/sim/lib/mothership/resources/file-tabs.ts @@ -1,6 +1,8 @@ import { db } from '@sim/db' import { workspaceFiles } from '@sim/db/schema' +import { toStringOrNull } from '@sim/utils/coerce' import { and, inArray, notInArray } from 'drizzle-orm' +import type { BaseServerTool } from '@/lib/mothership/tools/server/base-tool' /** Row contexts that open as a file tab: workspace files and chat uploads. */ const FILE_TAB_CONTEXTS = ['workspace', 'mothership'] @@ -23,3 +25,21 @@ export async function findNonTabFileIds(fileIds: string[]): Promise> ) return new Set(rows.map((row) => row.id)) } + +/** + * Marks a file-editing tool's result `fileTab: false` when the file it touched is not a file tab, + * so the browser, which promotes edited files to tabs from the result, leaves it alone. + */ +export function withFileTabFlag }>( + tool: BaseServerTool +): BaseServerTool { + return { + ...tool, + async execute(args, context) { + const result = await tool.execute(args, context) + const id = toStringOrNull(result.data?.id) + if (id === null || !(await findNonTabFileIds([id])).has(id)) return result + return { ...result, data: { ...result.data, fileTab: false } } + }, + } +} diff --git a/apps/sim/lib/mothership/tools/server/open-resource.ts b/apps/sim/lib/mothership/tools/server/open-resource.ts index ad622bc329b..07015373de3 100644 --- a/apps/sim/lib/mothership/tools/server/open-resource.ts +++ b/apps/sim/lib/mothership/tools/server/open-resource.ts @@ -15,6 +15,7 @@ import { executeDashboardUseCase } from '@/lib/mothership/application/execute-da import { executeCopilotFileUseCase } from '@/lib/mothership/application/execute-file-use-case' import { executeCopilotKnowledgeUseCase } from '@/lib/mothership/application/execute-knowledge-use-case' import { executeCopilotTableUseCase } from '@/lib/mothership/application/execute-table-use-case' +import { executeWorkflowTestUseCase } from '@/lib/mothership/application/execute-workflow-test-use-case' import { executeCopilotWorkflowUseCase } from '@/lib/mothership/application/execute-workflow-use-case' import { COPILOT_APPLICATION_DELEGATION_TTL_MS, @@ -26,6 +27,7 @@ import { } from '@/lib/mothership/tools/server/base-tool' import { readTableDefinitionUseCase } from '@/lib/table/application/tables' import { readTableViewUseCase } from '@/lib/table/application/views' +import { getWorkflowTestDetail } from '@/lib/workflow-tests/application/tests' import { readWorkflowMetadata } from '@/lib/workflows/application/read-workflow' import { readWorkspaceFileMetadata } from '@/lib/workspace-files/application/read-workspace-file-metadata' @@ -96,6 +98,14 @@ export const openResourceServerTool: BaseServerTool = { [readDocumentServerTool.name]: readDocumentServerTool, // The streamed file-writing pair: prepare opens the write (live preview), // apply continues it. The preview machinery keys off these exact names. - [workspaceFileServerTool.name]: workspaceFileServerTool, - [editContentServerTool.name]: editContentServerTool, + [workspaceFileServerTool.name]: withFileTabFlag(workspaceFileServerTool), + [editContentServerTool.name]: withFileTabFlag(editContentServerTool), [generateImageServerTool.name]: generateImageServerTool, [generateVideoServerTool.name]: generateVideoServerTool, [generateAudioServerTool.name]: generateAudioServerTool, diff --git a/apps/sim/lib/mothership/tools/server/tests.ts b/apps/sim/lib/mothership/tools/server/tests.ts index 5b066ae2105..e0e81eac0d8 100644 --- a/apps/sim/lib/mothership/tools/server/tests.ts +++ b/apps/sim/lib/mothership/tools/server/tests.ts @@ -143,7 +143,12 @@ export const testsServerTool: BaseServerTool = { } ) if (current.every((run) => run.status !== 'running')) { + const resources: ResourceChange[] = current.map((run) => ({ + op: 'upsert', + resource: { type: 'test', workspaceId, id: run.name, title: run.title }, + })) return { + resources, version: input.version, results: current.map((run) => ({ name: run.name, diff --git a/apps/sim/lib/workflow-tests/application/tests.ts b/apps/sim/lib/workflow-tests/application/tests.ts index 038ecabb4a2..71815160ca4 100644 --- a/apps/sim/lib/workflow-tests/application/tests.ts +++ b/apps/sim/lib/workflow-tests/application/tests.ts @@ -390,6 +390,7 @@ export const readWorkflowTestRuns = defineAuthorizedWorkspaceUseCase({ runs: rows.map((row) => ({ ...presentRun(row, row.testSourceHash, facts), name: row.testName, + title: row.testTitle, report: row.report as TestReport | null, })), } diff --git a/apps/sim/lib/workflow-tests/repository.ts b/apps/sim/lib/workflow-tests/repository.ts index 5ea1d280954..4780f1c31db 100644 --- a/apps/sim/lib/workflow-tests/repository.ts +++ b/apps/sim/lib/workflow-tests/repository.ts @@ -437,16 +437,24 @@ export async function getWorkflowTestRunById( export async function listWorkflowTestRunsById( runIds: string[], workspaceId: string -): Promise> { +): Promise< + Array +> { if (runIds.length === 0) return [] const rows = await db .select({ run: workflowTestRun, testName: workflowTest.name, + testTitle: workflowTest.title, testSourceHash: workflowTest.sourceHash, }) .from(workflowTestRun) .innerJoin(workflowTest, eq(workflowTest.id, workflowTestRun.testId)) .where(and(inArray(workflowTestRun.id, runIds), eq(workflowTestRun.workspaceId, workspaceId))) - return rows.map(({ run, testName, testSourceHash }) => ({ ...run, testName, testSourceHash })) + return rows.map(({ run, testName, testTitle, testSourceHash }) => ({ + ...run, + testName, + testTitle, + testSourceHash, + })) } From e942dd4fb99f4b0efbd6b3e5d16308b5d8a8aa39 Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Thu, 8 Oct 2026 11:35:47 -0700 Subject: [PATCH 08/14] fix(tests): refresh test tabs after a run, keep saved edits successful - A finished Mothership run refreshes its tests instead of upserting tabs, so a test deleted mid-run does not come back. - A failed file-tab lookup after a saved edit opens no tab instead of reporting the edit as failed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EHGBgHrtePpi7KMrEfy41R --- apps/sim/lib/mothership/resources/file-tabs.ts | 15 +++++++++++++-- apps/sim/lib/mothership/tools/server/tests.ts | 4 ++-- apps/sim/lib/workflow-tests/application/tests.ts | 1 - apps/sim/lib/workflow-tests/repository.ts | 12 ++---------- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/apps/sim/lib/mothership/resources/file-tabs.ts b/apps/sim/lib/mothership/resources/file-tabs.ts index e5ec9c1b636..73cbff282c0 100644 --- a/apps/sim/lib/mothership/resources/file-tabs.ts +++ b/apps/sim/lib/mothership/resources/file-tabs.ts @@ -1,9 +1,12 @@ import { db } from '@sim/db' import { workspaceFiles } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { toStringOrNull } from '@sim/utils/coerce' import { and, inArray, notInArray } from 'drizzle-orm' import type { BaseServerTool } from '@/lib/mothership/tools/server/base-tool' +const logger = createLogger('FileTabs') + /** Row contexts that open as a file tab: workspace files and chat uploads. */ const FILE_TAB_CONTEXTS = ['workspace', 'mothership'] @@ -38,8 +41,16 @@ export function withFileTabFlag run.status !== 'running')) { const resources: ResourceChange[] = current.map((run) => ({ - op: 'upsert', - resource: { type: 'test', workspaceId, id: run.name, title: run.title }, + op: 'refresh', + resource: { type: 'test', id: run.name }, })) return { resources, diff --git a/apps/sim/lib/workflow-tests/application/tests.ts b/apps/sim/lib/workflow-tests/application/tests.ts index 71815160ca4..038ecabb4a2 100644 --- a/apps/sim/lib/workflow-tests/application/tests.ts +++ b/apps/sim/lib/workflow-tests/application/tests.ts @@ -390,7 +390,6 @@ export const readWorkflowTestRuns = defineAuthorizedWorkspaceUseCase({ runs: rows.map((row) => ({ ...presentRun(row, row.testSourceHash, facts), name: row.testName, - title: row.testTitle, report: row.report as TestReport | null, })), } diff --git a/apps/sim/lib/workflow-tests/repository.ts b/apps/sim/lib/workflow-tests/repository.ts index 4780f1c31db..5ea1d280954 100644 --- a/apps/sim/lib/workflow-tests/repository.ts +++ b/apps/sim/lib/workflow-tests/repository.ts @@ -437,24 +437,16 @@ export async function getWorkflowTestRunById( export async function listWorkflowTestRunsById( runIds: string[], workspaceId: string -): Promise< - Array -> { +): Promise> { if (runIds.length === 0) return [] const rows = await db .select({ run: workflowTestRun, testName: workflowTest.name, - testTitle: workflowTest.title, testSourceHash: workflowTest.sourceHash, }) .from(workflowTestRun) .innerJoin(workflowTest, eq(workflowTest.id, workflowTestRun.testId)) .where(and(inArray(workflowTestRun.id, runIds), eq(workflowTestRun.workspaceId, workspaceId))) - return rows.map(({ run, testName, testTitle, testSourceHash }) => ({ - ...run, - testName, - testTitle, - testSourceHash, - })) + return rows.map(({ run, testName, testSourceHash }) => ({ ...run, testName, testSourceHash })) } From 825a1ad36401ac67668f69720c4dd12353f74cd2 Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Thu, 8 Oct 2026 11:41:12 -0700 Subject: [PATCH 09/14] fix(tests): starter source imports every test helper Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EHGBgHrtePpi7KMrEfy41R --- apps/sim/lib/workflow-tests/definition.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/sim/lib/workflow-tests/definition.ts b/apps/sim/lib/workflow-tests/definition.ts index 1b51032b905..4996d97ce12 100644 --- a/apps/sim/lib/workflow-tests/definition.ts +++ b/apps/sim/lib/workflow-tests/definition.ts @@ -59,8 +59,8 @@ export function testSourceHash(source: string): string { /** The source `tests create` starts a test file with: one empty describe, ready for cases. */ export function starterTestSource(title: string): string { return [ - "import { describe, expect, it } from 'vitest'", - "import { mockBlock, runWorkflow, spyOnBlock } from 'sim:test'", + "import { describe, expect, it, vi } from 'vitest'", + "import { mockBlock, mockTool, runWorkflow, spyOnBlock } from 'sim:test'", '', `describe(${JSON.stringify(title)}, () => {})`, '', From 1feb7328156058dea5cd8c96c820595b9592770b Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Thu, 8 Oct 2026 12:24:47 -0700 Subject: [PATCH 10/14] build(tests): add @vitest/expect and @vitest/spy for the sandbox bundle The vitest-expect sandbox bundle builds from these packages; rebuilt with the Reflect-based event polyfills. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EHGBgHrtePpi7KMrEfy41R --- .../sandbox/bundles/vitest-expect.cjs | 114 +++++++++--------- apps/sim/package.json | 2 + bun.lock | 22 ++-- 3 files changed, 72 insertions(+), 66 deletions(-) diff --git a/apps/sim/lib/execution/sandbox/bundles/vitest-expect.cjs b/apps/sim/lib/execution/sandbox/bundles/vitest-expect.cjs index 89feed3133c..25215c0afe2 100644 --- a/apps/sim/lib/execution/sandbox/bundles/vitest-expect.cjs +++ b/apps/sim/lib/execution/sandbox/bundles/vitest-expect.cjs @@ -1,90 +1,90 @@ // sandbox bundle: vitest-expect // generated by apps/sim/lib/execution/sandbox/bundles/build.ts // do not edit by hand. run `bun run build:sandbox-bundles` to regenerate. -(()=>{var g7=Object.create;var{getPrototypeOf:m7,defineProperty:m9,getOwnPropertyNames:d7}=Object;var c7=Object.prototype.hasOwnProperty;function l7(Z){return this[Z]}var p7,i7,n7=(Z,$,Q)=>{var X=Z!=null&&typeof Z==="object";if(X){var J=$?p7??=new WeakMap:i7??=new WeakMap,W=J.get(Z);if(W)return W}Q=Z!=null?g7(m7(Z)):{};let G=$||!Z||!Z.__esModule?m9(Q,"default",{value:Z,enumerable:!0}):Q;for(let Y of d7(Z))if(!c7.call(G,Y))m9(G,Y,{get:l7.bind(Z,Y),enumerable:!0});if(X)J.set(Z,G);return G};var o7=(Z,$)=>()=>($||Z(($={exports:{}}).exports,$),$.exports);var d9=o7(()=>{var z0=globalThis;if(typeof z0.Event>"u")z0.Event=class{type;constructor($){this.type=$}};if(typeof z0.EventTarget>"u")z0.EventTarget=class{#Z=new Map;addEventListener($,Q){let X=this.#Z.get($)??new Set;X.add(Q),this.#Z.set($,X)}removeEventListener($,Q){this.#Z.get($)?.delete(Q)}dispatchEvent($){for(let Q of this.#Z.get($.type)??[])Q($);return!0}}});var U0=globalThis;if(typeof U0.global>"u")U0.global=globalThis;if(typeof U0.__require>"u")U0.__require=(Z)=>{throw Error(`Dynamic require of "${Z}" is not supported in the sandbox`)};var eX=n7(d9(),1);var Z5=(Z)=>String(Z);Z5.open=Z5.close="";var p1=()=>Z5,l="\x1B[39m",p="\x1B[49m",r7=(Z,$,Q,X,J)=>{let W="",G=0;do W+=Z.substring(G,X)+Q,G=X+J,X=Z.indexOf($,G);while(~X);return W+Z.substring(G)},l9=(Z,$,Q=Z,X=Z.length)=>{let J=$.length,W=(G)=>{let Y=G+"",z=Y.indexOf($,X);return~z?Z+r7(Y,$,Q,z,J)+$:Z+Y+$};return W.open=Z,W.close=$,W},K0=(Z,$)=>l9(Z,$,Z,5),c9=(Z)=>{let $=Z.charCodeAt(0)===35?Z.slice(1):Z,Q=parseInt($,16)||0;return $.length<6?`${(Q>>8&15)*17};${(Q>>4&15)*17};${(Q&15)*17}`:`${Q>>16&255};${Q>>8&255};${Q&255}`};function p9(Z){let $=Z?l9:p1;return{isColorSupported:Z,reset:$("\x1B[0m","\x1B[0m"),bold:$("\x1B[1m","\x1B[22m","\x1B[22m\x1B[1m"),dim:$("\x1B[2m","\x1B[22m","\x1B[22m\x1B[2m"),italic:$("\x1B[3m","\x1B[23m"),underline:$("\x1B[4m","\x1B[24m"),inverse:$("\x1B[7m","\x1B[27m"),hidden:$("\x1B[8m","\x1B[28m"),strikethrough:$("\x1B[9m","\x1B[29m"),black:$("\x1B[30m",l),red:$("\x1B[31m",l),green:$("\x1B[32m",l),yellow:$("\x1B[33m",l),blue:$("\x1B[34m",l),magenta:$("\x1B[35m",l),cyan:$("\x1B[36m",l),white:$("\x1B[37m",l),gray:$("\x1B[90m",l),bgBlack:$("\x1B[40m",p),bgRed:$("\x1B[41m",p),bgGreen:$("\x1B[42m",p),bgYellow:$("\x1B[43m",p),bgBlue:$("\x1B[44m",p),bgMagenta:$("\x1B[45m",p),bgCyan:$("\x1B[46m",p),bgWhite:$("\x1B[47m",p),blackBright:$("\x1B[90m",l),redBright:$("\x1B[91m",l),greenBright:$("\x1B[92m",l),yellowBright:$("\x1B[93m",l),blueBright:$("\x1B[94m",l),magentaBright:$("\x1B[95m",l),cyanBright:$("\x1B[96m",l),whiteBright:$("\x1B[97m",l),bgBlackBright:$("\x1B[100m",p),bgRedBright:$("\x1B[101m",p),bgGreenBright:$("\x1B[102m",p),bgYellowBright:$("\x1B[103m",p),bgBlueBright:$("\x1B[104m",p),bgMagentaBright:$("\x1B[105m",p),bgCyanBright:$("\x1B[106m",p),bgWhiteBright:$("\x1B[107m",p),rgb:Z?(Q,X,J)=>K0(`\x1B[38;2;${Q};${X};${J}m`,l):p1,bgRgb:Z?(Q,X,J)=>K0(`\x1B[48;2;${Q};${X};${J}m`,p):p1,hex:Z?(Q)=>K0(`\x1B[38;2;${c9(Q)}m`,l):p1,bgHex:Z?(Q)=>K0(`\x1B[48;2;${c9(Q)}m`,p):p1}}function a7(){return p9(!1)}function s7(){let Z=typeof process<"u"?process:void 0,$=(Z==null?void 0:Z.env)||{},Q=$.FORCE_TTY!=="false",X=(Z==null?void 0:Z.argv)||[];return!(("NO_COLOR"in $)||X.includes("--no-color"))&&(("FORCE_COLOR"in $)||X.includes("--color")||(Z==null?void 0:Z.platform)==="win32"||Q&&$.TERM!=="dumb"||("CI"in $))||typeof window<"u"&&!!window.chrome}function t7({force:Z}={}){let $=Z||s7();return p9($)}var i9=t7();function n9(){Object.assign(i9,a7())}var f=i9;var D5=Object.defineProperty,e7=(Z,$,Q)=>($ in Z)?D5(Z,$,{enumerable:!0,configurable:!0,writable:!0,value:Q}):Z[$]=Q,F=(Z,$)=>D5(Z,"name",{value:$,configurable:!0}),R5=(Z,$)=>{for(var Q in $)D5(Z,Q,{get:$[Q],enumerable:!0})},X8=(Z,$,Q)=>e7(Z,typeof $!=="symbol"?$+"":$,Q),K1={};R5(K1,{addChainableMethod:()=>E5,addLengthGuard:()=>s1,addMethod:()=>S5,addProperty:()=>k5,checkError:()=>J1,compareByInspect:()=>q0,eql:()=>j8,events:()=>M0,expectTypes:()=>K8,flag:()=>k,getActual:()=>R0,getMessage:()=>O5,getName:()=>O0,getOperator:()=>y5,getOwnEnumerableProperties:()=>h5,getOwnEnumerablePropertySymbols:()=>v5,getPathInfo:()=>T5,hasProperty:()=>F0,inspect:()=>_,isNaN:()=>H0,isNumeric:()=>i,isProxyEnabled:()=>a1,isRegExp:()=>P0,objDisplay:()=>_1,overwriteChainableMethod:()=>x5,overwriteMethod:()=>j5,overwriteProperty:()=>_5,proxify:()=>u1,test:()=>F5,transferFlags:()=>V1,type:()=>h});var J1={};R5(J1,{compatibleConstructor:()=>G8,compatibleInstance:()=>W8,compatibleMessage:()=>B8,getConstructorName:()=>Y8,getMessage:()=>U8});function D0(Z){return Z instanceof Error||Object.prototype.toString.call(Z)==="[object Error]"}F(D0,"isErrorInstance");function J8(Z){return Object.prototype.toString.call(Z)==="[object RegExp]"}F(J8,"isRegExp");function W8(Z,$){return D0($)&&Z===$}F(W8,"compatibleInstance");function G8(Z,$){if(D0($))return Z.constructor===$.constructor||Z instanceof $.constructor;else if((typeof $==="object"||typeof $==="function")&&$.prototype)return Z.constructor===$||Z instanceof $;return!1}F(G8,"compatibleConstructor");function B8(Z,$){let Q=typeof Z==="string"?Z:Z.message;if(Q===void 0)return!1;if(J8($))return $.test(Q);else if(typeof $==="string")return Q.indexOf($)!==-1;return!1}F(B8,"compatibleMessage");function Y8(Z){let $=Z;if(D0(Z))$=Z.constructor.name;else if(typeof Z==="function"){if($=Z.name,$==="")$=new Z().name||$}return $}F(Y8,"getConstructorName");function U8(Z){let $="";if(Z&&Z.message)$=Z.message;else if(typeof Z==="string")$=Z;return $}F(U8,"getMessage");function k(Z,$,Q){let X=Z.__flags||(Z.__flags=Object.create(null));if(arguments.length===3)X[$]=Q;else return X[$]}F(k,"flag");function F5(Z,$){let Q=k(Z,"negate"),X=$[0];return Q?!X:X}F(F5,"test");function h(Z){if(typeof Z>"u")return"undefined";if(Z===null)return"null";let $=Z[Symbol.toStringTag];if(typeof $==="string")return $;return Object.prototype.toString.call(Z).slice(8,-1)}F(h,"type");var ZZ="captureStackTrace"in Error,z8=class Z extends Error{constructor($="Unspecified AssertionError",Q,X){super($);if(X8(this,"message"),this.message=$,ZZ)Error.captureStackTrace(this,X||Z);for(let J in Q)if(!(J in this))this[J]=Q[J]}get name(){return"AssertionError"}get ok(){return!1}toJSON($){return{...this,name:this.name,message:this.message,ok:!1,stack:$!==!1?this.stack:void 0}}};F(z8,"AssertionError");var j=z8;function K8(Z,$){let Q=k(Z,"message"),X=k(Z,"ssfi");Q=Q?Q+": ":"",Z=k(Z,"object"),$=$.map(function(G){return G.toLowerCase()}),$.sort();let J=$.map(function(G,Y){let z=~["a","e","i","o","u"].indexOf(G.charAt(0))?"an":"a";return($.length>1&&Y===$.length-1?"or ":"")+z+" "+G}).join(", "),W=h(Z).toLowerCase();if(!$.some(function(G){return W===G}))throw new j(Q+"object tested must be "+J+", but "+W+" given",void 0,X)}F(K8,"expectTypes");function R0(Z,$){return $.length>4?$[4]:Z._obj}F(R0,"getActual");var o9={bold:["1","22"],dim:["2","22"],italic:["3","23"],underline:["4","24"],inverse:["7","27"],hidden:["8","28"],strike:["9","29"],black:["30","39"],red:["31","39"],green:["32","39"],yellow:["33","39"],blue:["34","39"],magenta:["35","39"],cyan:["36","39"],white:["37","39"],brightblack:["30;1","39"],brightred:["31;1","39"],brightgreen:["32;1","39"],brightyellow:["33;1","39"],brightblue:["34;1","39"],brightmagenta:["35;1","39"],brightcyan:["36;1","39"],brightwhite:["37;1","39"],grey:["90","39"]},$Z={special:"cyan",number:"yellow",bigint:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",symbol:"green",date:"magenta",regexp:"red"},y1="…";function V8(Z,$){let Q=o9[$Z[$]]||o9[$]||"";if(!Q)return String(Z);return`\x1B[${Q[0]}m${String(Z)}\x1B[${Q[1]}m`}F(V8,"colorise");function w8({showHidden:Z=!1,depth:$=2,colors:Q=!1,customInspect:X=!0,showProxy:J=!1,maxArrayLength:W=1/0,breakLength:G=1/0,seen:Y=[],truncate:z=1/0,stylize:B=String}={},U){let K={showHidden:Boolean(Z),depth:Number($),colors:Boolean(Q),customInspect:Boolean(X),showProxy:Boolean(J),maxArrayLength:Number(W),breakLength:Number(G),truncate:Number(z),seen:Y,inspect:U,stylize:B};if(K.colors)K.stylize=V8;return K}F(w8,"normaliseOptions");function q8(Z){return Z>="\uD800"&&Z<="\uDBFF"}F(q8,"isHighSurrogate");function R1(Z,$,Q=y1){Z=String(Z);let X=Q.length,J=Z.length;if(X>$&&J>X)return Q;if(J>$&&J>X){let W=$-X;if(W>0&&q8(Z[W-1]))W=W-1;return`${Z.slice(0,W)}${Q}`}return Z}F(R1,"truncate");function B1(Z,$,Q,X=", "){Q=Q||$.inspect;let J=Z.length;if(J===0)return"";let W=$.truncate,G="",Y="",z="";for(let B=0;BW&&G.length+z.length<=W)break;if(!U&&!K&&H>W)break;if(Y=U?"":Q(Z[B+1],$)+(K?"":X),!U&&K&&H>W&&N+Y.length>W)break;if(G+=w,!U&&!K&&N+Y.length>=W){z=`${y1}(${Z.length-B-1})`;break}z=""}return`${G}${z}`}F(B1,"inspectList");function H8(Z){if(Z.match(/^[a-zA-Z_][a-zA-Z_0-9]*$/))return Z;return JSON.stringify(Z).replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'")}F(H8,"quoteComplexKey");function b1([Z,$],Q){if(Q.truncate-=2,typeof Z==="string")Z=H8(Z);else if(typeof Z!=="number")Z=`[${Q.inspect(Z,Q)}]`;return Q.truncate-=Z.length,$=Q.inspect($,Q),`${Z}: ${$}`}F(b1,"inspectProperty");function P8(Z,$){let Q=Object.keys(Z).slice(Z.length);if(!Z.length&&!Q.length)return"[]";$.truncate-=4;let X=B1(Z,$);$.truncate-=X.length;let J="";if(Q.length)J=B1(Q.map((W)=>[W,Z[W]]),$,b1);return`[ ${X}${J?`, ${J}`:""} ]`}F(P8,"inspectArray");var QZ=F((Z)=>{if(typeof Buffer==="function"&&Z instanceof Buffer)return"Buffer";if(Z[Symbol.toStringTag])return Z[Symbol.toStringTag];return Z.constructor.name},"getArrayName");function P1(Z,$){let Q=QZ(Z);$.truncate-=Q.length+4;let X=Object.keys(Z).slice(Z.length);if(!Z.length&&!X.length)return`${Q}[]`;let J="";for(let G=0;G[G,Z[G]]),$,b1);return`${Q}[ ${J}${W?`, ${W}`:""} ]`}F(P1,"inspectTypedArray");function N8(Z,$){let Q=Z.toJSON();if(Q===null)return"Invalid Date";let X=Q.split("T"),J=X[0];return $.stylize(`${J}T${R1(X[1],$.truncate-J.length-1)}`,"date")}F(N8,"inspectDate");function J5(Z,$){let Q=Z[Symbol.toStringTag]||"Function",X=Z.name;if(!X)return $.stylize(`[${Q}]`,"special");return $.stylize(`[${Q} ${R1(X,$.truncate-11)}]`,"special")}F(J5,"inspectFunction");function D8([Z,$],Q){return Q.truncate-=4,Z=Q.inspect(Z,Q),Q.truncate-=Z.length,$=Q.inspect($,Q),`${Z} => ${$}`}F(D8,"inspectMapEntry");function R8(Z){let $=[];return Z.forEach((Q,X)=>{$.push([X,Q])}),$}F(R8,"mapToEntries");function F8(Z,$){if(Z.size===0)return"Map{}";return $.truncate-=7,`Map{ ${B1(R8(Z),$,D8)} }`}F(F8,"inspectMap");var XZ=Number.isNaN||((Z)=>Z!==Z);function W5(Z,$){if(XZ(Z))return $.stylize("NaN","number");if(Z===1/0)return $.stylize("Infinity","number");if(Z===-1/0)return $.stylize("-Infinity","number");if(Z===0)return $.stylize(1/Z===1/0?"+0":"-0","number");return $.stylize(R1(String(Z),$.truncate),"number")}F(W5,"inspectNumber");function G5(Z,$){let Q=R1(Z.toString(),$.truncate-1);if(Q!==y1)Q+="n";return $.stylize(Q,"bigint")}F(G5,"inspectBigInt");function M8(Z,$){let Q=Z.toString().split("/")[2],X=$.truncate-(2+Q.length),J=Z.source;return $.stylize(`/${R1(J,X)}/${Q}`,"regexp")}F(M8,"inspectRegExp");function O8(Z){let $=[];return Z.forEach((Q)=>{$.push(Q)}),$}F(O8,"arrayFromSet");function I8(Z,$){if(Z.size===0)return"Set{}";return $.truncate-=7,`Set{ ${B1(O8(Z),$)} }`}F(I8,"inspectSet");var r9=new RegExp("['\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]","g"),JZ={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r","'":"\\'","\\":"\\\\"},WZ=16,GZ=4;function C8(Z){return JZ[Z]||`\\u${`0000${Z.charCodeAt(0).toString(WZ)}`.slice(-GZ)}`}F(C8,"escape");function B5(Z,$){if(r9.test(Z))Z=Z.replace(r9,C8);return $.stylize(`'${R1(Z,$.truncate-2)}'`,"string")}F(B5,"inspectString");function Y5(Z){if("description"in Symbol.prototype)return Z.description?`Symbol(${Z.description})`:"Symbol()";return Z.toString()}F(Y5,"inspectSymbol");var BZ=F(()=>"Promise{…}","getPromiseValue"),YZ=BZ;function n1(Z,$){let Q=Object.getOwnPropertyNames(Z),X=Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(Z):[];if(Q.length===0&&X.length===0)return"{}";if($.truncate-=4,$.seen=$.seen||[],$.seen.includes(Z))return"[Circular]";$.seen.push(Z);let J=B1(Q.map((Y)=>[Y,Z[Y]]),$,b1),W=B1(X.map((Y)=>[Y,Z[Y]]),$,b1);$.seen.pop();let G="";if(J&&W)G=", ";return`{ ${J}${G}${W} }`}F(n1,"inspectObject");var $5=typeof Symbol<"u"&&Symbol.toStringTag?Symbol.toStringTag:!1;function L8(Z,$){let Q="";if($5&&$5 in Z)Q=Z[$5];if(Q=Q||Z.constructor.name,!Q||Q==="_class")Q="";return $.truncate-=Q.length,`${Q}${n1(Z,$)}`}F(L8,"inspectClass");function T8(Z,$){if(Z.length===0)return"Arguments[]";return $.truncate-=13,`Arguments[ ${B1(Z,$)} ]`}F(T8,"inspectArguments");var UZ=["stack","line","column","name","message","fileName","lineNumber","columnNumber","number","description","cause"];function A8(Z,$){let Q=Object.getOwnPropertyNames(Z).filter((G)=>UZ.indexOf(G)===-1),X=Z.name;$.truncate-=X.length;let J="";if(typeof Z.message==="string")J=R1(Z.message,$.truncate);else Q.unshift("message");if(J=J?`: ${J}`:"",$.truncate-=J.length+5,$.seen=$.seen||[],$.seen.includes(Z))return"[Circular]";$.seen.push(Z);let W=B1(Q.map((G)=>[G,Z[G]]),$,b1);return`${X}${J}${W?` { ${W} }`:""}`}F(A8,"inspectObject");function k8([Z,$],Q){if(Q.truncate-=3,!$)return`${Q.stylize(String(Z),"yellow")}`;return`${Q.stylize(String(Z),"yellow")}=${Q.stylize(`"${$}"`,"string")}`}F(k8,"inspectAttribute");function V0(Z,$){return B1(Z,$,S8,` -`)}F(V0,"inspectNodeCollection");function S8(Z,$){switch(Z.nodeType){case 1:return M5(Z,$);case 3:return $.inspect(Z.data,$);default:return $.inspect(Z,$)}}F(S8,"inspectNode");function M5(Z,$){let Q=Z.getAttributeNames(),X=Z.tagName.toLowerCase(),J=$.stylize(`<${X}`,"special"),W=$.stylize(">","special"),G=$.stylize(``,"special");$.truncate-=X.length*2+5;let Y="";if(Q.length>0)Y+=" ",Y+=B1(Q.map((U)=>[U,Z.getAttribute(U)]),$,k8," ");$.truncate-=Y.length;let z=$.truncate,B=V0(Z.children,$);if(B&&B.length>z)B=`${y1}(${Z.children.length})`;return`${J}${Y}${W}${B}${G}`}F(M5,"inspectHTML");var zZ=typeof Symbol==="function"&&typeof Symbol.for==="function",Q5=zZ?Symbol.for("chai/inspect"):"@@chai/inspect",X5=Symbol.for("nodejs.util.inspect.custom"),a9=new WeakMap,s9={},t9={undefined:F((Z,$)=>$.stylize("undefined","undefined"),"undefined"),null:F((Z,$)=>$.stylize("null","null"),"null"),boolean:F((Z,$)=>$.stylize(String(Z),"boolean"),"boolean"),Boolean:F((Z,$)=>$.stylize(String(Z),"boolean"),"Boolean"),number:W5,Number:W5,bigint:G5,BigInt:G5,string:B5,String:B5,function:J5,Function:J5,symbol:Y5,Symbol:Y5,Array:P8,Date:N8,Map:F8,Set:I8,RegExp:M8,Promise:YZ,WeakSet:F((Z,$)=>$.stylize("WeakSet{…}","special"),"WeakSet"),WeakMap:F((Z,$)=>$.stylize("WeakMap{…}","special"),"WeakMap"),Arguments:T8,Int8Array:P1,Uint8Array:P1,Uint8ClampedArray:P1,Int16Array:P1,Uint16Array:P1,Int32Array:P1,Uint32Array:P1,Float32Array:P1,Float64Array:P1,Generator:F(()=>"","Generator"),DataView:F(()=>"","DataView"),ArrayBuffer:F(()=>"","ArrayBuffer"),Error:A8,HTMLCollection:V0,NodeList:V0},KZ=F((Z,$,Q,X)=>{if(Q5 in Z&&typeof Z[Q5]==="function")return Z[Q5]($);if(X5 in Z&&typeof Z[X5]==="function")return Z[X5]($.depth,$,X);if("inspect"in Z&&typeof Z.inspect==="function")return Z.inspect($.depth,$);if("constructor"in Z&&a9.has(Z.constructor))return a9.get(Z.constructor)(Z,$);if(s9[Q])return s9[Q](Z,$);return""},"inspectCustom"),VZ=Object.prototype.toString;function o1(Z,$={}){let Q=w8($,o1),{customInspect:X}=Q,J=Z===null?"null":typeof Z;if(J==="object")J=VZ.call(Z).slice(8,-1);if(J in t9)return t9[J](Z,Q);if(X&&Z){let G=KZ(Z,Q,J,o1);if(G){if(typeof G==="string")return G;return o1(G,Q)}}let W=Z?Object.getPrototypeOf(Z):!1;if(W===Object.prototype||W===null)return n1(Z,Q);if(Z&&typeof HTMLElement==="function"&&Z instanceof HTMLElement)return M5(Z,Q);if("constructor"in Z){if(Z.constructor!==Object)return L8(Z,Q);return n1(Z,Q)}if(Z===Object(Z))return n1(Z,Q);return Q.stylize(String(Z),J)}F(o1,"inspect");var $1={includeStack:!1,showDiff:!0,truncateThreshold:40,useProxy:!0,proxyExcludedKeys:["then","catch","inspect","toJSON"],deepEqual:null};function _(Z,$,Q,X){let J={colors:X,depth:typeof Q>"u"?2:Q,showHidden:$,truncate:$1.truncateThreshold?$1.truncateThreshold:1/0};return o1(Z,J)}F(_,"inspect");function _1(Z){let $=_(Z),Q=Object.prototype.toString.call(Z);if($1.truncateThreshold&&$.length>=$1.truncateThreshold)if(Q==="[object Function]"){let X=Z;return!X.name||X.name===""?"[Function]":"[Function: "+X.name+"]"}else if(Q==="[object Array]")return"[ Array("+Z.length+") ]";else if(Q==="[object Object]"){let J=Object.keys(Z);return"{ Object ("+(J.length>2?J.splice(0,2).join(", ")+", ...":J.join(", "))+") }"}else return $;else return $}F(_1,"objDisplay");function O5(Z,$){let Q=k(Z,"negate"),X=k(Z,"object"),J=$[3],W=R0(Z,$),G=Q?$[2]:$[1],Y=k(Z,"message");if(typeof G==="function")G=G();let B=(G||"").replace(/#\{(this|act|exp)\}/g,function(U,K){if(K==="this")return _1(X);if(K==="act")return _1(W);return _1(J)});return Y?Y+": "+B:B}F(O5,"getMessage");function V1(Z,$,Q){let X=Z,J=$,W=X.__flags||(X.__flags=Object.create(null)),G=J.__flags||(J.__flags=Object.create(null));Q=arguments.length===3?Q:!0;for(let Y in W)if(Q||Y!=="object"&&Y!=="ssfi"&&Y!=="lockSsfi"&&Y!="message")G[Y]=W[Y]}F(V1,"transferFlags");function U5(Z){if(typeof Z>"u")return"undefined";if(Z===null)return"null";let $=Z[Symbol.toStringTag];if(typeof $==="string")return $;let Q=8,X=-1;return Object.prototype.toString.call(Z).slice(Q,X)}F(U5,"type");function I5(){this._key="chai/deep-eql__"+Math.random()+Date.now()}F(I5,"FakeMap");I5.prototype={get:F(function($){return $[this._key]},"get"),set:F(function($,Q){if(Object.isExtensible($))Object.defineProperty($,this._key,{value:Q,configurable:!0})},"set")};var _8=typeof WeakMap==="function"?WeakMap:I5;function z5(Z,$,Q){if(!Q||j1(Z)||j1($))return null;var X=Q.get(Z);if(X){var J=X.get($);if(typeof J==="boolean")return J}return null}F(z5,"memoizeCompare");function i1(Z,$,Q,X){if(!Q||j1(Z)||j1($))return;var J=Q.get(Z);if(J)J.set($,X);else J=new _8,J.set($,X),Q.set(Z,J)}F(i1,"memoizeSet");var j8=r1;function r1(Z,$,Q){if(Q&&Q.comparator)return K5(Z,$,Q);var X=C5(Z,$);if(X!==null)return X;return K5(Z,$,Q)}F(r1,"deepEqual");function C5(Z,$){if(Z===$)return Z!==0||1/Z===1/$;if(Z!==Z&&$!==$)return!0;if(j1(Z)||j1($))return!1;return null}F(C5,"simpleEqual");function K5(Z,$,Q){Q=Q||{},Q.memoize=Q.memoize===!1?!1:Q.memoize||new _8;var X=Q&&Q.comparator,J=z5(Z,$,Q.memoize);if(J!==null)return J;var W=z5($,Z,Q.memoize);if(W!==null)return W;if(X){var G=X(Z,$);if(G===!1||G===!0)return i1(Z,$,Q.memoize,G),G;var Y=C5(Z,$);if(Y!==null)return Y}var z=U5(Z);if(z!==U5($))return i1(Z,$,Q.memoize,!1),!1;i1(Z,$,Q.memoize,!0);var B=E8(Z,$,z,Q);return i1(Z,$,Q.memoize,B),B}F(K5,"extensiveDeepEqual");function E8(Z,$,Q,X){switch(Q){case"String":case"Number":case"Boolean":case"Date":return r1(Z.valueOf(),$.valueOf());case"Promise":case"Symbol":case"function":case"WeakMap":case"WeakSet":return Z===$;case"Error":return L5(Z,$,["name","message","code"],X);case"Arguments":case"Int8Array":case"Uint8Array":case"Uint8ClampedArray":case"Int16Array":case"Uint16Array":case"Int32Array":case"Uint32Array":case"Float32Array":case"Float64Array":case"Array":return C1(Z,$,X);case"RegExp":return x8(Z,$);case"Generator":return v8(Z,$,X);case"DataView":return C1(new Uint8Array(Z.buffer),new Uint8Array($.buffer),X);case"ArrayBuffer":return C1(new Uint8Array(Z),new Uint8Array($),X);case"Set":return V5(Z,$,X);case"Map":return V5(Z,$,X);case"Temporal.PlainDate":case"Temporal.PlainTime":case"Temporal.PlainDateTime":case"Temporal.Instant":case"Temporal.ZonedDateTime":case"Temporal.PlainYearMonth":case"Temporal.PlainMonthDay":return Z.equals($);case"Temporal.Duration":return Z.total("nanoseconds")===$.total("nanoseconds");case"Temporal.TimeZone":case"Temporal.Calendar":return Z.toString()===$.toString();default:return y8(Z,$,X)}}F(E8,"extensiveDeepEqualByType");function x8(Z,$){return Z.toString()===$.toString()}F(x8,"regexpEqual");function V5(Z,$,Q){try{if(Z.size!==$.size)return!1;if(Z.size===0)return!0}catch(W){return!1}var X=[],J=[];return Z.forEach(F(function(G,Y){X.push([G,Y])},"gatherEntries")),$.forEach(F(function(G,Y){J.push([G,Y])},"gatherEntries")),C1(X.sort(),J.sort(),Q)}F(V5,"entriesEqual");function C1(Z,$,Q){var X=Z.length;if(X!==$.length)return!1;if(X===0)return!0;var J=-1;while(++J"u"||Z===null)return!1;return $ in Object(Z)}F(F0,"hasProperty");function b8(Z){return Z.replace(/([^\\])\[/g,"$1.[").match(/(\\\.|[^.]+?)+/g).map((X)=>{if(X==="constructor"||X==="__proto__"||X==="prototype")return{};let W=/^\[(\d+)\]$/.exec(X),G=null;if(W)G={i:parseFloat(W[1])};else G={p:X.replace(/\\([.[\]])/g,"$1")};return G})}F(b8,"parsePath");function N5(Z,$,Q){let X=Z,J=null;Q=typeof Q>"u"?$.length:Q;for(let W=0;W"u")X=X[G.i];else X=X[G.p];if(W===Q-1)J=X}}return J}F(N5,"internalGetPathValue");function T5(Z,$){let Q=b8($),X=Q[Q.length-1],J={parent:Q.length>1?N5(Z,Q,Q.length-1):Z,name:X.p||X.i,value:N5(Z,Q)};return J.exists=F0(J.parent,J.name),J}F(T5,"getPathInfo");var u8=class Z{constructor($,Q,X,J){return X8(this,"__flags",{}),k(this,"ssfi",X||Z),k(this,"lockSsfi",J),k(this,"object",$),k(this,"message",Q),k(this,"eql",$1.deepEqual||j8),u1(this)}static get includeStack(){return console.warn("Assertion.includeStack is deprecated, use chai.config.includeStack instead."),$1.includeStack}static set includeStack($){console.warn("Assertion.includeStack is deprecated, use chai.config.includeStack instead."),$1.includeStack=$}static get showDiff(){return console.warn("Assertion.showDiff is deprecated, use chai.config.showDiff instead."),$1.showDiff}static set showDiff($){console.warn("Assertion.showDiff is deprecated, use chai.config.showDiff instead."),$1.showDiff=$}static addProperty($,Q){k5(this.prototype,$,Q)}static addMethod($,Q){S5(this.prototype,$,Q)}static addChainableMethod($,Q,X){E5(this.prototype,$,Q,X)}static overwriteProperty($,Q){_5(this.prototype,$,Q)}static overwriteMethod($,Q){j5(this.prototype,$,Q)}static overwriteChainableMethod($,Q,X){x5(this.prototype,$,Q,X)}assert($,Q,X,J,W,G){let Y=F5(this,arguments);if(G!==!1)G=!0;if(J===void 0&&W===void 0)G=!1;if($1.showDiff!==!0)G=!1;if(!Y){Q=O5(this,arguments);let B={actual:R0(this,arguments),expected:J,showDiff:G},U=y5(this,arguments);if(U)B.operator=U;throw new j(Q,B,$1.includeStack?this.assert:k(this,"ssfi"))}}get _obj(){return k(this,"object")}set _obj($){k(this,"object",$)}};F(u8,"Assertion");var q=u8,M0=new EventTarget,f8=class extends Event{constructor($,Q,X){super($);this.name=String(Q),this.fn=X}};F(f8,"PluginEvent");var A5=f8;function a1(){return $1.useProxy&&typeof Proxy<"u"&&typeof Reflect<"u"}F(a1,"isProxyEnabled");function k5(Z,$,Q){Q=Q===void 0?function(){}:Q,Object.defineProperty(Z,$,{get:F(function X(){if(!a1()&&!k(this,"lockSsfi"))k(this,"ssfi",X);let J=Q.call(this);if(J!==void 0)return J;let W=new q;return V1(this,W),W},"propertyGetter"),configurable:!0}),M0.dispatchEvent(new A5("addProperty",$,Q))}F(k5,"addProperty");var wZ=Object.getOwnPropertyDescriptor(function(){},"length");function s1(Z,$,Q){if(!wZ?.configurable)return Z;return Object.defineProperty(Z,"length",{get:F(function(){if(Q)throw Error("Invalid Chai property: "+$+'.length. Due to a compatibility issue, "length" cannot directly follow "'+$+'". Use "'+$+'.lengthOf" instead.');throw Error("Invalid Chai property: "+$+'.length. See docs for proper usage of "'+$+'".')},"get")}),Z}F(s1,"addLengthGuard");function g8(Z){let $=Object.getOwnPropertyNames(Z);function Q(J){if($.indexOf(J)===-1)$.push(J)}F(Q,"addProperty");let X=Object.getPrototypeOf(Z);while(X!==null)Object.getOwnPropertyNames(X).forEach(Q),X=Object.getPrototypeOf(X);return $}F(g8,"getProperties");var e9=["__flags","__methods","_obj","assert"];function u1(Z,$){if(!a1())return Z;return new Proxy(Z,{get:F(function Q(X,J){if(typeof J==="string"&&$1.proxyExcludedKeys.indexOf(J)===-1&&!Reflect.has(X,J)){if($)throw Error("Invalid Chai property: "+String($)+"."+J+'. See docs for proper usage of "'+String($)+'".');let W=null,G=4;if(g8(X).forEach(function(Y){if(!Object.prototype.hasOwnProperty(Y)&&e9.indexOf(Y)===-1){let z=m8(J,Y,G);if(z=Q)return Q;let X=[];for(let J=0;J<=Z.length;J++)X[J]=Array($.length+1).fill(0),X[J][0]=J;for(let J=0;J<$.length;J++)X[0][J]=J;for(let J=1;J<=Z.length;J++){let W=Z.charCodeAt(J-1);for(let G=1;G<=$.length;G++){if(Math.abs(J-G)>=Q){X[J][G]=Q;continue}X[J][G]=Math.min(X[J-1][G]+1,X[J][G-1]+1,X[J-1][G-1]+(W===$.charCodeAt(G-1)?0:1))}}return X[Z.length][$.length]}F(m8,"stringDistanceCapped");function S5(Z,$,Q){let X=F(function(){if(!k(this,"lockSsfi"))k(this,"ssfi",X);let J=Q.apply(this,arguments);if(J!==void 0)return J;let W=new q;return V1(this,W),W},"methodWrapper");s1(X,$,!1),Z[$]=u1(X,$),M0.dispatchEvent(new A5("addMethod",$,Q))}F(S5,"addMethod");function _5(Z,$,Q){let X=Object.getOwnPropertyDescriptor(Z,$),J=F(function(){return},"_super");if(X&&typeof X.get==="function")J=X.get;Object.defineProperty(Z,$,{get:F(function W(){if(!a1()&&!k(this,"lockSsfi"))k(this,"ssfi",W);let G=k(this,"lockSsfi");k(this,"lockSsfi",!0);let Y=Q(J).call(this);if(k(this,"lockSsfi",G),Y!==void 0)return Y;let z=new q;return V1(this,z),z},"overwritingPropertyGetter"),configurable:!0})}F(_5,"overwriteProperty");function j5(Z,$,Q){let X=Z[$],J=F(function(){throw Error(String($)+" is not a function")},"_super");if(X&&typeof X==="function")J=X;function W(){if(!k(this,"lockSsfi"))k(this,"ssfi",W);let G=k(this,"lockSsfi");k(this,"lockSsfi",!0);let Y=Q(J).apply(this,arguments);if(k(this,"lockSsfi",G),Y!==void 0)return Y;let z=new q;return V1(this,z),z}F(W,"overwritingMethodWrapper"),s1(W,String($),!1),Z[$]=u1(W,$)}F(j5,"overwriteMethod");var qZ=typeof Object.setPrototypeOf==="function",Z8=F(function(){},"testFn"),HZ=Object.getOwnPropertyNames(Z8).filter(function(Z){let $=Object.getOwnPropertyDescriptor(Z8,Z);if(typeof $!=="object")return!0;return!$.configurable}),PZ=Function.prototype.call,NZ=Function.prototype.apply,d8=class extends A5{constructor($,Q,X,J){super($,Q,X);this.chainingBehavior=J}};F(d8,"PluginAddChainableMethodEvent");var DZ=d8;function E5(Z,$,Q,X){let J=Z;if(typeof X!=="function")X=F(function(){},"chainingBehavior");let W={method:Q,chainingBehavior:X};if(!J.__methods)J.__methods={};J.__methods[$]=W,Object.defineProperty(Z,$,{get:F(function(){W.chainingBehavior.call(this);function Y(...z){if(!k(this,"lockSsfi"))k(this,"ssfi",Y);let B=W.method.apply(this,z);if(B!==void 0)return B;let U=new q;return V1(this,U),U}if(F(Y,"chainableMethodWrapper"),s1(Y,$,!0),qZ){let z=Object.create(this);z.call=PZ,z.apply=NZ,Object.setPrototypeOf(Y,z)}else Object.getOwnPropertyNames(Z).forEach(function(B){if(HZ.indexOf(B)!==-1)return;let U=Object.getOwnPropertyDescriptor(Z,B);if(U)Object.defineProperty(Y,B,U)});return V1(this,Y),u1(Y)},"chainableMethodGetter"),configurable:!0}),M0.dispatchEvent(new DZ("addChainableMethod",$,Q,X))}F(E5,"addChainableMethod");function x5(Z,$,Q,X){let J=Z.__methods[$],W=J.chainingBehavior;J.chainingBehavior=F(function(){let z=X(W).call(this);if(z!==void 0)return z;let B=new q;return V1(this,B),B},"overwritingChainableMethodGetter");let G=J.method;J.method=F(function(){let z=Q(G).apply(this,arguments);if(z!==void 0)return z;let B=new q;return V1(this,B),B},"overwritingChainableMethodWrapper")}F(x5,"overwriteChainableMethod");function q0(Z,$){return _(Z)<_($)?-1:1}F(q0,"compareByInspect");function v5(Z){if(typeof Object.getOwnPropertySymbols!=="function")return[];return Object.getOwnPropertySymbols(Z).filter(function($){return Object.getOwnPropertyDescriptor(Z,$)?.enumerable})}F(v5,"getOwnEnumerablePropertySymbols");function h5(Z){return Object.keys(Z).concat(v5(Z))}F(h5,"getOwnEnumerableProperties");var H0=Number.isNaN;function c8(Z){let $=h(Z);return["Array","Object","Function"].indexOf($)!==-1}F(c8,"isObjectType");function y5(Z,$){let Q=k(Z,"operator"),X=k(Z,"negate"),J=$[3],W=X?$[2]:$[1];if(Q)return String(Q);if(typeof W==="function")W=W();if(W=W||"",!W)return;let G=String(W);if(/\shave\s/.test(G))return;let Y=c8(J);if(/\snot\s/.test(G))return Y?"notDeepStrictEqual":"notStrictEqual";return Y?"deepStrictEqual":"strictEqual"}F(y5,"getOperator");function O0(Z){return Z.name}F(O0,"getName");function P0(Z){return Object.prototype.toString.call(Z)==="[object RegExp]"}F(P0,"isRegExp");function i(Z){return["Number","BigInt"].includes(h(Z))}F(i,"isNumeric");var{flag:D}=K1;["to","be","been","is","and","has","have","with","that","which","at","of","same","but","does","still","also"].forEach(function(Z){q.addProperty(Z)});q.addProperty("not",function(){D(this,"negate",!0)});q.addProperty("deep",function(){D(this,"deep",!0)});q.addProperty("nested",function(){D(this,"nested",!0)});q.addProperty("own",function(){D(this,"own",!0)});q.addProperty("ordered",function(){D(this,"ordered",!0)});q.addProperty("any",function(){D(this,"any",!0),D(this,"all",!1)});q.addProperty("all",function(){D(this,"all",!0),D(this,"any",!1)});var $8={function:["function","asyncfunction","generatorfunction","asyncgeneratorfunction"],asyncfunction:["asyncfunction","asyncgeneratorfunction"],generatorfunction:["generatorfunction","asyncgeneratorfunction"],asyncgeneratorfunction:["asyncgeneratorfunction"]};function b5(Z,$){if($)D(this,"message",$);Z=Z.toLowerCase();let Q=D(this,"object"),X=~["a","e","i","o","u"].indexOf(Z.charAt(0))?"an ":"a ",J=h(Q).toLowerCase();if($8.function.includes(Z))this.assert($8[Z].includes(J),"expected #{this} to be "+X+Z,"expected #{this} not to be "+X+Z);else this.assert(Z===J,"expected #{this} to be "+X+Z,"expected #{this} not to be "+X+Z)}F(b5,"an");q.addChainableMethod("an",b5);q.addChainableMethod("a",b5);function l8(Z,$){return H0(Z)&&H0($)||Z===$}F(l8,"SameValueZero");function t1(){D(this,"contains",!0)}F(t1,"includeChainingBehavior");function e1(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=h(Q).toLowerCase(),J=D(this,"message"),W=D(this,"negate"),G=D(this,"ssfi"),Y=D(this,"deep"),z=Y?"deep ":"",B=Y?D(this,"eql"):l8;J=J?J+": ":"";let U=!1;switch(X){case"string":U=Q.indexOf(Z)!==-1;break;case"weakset":if(Y)throw new j(J+"unable to use .deep.include with WeakSet",void 0,G);U=Q.has(Z);break;case"map":Q.forEach(function(K){U=U||B(K,Z)});break;case"set":if(Y)Q.forEach(function(K){U=U||B(K,Z)});else U=Q.has(Z);break;case"array":if(Y)U=Q.some(function(K){return B(K,Z)});else U=Q.indexOf(Z)!==-1;break;default:{if(Z!==Object(Z))throw new j(J+"the given combination of arguments ("+X+" and "+h(Z).toLowerCase()+") is invalid for this assertion. You can use an array, a map, an object, a set, a string, or a weakset instead of a "+h(Z).toLowerCase(),void 0,G);let K=Object.keys(Z),P=null,w=0;if(K.forEach(function(N){let H=new q(Q);if(V1(this,H,!0),D(H,"lockSsfi",!0),!W||K.length===1){H.property(N,Z[N]);return}try{H.property(N,Z[N])}catch(R){if(!J1.compatibleConstructor(R,j))throw R;if(P===null)P=R;w++}},this),W&&K.length>1&&w===K.length)throw P;return}}this.assert(U,"expected #{this} to "+z+"include "+_(Z),"expected #{this} to not "+z+"include "+_(Z))}F(e1,"include");q.addChainableMethod("include",e1,t1);q.addChainableMethod("contain",e1,t1);q.addChainableMethod("contains",e1,t1);q.addChainableMethod("includes",e1,t1);q.addProperty("ok",function(){this.assert(D(this,"object"),"expected #{this} to be truthy","expected #{this} to be falsy")});q.addProperty("true",function(){this.assert(D(this,"object")===!0,"expected #{this} to be true","expected #{this} to be false",D(this,"negate")?!1:!0)});q.addProperty("numeric",function(){let Z=D(this,"object");this.assert(["Number","BigInt"].includes(h(Z)),"expected #{this} to be numeric","expected #{this} to not be numeric",D(this,"negate")?!1:!0)});q.addProperty("callable",function(){let Z=D(this,"object"),$=D(this,"ssfi"),Q=D(this,"message"),X=Q?`${Q}: `:"",J=D(this,"negate"),W=J?`${X}expected ${_(Z)} not to be a callable function`:`${X}expected ${_(Z)} to be a callable function`,G=["Function","AsyncFunction","GeneratorFunction","AsyncGeneratorFunction"].includes(h(Z));if(G&&J||!G&&!J)throw new j(W,void 0,$)});q.addProperty("false",function(){this.assert(D(this,"object")===!1,"expected #{this} to be false","expected #{this} to be true",D(this,"negate")?!0:!1)});q.addProperty("null",function(){this.assert(D(this,"object")===null,"expected #{this} to be null","expected #{this} not to be null")});q.addProperty("undefined",function(){this.assert(D(this,"object")===void 0,"expected #{this} to be undefined","expected #{this} not to be undefined")});q.addProperty("NaN",function(){this.assert(H0(D(this,"object")),"expected #{this} to be NaN","expected #{this} not to be NaN")});function u5(){let Z=D(this,"object");this.assert(Z!==null&&Z!==void 0,"expected #{this} to exist","expected #{this} to not exist")}F(u5,"assertExist");q.addProperty("exist",u5);q.addProperty("exists",u5);q.addProperty("empty",function(){let Z=D(this,"object"),$=D(this,"ssfi"),Q=D(this,"message"),X;switch(Q=Q?Q+": ":"",h(Z).toLowerCase()){case"array":case"string":X=Z.length;break;case"map":case"set":X=Z.size;break;case"weakmap":case"weakset":throw new j(Q+".empty was passed a weak collection",void 0,$);case"function":{let J=Q+".empty was passed a function "+O0(Z);throw new j(J.trim(),void 0,$)}default:if(Z!==Object(Z))throw new j(Q+".empty was passed non-string primitive "+_(Z),void 0,$);X=Object.keys(Z).length}this.assert(X===0,"expected #{this} to be empty","expected #{this} not to be empty")});function f5(){let Z=D(this,"object"),$=h(Z);this.assert($==="Arguments","expected #{this} to be arguments but got "+$,"expected #{this} to not be arguments")}F(f5,"checkArguments");q.addProperty("arguments",f5);q.addProperty("Arguments",f5);function I0(Z,$){if($)D(this,"message",$);let Q=D(this,"object");if(D(this,"deep")){let X=D(this,"lockSsfi");D(this,"lockSsfi",!0),this.eql(Z),D(this,"lockSsfi",X)}else this.assert(Z===Q,"expected #{this} to equal #{exp}","expected #{this} to not equal #{exp}",Z,this._obj,!0)}F(I0,"assertEqual");q.addMethod("equal",I0);q.addMethod("equals",I0);q.addMethod("eq",I0);function g5(Z,$){if($)D(this,"message",$);let Q=D(this,"eql");this.assert(Q(Z,D(this,"object")),"expected #{this} to deeply equal #{exp}","expected #{this} to not deeply equal #{exp}",Z,this._obj,!0)}F(g5,"assertEql");q.addMethod("eql",g5);q.addMethod("eqls",g5);function C0(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=D(this,"doLength"),J=D(this,"message"),W=J?J+": ":"",G=D(this,"ssfi"),Y=h(Q).toLowerCase(),z=h(Z).toLowerCase();if(X&&Y!=="map"&&Y!=="set")new q(Q,J,G,!0).to.have.property("length");if(!X&&Y==="date"&&z!=="date")throw new j(W+"the argument to above must be a date",void 0,G);else if(!i(Z)&&(X||i(Q)))throw new j(W+"the argument to above must be a number",void 0,G);else if(!X&&Y!=="date"&&!i(Q)){let B=Y==="string"?"'"+Q+"'":Q;throw new j(W+"expected "+B+" to be a number or a date",void 0,G)}if(X){let B="length",U;if(Y==="map"||Y==="set")B="size",U=Q.size;else U=Q.length;this.assert(U>Z,"expected #{this} to have a "+B+" above #{exp} but got #{act}","expected #{this} to not have a "+B+" above #{exp}",Z,U)}else this.assert(Q>Z,"expected #{this} to be above #{exp}","expected #{this} to be at most #{exp}",Z)}F(C0,"assertAbove");q.addMethod("above",C0);q.addMethod("gt",C0);q.addMethod("greaterThan",C0);function L0(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=D(this,"doLength"),J=D(this,"message"),W=J?J+": ":"",G=D(this,"ssfi"),Y=h(Q).toLowerCase(),z=h(Z).toLowerCase(),B,U=!0;if(X&&Y!=="map"&&Y!=="set")new q(Q,J,G,!0).to.have.property("length");if(!X&&Y==="date"&&z!=="date")B=W+"the argument to least must be a date";else if(!i(Z)&&(X||i(Q)))B=W+"the argument to least must be a number";else if(!X&&Y!=="date"&&!i(Q)){let K=Y==="string"?"'"+Q+"'":Q;B=W+"expected "+K+" to be a number or a date"}else U=!1;if(U)throw new j(B,void 0,G);if(X){let K="length",P;if(Y==="map"||Y==="set")K="size",P=Q.size;else P=Q.length;this.assert(P>=Z,"expected #{this} to have a "+K+" at least #{exp} but got #{act}","expected #{this} to have a "+K+" below #{exp}",Z,P)}else this.assert(Q>=Z,"expected #{this} to be at least #{exp}","expected #{this} to be below #{exp}",Z)}F(L0,"assertLeast");q.addMethod("least",L0);q.addMethod("gte",L0);q.addMethod("greaterThanOrEqual",L0);function T0(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=D(this,"doLength"),J=D(this,"message"),W=J?J+": ":"",G=D(this,"ssfi"),Y=h(Q).toLowerCase(),z=h(Z).toLowerCase(),B,U=!0;if(X&&Y!=="map"&&Y!=="set")new q(Q,J,G,!0).to.have.property("length");if(!X&&Y==="date"&&z!=="date")B=W+"the argument to below must be a date";else if(!i(Z)&&(X||i(Q)))B=W+"the argument to below must be a number";else if(!X&&Y!=="date"&&!i(Q)){let K=Y==="string"?"'"+Q+"'":Q;B=W+"expected "+K+" to be a number or a date"}else U=!1;if(U)throw new j(B,void 0,G);if(X){let K="length",P;if(Y==="map"||Y==="set")K="size",P=Q.size;else P=Q.length;this.assert(P=Z&&H<=$,"expected #{this} to have a "+N+" within "+w,"expected #{this} to not have a "+N+" within "+w)}else this.assert(X>=Z&&X<=$,"expected #{this} to be within "+w,"expected #{this} to not be within "+w)});function m5(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=D(this,"ssfi"),J=D(this,"message"),W;try{W=Q instanceof Z}catch(Y){if(Y instanceof TypeError)throw J=J?J+": ":"",new j(J+"The instanceof assertion needs a constructor but "+h(Z)+" was given.",void 0,X);throw Y}let G=O0(Z);if(G==null)G="an unnamed constructor";this.assert(W,"expected #{this} to be an instance of "+G,"expected #{this} to not be an instance of "+G)}F(m5,"assertInstanceOf");q.addMethod("instanceof",m5);q.addMethod("instanceOf",m5);function d5(Z,$,Q){if(Q)D(this,"message",Q);let X=D(this,"nested"),J=D(this,"own"),W=D(this,"message"),G=D(this,"object"),Y=D(this,"ssfi"),z=typeof Z;if(W=W?W+": ":"",X){if(z!=="string")throw new j(W+"the argument to property must be a string when using nested syntax",void 0,Y)}else if(z!=="string"&&z!=="number"&&z!=="symbol")throw new j(W+"the argument to property must be a string, number, or symbol",void 0,Y);if(X&&J)throw new j(W+'The "nested" and "own" flags cannot be combined.',void 0,Y);if(G===null||G===void 0)throw new j(W+"Target cannot be null or undefined.",void 0,Y);let B=D(this,"deep"),U=D(this,"negate"),K=X?T5(G,Z):null,P=X?K.value:G[Z],w=B?D(this,"eql"):(R,O)=>R===O,N="";if(B)N+="deep ";if(J)N+="own ";if(X)N+="nested ";N+="property ";let H;if(J)H=Object.prototype.hasOwnProperty.call(G,Z);else if(X)H=K.exists;else H=F0(G,Z);if(!U||arguments.length===1)this.assert(H,"expected #{this} to have "+N+_(Z),"expected #{this} to not have "+N+_(Z));if(arguments.length>1)this.assert(H&&w($,P),"expected #{this} to have "+N+_(Z)+" of #{exp}, but got #{act}","expected #{this} to not have "+N+_(Z)+" of #{act}",$,P);D(this,"object",P)}F(d5,"assertProperty");q.addMethod("property",d5);function c5(Z,$,Q){D(this,"own",!0),d5.apply(this,arguments)}F(c5,"assertOwnProperty");q.addMethod("ownProperty",c5);q.addMethod("haveOwnProperty",c5);function l5(Z,$,Q){if(typeof $==="string")Q=$,$=null;if(Q)D(this,"message",Q);let X=D(this,"object"),J=Object.getOwnPropertyDescriptor(Object(X),Z),W=D(this,"eql");if(J&&$)this.assert(W($,J),"expected the own property descriptor for "+_(Z)+" on #{this} to match "+_($)+", got "+_(J),"expected the own property descriptor for "+_(Z)+" on #{this} to not match "+_($),$,J,!0);else this.assert(J,"expected #{this} to have an own property descriptor for "+_(Z),"expected #{this} to not have an own property descriptor for "+_(Z));D(this,"object",J)}F(l5,"assertOwnPropertyDescriptor");q.addMethod("ownPropertyDescriptor",l5);q.addMethod("haveOwnPropertyDescriptor",l5);function p5(){D(this,"doLength",!0)}F(p5,"assertLengthChain");function i5(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=h(Q).toLowerCase(),J=D(this,"message"),W=D(this,"ssfi"),G="length",Y;switch(X){case"map":case"set":G="size",Y=Q.size;break;default:new q(Q,J,W,!0).to.have.property("length"),Y=Q.length}this.assert(Y==Z,"expected #{this} to have a "+G+" of #{exp} but got #{act}","expected #{this} to not have a "+G+" of #{act}",Z,Y)}F(i5,"assertLength");q.addChainableMethod("length",i5,p5);q.addChainableMethod("lengthOf",i5,p5);function n5(Z,$){if($)D(this,"message",$);let Q=D(this,"object");this.assert(Z.exec(Q),"expected #{this} to match "+Z,"expected #{this} not to match "+Z)}F(n5,"assertMatch");q.addMethod("match",n5);q.addMethod("matches",n5);q.addMethod("string",function(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=D(this,"message"),J=D(this,"ssfi");new q(Q,X,J,!0).is.a("string"),this.assert(~Q.indexOf(Z),"expected #{this} to contain "+_(Z),"expected #{this} to not contain "+_(Z))});function o5(Z){let $=D(this,"object"),Q=h($),X=h(Z),J=D(this,"ssfi"),W=D(this,"deep"),G,Y="",z,B=!0,U=D(this,"message");U=U?U+": ":"";let K=U+"when testing keys against an object or an array you must give a single Array|Object|String argument or multiple String arguments";if(Q==="Map"||Q==="Set"){if(Y=W?"deeply ":"",z=[],$.forEach(function(M,T){z.push(T)}),X!=="Array")Z=Array.prototype.slice.call(arguments)}else{switch(z=h5($),X){case"Array":if(arguments.length>1)throw new j(K,void 0,J);break;case"Object":if(arguments.length>1)throw new j(K,void 0,J);Z=Object.keys(Z);break;default:Z=Array.prototype.slice.call(arguments)}Z=Z.map(function(M){return typeof M==="symbol"?M:String(M)})}if(!Z.length)throw new j(U+"keys required",void 0,J);let P=Z.length,w=D(this,"any"),N=D(this,"all"),H=Z,R=W?D(this,"eql"):(M,T)=>M===T;if(!w&&!N)N=!0;let O=[...new Set(H)];if(w)B=O.some(function(M){return z.some(function(T){return R(M,T)})});if(N){if(B=O.every(function(M){return z.some(function(T){return R(M,T)})}),!D(this,"contains"))B=B&&O.length==z.length}if(P>1){Z=Z.map(function(T){return _(T)});let M=Z.pop();if(N)G=Z.join(", ")+", and "+M;if(w)G=Z.join(", ")+", or "+M}else G=_(Z[0]);G=(P>1?"keys ":"key ")+G,G=(D(this,"contains")?"contain ":"have ")+G,this.assert(B,"expected #{this} to "+Y+G,"expected #{this} to not "+Y+G,H.slice(0).sort(q0),z.sort(q0),!0)}F(o5,"assertKeys");q.addMethod("keys",o5);q.addMethod("key",o5);function k0(Z,$,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),J=D(this,"ssfi"),W=D(this,"message"),G=D(this,"negate")||!1;if(new q(X,W,J,!0).is.a("function"),P0(Z)||typeof Z==="string")$=Z,Z=null;let Y,z=!1;try{X()}catch(w){z=!0,Y=w}let B=Z===void 0&&$===void 0,U=Boolean(Z&&$),K=!1,P=!1;if(B||!B&&!G){let w="an error";if(Z instanceof Error)w="#{exp}";else if(Z)w=J1.getConstructorName(Z);let N=Y;if(Y instanceof Error)N=Y.toString();else if(typeof Y==="string")N=Y;else if(Y&&(typeof Y==="object"||typeof Y==="function"))try{N=J1.getConstructorName(Y)}catch(H){}this.assert(z,"expected #{this} to throw "+w,"expected #{this} to not throw an error but #{act} was thrown",Z&&Z.toString(),N)}if(Z&&Y){if(Z instanceof Error){if(J1.compatibleInstance(Y,Z)===G)if(U&&G)K=!0;else this.assert(G,"expected #{this} to throw #{exp} but #{act} was thrown","expected #{this} to not throw #{exp}"+(Y&&!G?" but #{act} was thrown":""),Z.toString(),Y.toString())}if(J1.compatibleConstructor(Y,Z)===G)if(U&&G)K=!0;else this.assert(G,"expected #{this} to throw #{exp} but #{act} was thrown","expected #{this} to not throw #{exp}"+(Y?" but #{act} was thrown":""),Z instanceof Error?Z.toString():Z&&J1.getConstructorName(Z),Y instanceof Error?Y.toString():Y&&J1.getConstructorName(Y))}if(Y&&$!==void 0&&$!==null){let w="including";if(P0($))w="matching";if(J1.compatibleMessage(Y,$)===G)if(U&&G)P=!0;else this.assert(G,"expected #{this} to throw error "+w+" #{exp} but got #{act}","expected #{this} to throw error not "+w+" #{exp}",$,J1.getMessage(Y))}if(K&&P)this.assert(G,"expected #{this} to throw #{exp} but #{act} was thrown","expected #{this} to not throw #{exp}"+(Y?" but #{act} was thrown":""),Z instanceof Error?Z.toString():Z&&J1.getConstructorName(Z),Y instanceof Error?Y.toString():Y&&J1.getConstructorName(Y));D(this,"object",Y)}F(k0,"assertThrows");q.addMethod("throw",k0);q.addMethod("throws",k0);q.addMethod("Throw",k0);function r5(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=D(this,"itself"),J=typeof Q==="function"&&!X?Q.prototype[Z]:Q[Z];this.assert(typeof J==="function","expected #{this} to respond to "+_(Z),"expected #{this} to not respond to "+_(Z))}F(r5,"respondTo");q.addMethod("respondTo",r5);q.addMethod("respondsTo",r5);q.addProperty("itself",function(){D(this,"itself",!0)});function a5(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=Z(Q);this.assert(X,"expected #{this} to satisfy "+_1(Z),"expected #{this} to not satisfy"+_1(Z),D(this,"negate")?!1:!0,X)}F(a5,"satisfy");q.addMethod("satisfy",a5);q.addMethod("satisfies",a5);function s5(Z,$,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),J=D(this,"message"),W=D(this,"ssfi");new q(X,J,W,!0).is.numeric;let G="A `delta` value is required for `closeTo`";if($==null)throw new j(J?`${J}: ${G}`:G,void 0,W);if(new q($,J,W,!0).is.numeric,G="A `expected` value is required for `closeTo`",Z==null)throw new j(J?`${J}: ${G}`:G,void 0,W);new q(Z,J,W,!0).is.numeric;let Y=F((B)=>B<0?-B:B,"abs"),z=F((B)=>parseFloat(parseFloat(B).toPrecision(12)),"strip");this.assert(z(Y(X-Z))<=$,"expected #{this} to be close to "+Z+" +/- "+$,"expected #{this} not to be close to "+Z+" +/- "+$)}F(s5,"closeTo");q.addMethod("closeTo",s5);q.addMethod("approximately",s5);function p8(Z,$,Q,X,J){let W=Array.from($),G=Array.from(Z);if(!X){if(G.length!==W.length)return!1;W=W.slice()}return G.every(function(Y,z){if(J)return Q?Q(Y,W[z]):Y===W[z];if(!Q){let B=W.indexOf(Y);if(B===-1)return!1;if(!X)W.splice(B,1);return!0}return W.some(function(B,U){if(!Q(Y,B))return!1;if(!X)W.splice(U,1);return!0})})}F(p8,"isSubsetOf");q.addMethod("members",function(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=D(this,"message"),J=D(this,"ssfi");new q(Q,X,J,!0).to.be.iterable,new q(Z,X,J,!0).to.be.iterable;let W=D(this,"contains"),G=D(this,"ordered"),Y,z,B;if(W)Y=G?"an ordered superset":"a superset",z="expected #{this} to be "+Y+" of #{exp}",B="expected #{this} to not be "+Y+" of #{exp}";else Y=G?"ordered members":"members",z="expected #{this} to have the same "+Y+" as #{exp}",B="expected #{this} to not have the same "+Y+" as #{exp}";let U=D(this,"deep")?D(this,"eql"):void 0;this.assert(p8(Z,Q,U,W,G),z,B,Z,Q,!0)});q.addProperty("iterable",function(Z){if(Z)D(this,"message",Z);let $=D(this,"object");this.assert($!=null&&typeof $[Symbol.iterator]==="function","expected #{this} to be an iterable","expected #{this} to not be an iterable",$)});function i8(Z,$){if($)D(this,"message",$);let Q=D(this,"object"),X=D(this,"message"),J=D(this,"ssfi"),W=D(this,"contains"),G=D(this,"deep"),Y=D(this,"eql");if(new q(Z,X,J,!0).to.be.an("array"),W)this.assert(Z.some(function(z){return Q.indexOf(z)>-1||G&&Array.isArray(Q)&&Q.some(function(B){return Y(B,z)})}),"expected #{this} to "+(G?"deeply ":"")+"contain one of #{exp}","expected #{this} to not "+(G?"deeply ":"")+"contain one of #{exp}",Z,Q);else if(G)this.assert(Z.some(function(z){return Y(Q,z)}),"expected #{this} to deeply equal one of #{exp}","expected #{this} to not deeply equal one of #{exp}",Z,Q);else this.assert(Z.indexOf(Q)>-1,"expected #{this} to be one of #{exp}","expected #{this} to not be one of #{exp}",Z,Q)}F(i8,"oneOf");q.addMethod("oneOf",i8);function t5(Z,$,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),J=D(this,"message"),W=D(this,"ssfi");new q(X,J,W,!0).is.a("function");let G;if($===null||$===void 0)new q(Z,J,W,!0).is.a("function"),G=Z();else new q(Z,J,W,!0).to.have.property($),G=Z[$];X();let Y=$===void 0||$===null?Z():Z[$],z=$===void 0||$===null?G:"."+$;D(this,"deltaMsgObj",z),D(this,"initialDeltaValue",G),D(this,"finalDeltaValue",Y),D(this,"deltaBehavior","change"),D(this,"realDelta",Y!==G),this.assert(G!==Y,"expected "+z+" to change","expected "+z+" to not change")}F(t5,"assertChanges");q.addMethod("change",t5);q.addMethod("changes",t5);function e5(Z,$,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),J=D(this,"message"),W=D(this,"ssfi");new q(X,J,W,!0).is.a("function");let G;if($===null||$===void 0)new q(Z,J,W,!0).is.a("function"),G=Z();else new q(Z,J,W,!0).to.have.property($),G=Z[$];new q(G,J,W,!0).is.a("number"),X();let Y=$===void 0||$===null?Z():Z[$],z=$===void 0||$===null?G:"."+$;D(this,"deltaMsgObj",z),D(this,"initialDeltaValue",G),D(this,"finalDeltaValue",Y),D(this,"deltaBehavior","increase"),D(this,"realDelta",Y-G),this.assert(Y-G>0,"expected "+z+" to increase","expected "+z+" to not increase")}F(e5,"assertIncreases");q.addMethod("increase",e5);q.addMethod("increases",e5);function Z9(Z,$,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),J=D(this,"message"),W=D(this,"ssfi");new q(X,J,W,!0).is.a("function");let G;if($===null||$===void 0)new q(Z,J,W,!0).is.a("function"),G=Z();else new q(Z,J,W,!0).to.have.property($),G=Z[$];new q(G,J,W,!0).is.a("number"),X();let Y=$===void 0||$===null?Z():Z[$],z=$===void 0||$===null?G:"."+$;D(this,"deltaMsgObj",z),D(this,"initialDeltaValue",G),D(this,"finalDeltaValue",Y),D(this,"deltaBehavior","decrease"),D(this,"realDelta",G-Y),this.assert(Y-G<0,"expected "+z+" to decrease","expected "+z+" to not decrease")}F(Z9,"assertDecreases");q.addMethod("decrease",Z9);q.addMethod("decreases",Z9);function n8(Z,$){if($)D(this,"message",$);let Q=D(this,"deltaMsgObj"),X=D(this,"initialDeltaValue"),J=D(this,"finalDeltaValue"),W=D(this,"deltaBehavior"),G=D(this,"realDelta"),Y;if(W==="change")Y=Math.abs(J-X)===Math.abs(Z);else Y=G===Math.abs(Z);this.assert(Y,"expected "+Q+" to "+W+" by "+Z,"expected "+Q+" to not "+W+" by "+Z)}F(n8,"assertDelta");q.addMethod("by",n8);q.addProperty("extensible",function(){let Z=D(this,"object"),$=Z===Object(Z)&&Object.isExtensible(Z);this.assert($,"expected #{this} to be extensible","expected #{this} to not be extensible")});q.addProperty("sealed",function(){let Z=D(this,"object"),$=Z===Object(Z)?Object.isSealed(Z):!0;this.assert($,"expected #{this} to be sealed","expected #{this} to not be sealed")});q.addProperty("frozen",function(){let Z=D(this,"object"),$=Z===Object(Z)?Object.isFrozen(Z):!0;this.assert($,"expected #{this} to be frozen","expected #{this} to not be frozen")});q.addProperty("finite",function(Z){let $=D(this,"object");this.assert(typeof $==="number"&&isFinite($),"expected #{this} to be a finite number","expected #{this} to not be a finite number")});function N0(Z,$){if(Z===$)return!0;if(typeof $!==typeof Z)return!1;if(typeof Z!=="object"||Z===null)return Z===$;if(!$)return!1;if(Array.isArray(Z)){if(!Array.isArray($))return!1;return Z.every(function(Q){return $.some(function(X){return N0(Q,X)})})}if(Z instanceof Date)if($ instanceof Date)return Z.getTime()===$.getTime();else return!1;return Object.keys(Z).every(function(Q){let X=Z[Q],J=$[Q];if(typeof X==="object"&&X!==null&&J!==null)return N0(X,J);if(typeof X==="function")return X(J);return J===X})}F(N0,"compareSubset");q.addMethod("containSubset",function(Z){let $=k(this,"object"),Q=$1.showDiff;this.assert(N0(Z,$),"expected #{act} to contain subset #{exp}","expected #{act} to not contain subset #{exp}",Z,$,Q)});function L1(Z,$){return new q(Z,$)}F(L1,"expect");L1.fail=function(Z,$,Q,X){let J=Q;if(arguments.length<2)J=Z,Z=void 0;throw new j(J||"expect.fail()",{actual:Z,expected:$,operator:X},L1.fail)};var o8={};R5(o8,{Should:()=>FZ,should:()=>RZ});function $9(){function Z(){if(this instanceof String||this instanceof Number||this instanceof Boolean||typeof Symbol==="function"&&this instanceof Symbol||typeof BigInt==="function"&&this instanceof BigInt)return new q(this.valueOf(),null,Z);return new q(this,null,Z)}F(Z,"shouldGetter");function $(X){Object.defineProperty(this,"should",{value:X,enumerable:!0,configurable:!0,writable:!0})}F($,"shouldSetter"),Object.defineProperty(Object.prototype,"should",{set:$,get:Z,configurable:!0});let Q={};return Q.fail=function(X,J,W,G){if(arguments.length<2)W=X,X=void 0;throw W=W||"should.fail()",new j(W,{actual:X,expected:J,operator:G},Q.fail)},Q.equal=function(X,J,W){new q(X,W).to.equal(J)},Q.Throw=function(X,J,W,G){new q(X,G).to.Throw(J,W)},Q.exist=function(X,J){new q(X,J).to.exist},Q.not={},Q.not.equal=function(X,J,W){new q(X,W).to.not.equal(J)},Q.not.Throw=function(X,J,W,G){new q(X,G).to.not.Throw(J,W)},Q.not.exist=function(X,J){new q(X,J).to.not.exist},Q.throw=Q.Throw,Q.not.throw=Q.not.Throw,Q}F($9,"loadShould");var RZ=$9,FZ=$9;function V(Z,$){new q(null,null,V,!0).assert(Z,$,"[ negation message unavailable ]")}F(V,"assert");V.fail=function(Z,$,Q,X){if(arguments.length<2)Q=Z,Z=void 0;throw Q=Q||"assert.fail()",new j(Q,{actual:Z,expected:$,operator:X},V.fail)};V.isOk=function(Z,$){new q(Z,$,V.isOk,!0).is.ok};V.isNotOk=function(Z,$){new q(Z,$,V.isNotOk,!0).is.not.ok};V.equal=function(Z,$,Q){let X=new q(Z,Q,V.equal,!0);X.assert($==k(X,"object"),"expected #{this} to equal #{exp}","expected #{this} to not equal #{act}",$,Z,!0)};V.notEqual=function(Z,$,Q){let X=new q(Z,Q,V.notEqual,!0);X.assert($!=k(X,"object"),"expected #{this} to not equal #{exp}","expected #{this} to equal #{act}",$,Z,!0)};V.strictEqual=function(Z,$,Q){new q(Z,Q,V.strictEqual,!0).to.equal($)};V.notStrictEqual=function(Z,$,Q){new q(Z,Q,V.notStrictEqual,!0).to.not.equal($)};V.deepEqual=V.deepStrictEqual=function(Z,$,Q){new q(Z,Q,V.deepEqual,!0).to.eql($)};V.notDeepEqual=function(Z,$,Q){new q(Z,Q,V.notDeepEqual,!0).to.not.eql($)};V.isAbove=function(Z,$,Q){new q(Z,Q,V.isAbove,!0).to.be.above($)};V.isAtLeast=function(Z,$,Q){new q(Z,Q,V.isAtLeast,!0).to.be.least($)};V.isBelow=function(Z,$,Q){new q(Z,Q,V.isBelow,!0).to.be.below($)};V.isAtMost=function(Z,$,Q){new q(Z,Q,V.isAtMost,!0).to.be.most($)};V.isTrue=function(Z,$){new q(Z,$,V.isTrue,!0).is.true};V.isNotTrue=function(Z,$){new q(Z,$,V.isNotTrue,!0).to.not.equal(!0)};V.isFalse=function(Z,$){new q(Z,$,V.isFalse,!0).is.false};V.isNotFalse=function(Z,$){new q(Z,$,V.isNotFalse,!0).to.not.equal(!1)};V.isNull=function(Z,$){new q(Z,$,V.isNull,!0).to.equal(null)};V.isNotNull=function(Z,$){new q(Z,$,V.isNotNull,!0).to.not.equal(null)};V.isNaN=function(Z,$){new q(Z,$,V.isNaN,!0).to.be.NaN};V.isNotNaN=function(Z,$){new q(Z,$,V.isNotNaN,!0).not.to.be.NaN};V.exists=function(Z,$){new q(Z,$,V.exists,!0).to.exist};V.notExists=function(Z,$){new q(Z,$,V.notExists,!0).to.not.exist};V.isUndefined=function(Z,$){new q(Z,$,V.isUndefined,!0).to.equal(void 0)};V.isDefined=function(Z,$){new q(Z,$,V.isDefined,!0).to.not.equal(void 0)};V.isCallable=function(Z,$){new q(Z,$,V.isCallable,!0).is.callable};V.isNotCallable=function(Z,$){new q(Z,$,V.isNotCallable,!0).is.not.callable};V.isObject=function(Z,$){new q(Z,$,V.isObject,!0).to.be.a("object")};V.isNotObject=function(Z,$){new q(Z,$,V.isNotObject,!0).to.not.be.a("object")};V.isArray=function(Z,$){new q(Z,$,V.isArray,!0).to.be.an("array")};V.isNotArray=function(Z,$){new q(Z,$,V.isNotArray,!0).to.not.be.an("array")};V.isString=function(Z,$){new q(Z,$,V.isString,!0).to.be.a("string")};V.isNotString=function(Z,$){new q(Z,$,V.isNotString,!0).to.not.be.a("string")};V.isNumber=function(Z,$){new q(Z,$,V.isNumber,!0).to.be.a("number")};V.isNotNumber=function(Z,$){new q(Z,$,V.isNotNumber,!0).to.not.be.a("number")};V.isNumeric=function(Z,$){new q(Z,$,V.isNumeric,!0).is.numeric};V.isNotNumeric=function(Z,$){new q(Z,$,V.isNotNumeric,!0).is.not.numeric};V.isFinite=function(Z,$){new q(Z,$,V.isFinite,!0).to.be.finite};V.isBoolean=function(Z,$){new q(Z,$,V.isBoolean,!0).to.be.a("boolean")};V.isNotBoolean=function(Z,$){new q(Z,$,V.isNotBoolean,!0).to.not.be.a("boolean")};V.typeOf=function(Z,$,Q){new q(Z,Q,V.typeOf,!0).to.be.a($)};V.notTypeOf=function(Z,$,Q){new q(Z,Q,V.notTypeOf,!0).to.not.be.a($)};V.instanceOf=function(Z,$,Q){new q(Z,Q,V.instanceOf,!0).to.be.instanceOf($)};V.notInstanceOf=function(Z,$,Q){new q(Z,Q,V.notInstanceOf,!0).to.not.be.instanceOf($)};V.include=function(Z,$,Q){new q(Z,Q,V.include,!0).include($)};V.notInclude=function(Z,$,Q){new q(Z,Q,V.notInclude,!0).not.include($)};V.deepInclude=function(Z,$,Q){new q(Z,Q,V.deepInclude,!0).deep.include($)};V.notDeepInclude=function(Z,$,Q){new q(Z,Q,V.notDeepInclude,!0).not.deep.include($)};V.nestedInclude=function(Z,$,Q){new q(Z,Q,V.nestedInclude,!0).nested.include($)};V.notNestedInclude=function(Z,$,Q){new q(Z,Q,V.notNestedInclude,!0).not.nested.include($)};V.deepNestedInclude=function(Z,$,Q){new q(Z,Q,V.deepNestedInclude,!0).deep.nested.include($)};V.notDeepNestedInclude=function(Z,$,Q){new q(Z,Q,V.notDeepNestedInclude,!0).not.deep.nested.include($)};V.ownInclude=function(Z,$,Q){new q(Z,Q,V.ownInclude,!0).own.include($)};V.notOwnInclude=function(Z,$,Q){new q(Z,Q,V.notOwnInclude,!0).not.own.include($)};V.deepOwnInclude=function(Z,$,Q){new q(Z,Q,V.deepOwnInclude,!0).deep.own.include($)};V.notDeepOwnInclude=function(Z,$,Q){new q(Z,Q,V.notDeepOwnInclude,!0).not.deep.own.include($)};V.match=function(Z,$,Q){new q(Z,Q,V.match,!0).to.match($)};V.notMatch=function(Z,$,Q){new q(Z,Q,V.notMatch,!0).to.not.match($)};V.property=function(Z,$,Q){new q(Z,Q,V.property,!0).to.have.property($)};V.notProperty=function(Z,$,Q){new q(Z,Q,V.notProperty,!0).to.not.have.property($)};V.propertyVal=function(Z,$,Q,X){new q(Z,X,V.propertyVal,!0).to.have.property($,Q)};V.notPropertyVal=function(Z,$,Q,X){new q(Z,X,V.notPropertyVal,!0).to.not.have.property($,Q)};V.deepPropertyVal=function(Z,$,Q,X){new q(Z,X,V.deepPropertyVal,!0).to.have.deep.property($,Q)};V.notDeepPropertyVal=function(Z,$,Q,X){new q(Z,X,V.notDeepPropertyVal,!0).to.not.have.deep.property($,Q)};V.ownProperty=function(Z,$,Q){new q(Z,Q,V.ownProperty,!0).to.have.own.property($)};V.notOwnProperty=function(Z,$,Q){new q(Z,Q,V.notOwnProperty,!0).to.not.have.own.property($)};V.ownPropertyVal=function(Z,$,Q,X){new q(Z,X,V.ownPropertyVal,!0).to.have.own.property($,Q)};V.notOwnPropertyVal=function(Z,$,Q,X){new q(Z,X,V.notOwnPropertyVal,!0).to.not.have.own.property($,Q)};V.deepOwnPropertyVal=function(Z,$,Q,X){new q(Z,X,V.deepOwnPropertyVal,!0).to.have.deep.own.property($,Q)};V.notDeepOwnPropertyVal=function(Z,$,Q,X){new q(Z,X,V.notDeepOwnPropertyVal,!0).to.not.have.deep.own.property($,Q)};V.nestedProperty=function(Z,$,Q){new q(Z,Q,V.nestedProperty,!0).to.have.nested.property($)};V.notNestedProperty=function(Z,$,Q){new q(Z,Q,V.notNestedProperty,!0).to.not.have.nested.property($)};V.nestedPropertyVal=function(Z,$,Q,X){new q(Z,X,V.nestedPropertyVal,!0).to.have.nested.property($,Q)};V.notNestedPropertyVal=function(Z,$,Q,X){new q(Z,X,V.notNestedPropertyVal,!0).to.not.have.nested.property($,Q)};V.deepNestedPropertyVal=function(Z,$,Q,X){new q(Z,X,V.deepNestedPropertyVal,!0).to.have.deep.nested.property($,Q)};V.notDeepNestedPropertyVal=function(Z,$,Q,X){new q(Z,X,V.notDeepNestedPropertyVal,!0).to.not.have.deep.nested.property($,Q)};V.lengthOf=function(Z,$,Q){new q(Z,Q,V.lengthOf,!0).to.have.lengthOf($)};V.hasAnyKeys=function(Z,$,Q){new q(Z,Q,V.hasAnyKeys,!0).to.have.any.keys($)};V.hasAllKeys=function(Z,$,Q){new q(Z,Q,V.hasAllKeys,!0).to.have.all.keys($)};V.containsAllKeys=function(Z,$,Q){new q(Z,Q,V.containsAllKeys,!0).to.contain.all.keys($)};V.doesNotHaveAnyKeys=function(Z,$,Q){new q(Z,Q,V.doesNotHaveAnyKeys,!0).to.not.have.any.keys($)};V.doesNotHaveAllKeys=function(Z,$,Q){new q(Z,Q,V.doesNotHaveAllKeys,!0).to.not.have.all.keys($)};V.hasAnyDeepKeys=function(Z,$,Q){new q(Z,Q,V.hasAnyDeepKeys,!0).to.have.any.deep.keys($)};V.hasAllDeepKeys=function(Z,$,Q){new q(Z,Q,V.hasAllDeepKeys,!0).to.have.all.deep.keys($)};V.containsAllDeepKeys=function(Z,$,Q){new q(Z,Q,V.containsAllDeepKeys,!0).to.contain.all.deep.keys($)};V.doesNotHaveAnyDeepKeys=function(Z,$,Q){new q(Z,Q,V.doesNotHaveAnyDeepKeys,!0).to.not.have.any.deep.keys($)};V.doesNotHaveAllDeepKeys=function(Z,$,Q){new q(Z,Q,V.doesNotHaveAllDeepKeys,!0).to.not.have.all.deep.keys($)};V.throws=function(Z,$,Q,X){if(typeof $==="string"||$ instanceof RegExp)Q=$,$=null;let J=new q(Z,X,V.throws,!0).to.throw($,Q);return k(J,"object")};V.doesNotThrow=function(Z,$,Q,X){if(typeof $==="string"||$ instanceof RegExp)Q=$,$=null;new q(Z,X,V.doesNotThrow,!0).to.not.throw($,Q)};V.operator=function(Z,$,Q,X){let J;switch($){case"==":J=Z==Q;break;case"===":J=Z===Q;break;case">":J=Z>Q;break;case">=":J=Z>=Q;break;case"<":J=ZB instanceof DataView,z=Y(Z)?Z.byteLength:Z.length;if(z>0){G+=$.spacingOuter;let B=Q+$.indent;for(let U=0;U0){Y+=$.spacingOuter;let B=Q+$.indent;for(let U=0;U{let G=Z.toString();if(G==="ArrayContaining"||G==="ArrayNotContaining"){if(++X>$.maxDepth)return`[${G}]`;return`${G+S0}[${E0(Z.sample,$,Q,X,J,W)}]`}if(G==="ObjectContaining"||G==="ObjectNotContaining"){if(++X>$.maxDepth)return`[${G}]`;return`${G+S0}{${v0(Z.sample,$,Q,X,J,W)}}`}if(G==="StringMatching"||G==="StringNotMatching")return G+S0+W(Z.sample,$,Q,X,J);if(G==="StringContaining"||G==="StringNotContaining")return G+S0+W(Z.sample,$,Q,X,J);if(typeof Z.toAsymmetricMatcher!=="function")throw TypeError(`Asymmetric matcher ${Z.constructor.name} does not implement toAsymmetricMatcher()`);return Z.toAsymmetricMatcher()},AZ=(Z)=>Z&&Z.$$typeof===LZ,kZ={serialize:TZ,test:AZ},SZ=" ",U6=new Set(["DOMStringMap","NamedNodeMap"]),_Z=/^(?:HTML\w*Collection|NodeList)$/;function jZ(Z){return U6.has(Z)||_Z.test(Z)}var EZ=(Z)=>Z&&Z.constructor&&!!Z.constructor.name&&jZ(Z.constructor.name);function xZ(Z){return Z.constructor.name==="NamedNodeMap"}var vZ=(Z,$,Q,X,J,W)=>{let G=Z.constructor.name;if(++X>$.maxDepth)return`[${G}]`;return($.min?"":G+SZ)+(U6.has(G)?`{${v0(xZ(Z)?[...Z].reduce((Y,z)=>{return Y[z.name]=z.value,Y},{}):{...Z},$,Q,X,J,W)}}`:`[${E0([...Z],$,Q,X,J,W)}]`)},hZ={serialize:vZ,test:EZ};function z6(Z){return Z.replaceAll("<","<").replaceAll(">",">")}function G9(Z,$,Q,X,J,W,G){let Y=X+Q.indent,z=Q.colors;return Z.map((B)=>{let U=$[B];if(typeof U==="string"&&U[0]==="_"&&U.startsWith("__vitest_")&&/__vitest_\d+__/.test(U))return"";let K=G(U,Q,Y,J,W);if(typeof U!=="string"){if(K.includes(` -`))K=Q.spacingOuter+Y+K+Q.spacingOuter+X;K=`{${K}}`}return`${Q.spacingInner+X+z.prop.open+B+z.prop.close}=${z.value.open}${K}${z.value.close}`}).join("")}function h0(Z,$,Q,X,J,W){return Z.map((G)=>$.spacingOuter+Q+(typeof G==="string"?K6(G,$):W(G,$,Q,X,J))).join("")}function yZ(Z,$,Q,X,J,W){if($.printShadowRoot===!1)return"";return[`${$.spacingOuter+Q}#shadow-root`,h0(Z,$,Q+$.indent,X,J,W)].join("")}function K6(Z,$){let Q=$.colors.content;return Q.open+z6(Z)+Q.close}function bZ(Z,$){let Q=$.colors.comment;return`${Q.open}${Q.close}`}function B9(Z,$,Q,X,J){let W=X.colors.tag;return`${W.open}<${Z}${$&&W.close+$+X.spacingOuter+J+W.open}${Q?`>${W.close}${Q}${X.spacingOuter}${J}${W.open}${W.close}`}function Y9(Z,$){let Q=$.colors.tag;return`${Q.open}<${Z}${Q.close} …${Q.open} />${Q.close}`}var uZ=1,U9=3,V6=8,w6=11,fZ=/^(?:(?:HTML|SVG)\w*)?Element$/;function gZ(Z){try{return typeof Z.hasAttribute==="function"&&Z.hasAttribute("is")}catch{return!1}}function mZ(Z){let $=Z.constructor.name,{nodeType:Q,tagName:X}=Z,J=typeof X==="string"&&X.includes("-")||gZ(Z);return Q===uZ&&(fZ.test($)||J)||Q===U9&&$==="Text"||Q===V6&&$==="Comment"||Q===w6&&$==="DocumentFragment"}var q6=(Z)=>Z?.constructor?.name&&mZ(Z);function dZ(Z){return Z.nodeType===U9}function cZ(Z){return Z.nodeType===V6}function _0(Z){return Z.nodeType===w6}function a8(Z,$){let Q=Z.filter((X)=>{if(X.nodeType===U9)return(X.data||"").trim().length>0;return!0});if($)Q=Q.filter($);return Q}function H6(Z,$,Q,X,J,W,G){if(dZ(Z))return K6(Z.data,$);if(cZ(Z))return bZ(Z.data,$);let Y=_0(Z)?"DocumentFragment":Z.tagName.toLowerCase();if(++X>$.maxDepth)return Y9(Y,$);let z=Array.prototype.slice.call(Z.childNodes||Z.children),B=_0(Z)||!Z.shadowRoot?[]:Array.prototype.slice.call(Z.shadowRoot.children),U=G?a8(z,G):z,K=G?a8(B,G):B;return B9(Y,G9(_0(Z)?[]:Array.from(Z.attributes,(P)=>P.name).sort(),_0(Z)?{}:[...Z.attributes].reduce((P,w)=>{return P[w.name]=w.value,P},{}),$,Q+$.indent,X,J,W),(K.length>0?yZ(K,$,Q+$.indent,X,J,W):"")+h0(U,$,Q+$.indent,X,J,W),$,Q)}var lZ=(Z,$,Q,X,J,W)=>H6(Z,$,Q,X,J,W);function P6(Z){return{test:q6,serialize:($,Q,X,J,W,G)=>H6($,Q,X,J,W,G,Z)}}var pZ={serialize:lZ,test:q6},iZ="@@__IMMUTABLE_ITERABLE__@@",nZ="@@__IMMUTABLE_LIST__@@",oZ="@@__IMMUTABLE_KEYED__@@",rZ="@@__IMMUTABLE_MAP__@@",s8="@@__IMMUTABLE_ORDERED__@@",aZ="@@__IMMUTABLE_RECORD__@@",sZ="@@__IMMUTABLE_SEQ__@@",tZ="@@__IMMUTABLE_SET__@@",eZ="@@__IMMUTABLE_STACK__@@",f1=(Z)=>`Immutable.${Z}`,y0=(Z)=>`[${Z}]`,Z0=" ",t8="…";function Z$(Z,$,Q,X,J,W,G){return++X>$.maxDepth?y0(f1(G)):`${f1(G)+Z0}{${x0(Z.entries(),$,Q,X,J,W)}}`}function $$(Z){let $=0;return{next(){if($$.maxDepth?y0(G):`${G+Z0}{${x0($$(Z),$,Q,X,J,W)}}`}function X$(Z,$,Q,X,J,W){let G=f1("Seq");if(++X>$.maxDepth)return y0(G);if(Z[oZ])return`${G+Z0}{${Z._iter||Z._object?x0(Z.entries(),$,Q,X,J,W):t8}}`;return`${G+Z0}[${Z._iter||Z._array||Z._collection||Z._iterable?W9(Z.values(),$,Q,X,J,W):t8}]`}function Q9(Z,$,Q,X,J,W,G){return++X>$.maxDepth?y0(f1(G)):`${f1(G)+Z0}[${W9(Z.values(),$,Q,X,J,W)}]`}var J$=(Z,$,Q,X,J,W)=>{if(Z[rZ])return Z$(Z,$,Q,X,J,W,Z[s8]?"OrderedMap":"Map");if(Z[nZ])return Q9(Z,$,Q,X,J,W,"List");if(Z[tZ])return Q9(Z,$,Q,X,J,W,Z[s8]?"OrderedSet":"Set");if(Z[eZ])return Q9(Z,$,Q,X,J,W,"Stack");if(Z[sZ])return X$(Z,$,Q,X,J,W);return Q$(Z,$,Q,X,J,W)},W$=(Z)=>Z&&(Z[iZ]===!0||Z[aZ]===!0),G$={serialize:J$,test:W$};function N6(Z){return Z&&Z.__esModule&&Object.prototype.hasOwnProperty.call(Z,"default")?Z.default:Z}var X9={exports:{}},y={},e8;function B$(){if(e8)return y;e8=1;var Z=Symbol.for("react.transitional.element"),$=Symbol.for("react.portal"),Q=Symbol.for("react.fragment"),X=Symbol.for("react.strict_mode"),J=Symbol.for("react.profiler"),W=Symbol.for("react.consumer"),G=Symbol.for("react.context"),Y=Symbol.for("react.forward_ref"),z=Symbol.for("react.suspense"),B=Symbol.for("react.suspense_list"),U=Symbol.for("react.memo"),K=Symbol.for("react.lazy"),P=Symbol.for("react.view_transition"),w=Symbol.for("react.client.reference");function N(H){if(typeof H==="object"&&H!==null){var R=H.$$typeof;switch(R){case Z:switch(H=H.type,H){case Q:case J:case X:case z:case B:case P:return H;default:switch(H=H&&H.$$typeof,H){case G:case Y:case K:case U:return H;case W:return H;default:return R}}case $:return R}}}return y.ContextConsumer=W,y.ContextProvider=G,y.Element=Z,y.ForwardRef=Y,y.Fragment=Q,y.Lazy=K,y.Memo=U,y.Portal=$,y.Profiler=J,y.StrictMode=X,y.Suspense=z,y.SuspenseList=B,y.isContextConsumer=function(H){return N(H)===W},y.isContextProvider=function(H){return N(H)===G},y.isElement=function(H){return typeof H==="object"&&H!==null&&H.$$typeof===Z},y.isForwardRef=function(H){return N(H)===Y},y.isFragment=function(H){return N(H)===Q},y.isLazy=function(H){return N(H)===K},y.isMemo=function(H){return N(H)===U},y.isPortal=function(H){return N(H)===$},y.isProfiler=function(H){return N(H)===J},y.isStrictMode=function(H){return N(H)===X},y.isSuspense=function(H){return N(H)===z},y.isSuspenseList=function(H){return N(H)===B},y.isValidElementType=function(H){return typeof H==="string"||typeof H==="function"||H===Q||H===J||H===X||H===z||H===B||typeof H==="object"&&H!==null&&(H.$$typeof===K||H.$$typeof===U||H.$$typeof===G||H.$$typeof===W||H.$$typeof===Y||H.$$typeof===w||H.getModuleId!==void 0)?!0:!1},y.typeOf=N,y}var Z6;function Y$(){if(Z6)return X9.exports;return Z6=1,X9.exports=B$(),X9.exports}var D6=Y$(),U$=N6(D6),z$=Y6({__proto__:null,default:U$},[D6]),J9={exports:{}},x={},$6;function K$(){if($6)return x;$6=1;var Z=Symbol.for("react.element"),$=Symbol.for("react.portal"),Q=Symbol.for("react.fragment"),X=Symbol.for("react.strict_mode"),J=Symbol.for("react.profiler"),W=Symbol.for("react.provider"),G=Symbol.for("react.context"),Y=Symbol.for("react.server_context"),z=Symbol.for("react.forward_ref"),B=Symbol.for("react.suspense"),U=Symbol.for("react.suspense_list"),K=Symbol.for("react.memo"),P=Symbol.for("react.lazy"),w=Symbol.for("react.offscreen"),N;N=Symbol.for("react.module.reference");function H(R){if(typeof R==="object"&&R!==null){var O=R.$$typeof;switch(O){case Z:switch(R=R.type,R){case Q:case J:case X:case B:case U:return R;default:switch(R=R&&R.$$typeof,R){case Y:case G:case z:case P:case K:case W:return R;default:return O}}case $:return O}}}return x.ContextConsumer=G,x.ContextProvider=W,x.Element=Z,x.ForwardRef=z,x.Fragment=Q,x.Lazy=P,x.Memo=K,x.Portal=$,x.Profiler=J,x.StrictMode=X,x.Suspense=B,x.SuspenseList=U,x.isAsyncMode=function(){return!1},x.isConcurrentMode=function(){return!1},x.isContextConsumer=function(R){return H(R)===G},x.isContextProvider=function(R){return H(R)===W},x.isElement=function(R){return typeof R==="object"&&R!==null&&R.$$typeof===Z},x.isForwardRef=function(R){return H(R)===z},x.isFragment=function(R){return H(R)===Q},x.isLazy=function(R){return H(R)===P},x.isMemo=function(R){return H(R)===K},x.isPortal=function(R){return H(R)===$},x.isProfiler=function(R){return H(R)===J},x.isStrictMode=function(R){return H(R)===X},x.isSuspense=function(R){return H(R)===B},x.isSuspenseList=function(R){return H(R)===U},x.isValidElementType=function(R){return typeof R==="string"||typeof R==="function"||R===Q||R===J||R===X||R===B||R===U||R===w||typeof R==="object"&&R!==null&&(R.$$typeof===P||R.$$typeof===K||R.$$typeof===W||R.$$typeof===G||R.$$typeof===z||R.$$typeof===N||R.getModuleId!==void 0)?!0:!1},x.typeOf=H,x}var Q6;function V$(){if(Q6)return J9.exports;return Q6=1,J9.exports=K$(),J9.exports}var R6=V$(),w$=N6(R6),q$=Y6({__proto__:null,default:w$},[R6]),H$=["isAsyncMode","isConcurrentMode","isContextConsumer","isContextProvider","isElement","isForwardRef","isFragment","isLazy","isMemo","isPortal","isProfiler","isStrictMode","isSuspense","isSuspenseList","isValidElementType"],E1=Object.fromEntries(H$.map((Z)=>[Z,($)=>q$[Z]($)||z$[Z]($)]));function F6(Z,$=[]){if(Array.isArray(Z))for(let Q of Z)F6(Q,$);else if(Z!=null&&Z!==!1&&Z!=="")$.push(Z);return $}function X6(Z){let $=Z.type;if(typeof $==="string")return $;if(typeof $==="function")return $.displayName||$.name||"Unknown";if(E1.isFragment(Z))return"React.Fragment";if(E1.isSuspense(Z))return"React.Suspense";if(typeof $==="object"&&$!==null){if(E1.isContextProvider(Z))return"Context.Provider";if(E1.isContextConsumer(Z))return"Context.Consumer";if(E1.isForwardRef(Z)){if($.displayName)return $.displayName;let Q=$.render.displayName||$.render.name||"";return Q===""?"ForwardRef":`ForwardRef(${Q})`}if(E1.isMemo(Z)){let Q=$.displayName||$.type.displayName||$.type.name||"";return Q===""?"Memo":`Memo(${Q})`}}return"UNDEFINED"}function P$(Z){let{props:$}=Z;return Object.keys($).filter((Q)=>Q!=="children"&&$[Q]!==void 0).sort()}var N$=(Z,$,Q,X,J,W)=>++X>$.maxDepth?Y9(X6(Z),$):B9(X6(Z),G9(P$(Z),Z.props,$,Q+$.indent,X,J,W),h0(F6(Z.props.children),$,Q+$.indent,X,J,W),$,Q),D$=(Z)=>Z!=null&&E1.isElement(Z),R$={serialize:N$,test:D$},F$=typeof Symbol==="function"&&Symbol.for?Symbol.for("react.test.json"):245830487;function M$(Z){let{props:$}=Z;return $?Object.keys($).filter((Q)=>$[Q]!==void 0).sort():[]}var O$=(Z,$,Q,X,J,W)=>++X>$.maxDepth?Y9(Z.type,$):B9(Z.type,Z.props?G9(M$(Z),Z.props,$,Q+$.indent,X,J,W):"",Z.children?h0(Z.children,$,Q+$.indent,X,J,W):"",$,Q),I$=(Z)=>Z&&Z.$$typeof===F$,C$={serialize:O$,test:I$},M6=Object.prototype.toString,L$=Date.prototype.toISOString,T$=Error.prototype.toString,J6=RegExp.prototype.toString;function j0(Z){return typeof Z.constructor==="function"&&Z.constructor.name||"Object"}function A$(Z){return typeof window<"u"&&Z===window}var k$=/^Symbol\((.*)\)(.*)$/,S$=/\n/g;class z9 extends Error{constructor(Z,$){super(Z);this.stack=$,this.name=this.constructor.name}}function _$(Z){return Z==="[object Array]"||Z==="[object ArrayBuffer]"||Z==="[object DataView]"||Z==="[object Float32Array]"||Z==="[object Float64Array]"||Z==="[object Int8Array]"||Z==="[object Int16Array]"||Z==="[object Int32Array]"||Z==="[object Uint8Array]"||Z==="[object Uint8ClampedArray]"||Z==="[object Uint16Array]"||Z==="[object Uint32Array]"}function j$(Z){return Object.is(Z,-0)?"-0":String(Z)}function E$(Z){return String(`${Z}n`)}function W6(Z,$){if(!$)return"[Function]";return`[Function ${Z.name||"anonymous"}]`}function G6(Z){return String(Z).replace(k$,"Symbol($1)")}function B6(Z){return`[${T$.call(Z)}]`}function O6(Z,$,Q,X,J){if(Z===!0||Z===!1)return`${Z}`;if(Z===void 0)return"undefined";if(Z===null)return"null";let W=typeof Z;if(W==="number")return j$(Z);if(W==="bigint")return E$(Z);if(W==="string"){let Y=J?"'":'"';if(X){let z=J?/['\\]/g:/["\\]/g;return`${Y}${Z.replaceAll(z,"\\$&")}${Y}`}return`${Y}${Z}${Y}`}if(W==="function")return W6(Z,$);if(W==="symbol")return G6(Z);let G=M6.call(Z);if(G==="[object WeakMap]")return"WeakMap {}";if(G==="[object WeakSet]")return"WeakSet {}";if(G==="[object Function]"||G==="[object GeneratorFunction]")return W6(Z,$);if(G==="[object Symbol]")return G6(Z);if(G==="[object Date]")return Number.isNaN(+Z)?"Date { NaN }":L$.call(Z);if(G==="[object Error]")return B6(Z);if(G==="[object RegExp]"){if(Q)return J6.call(Z).replaceAll(/[$()*+.?[\\\]^{|}]/g,"\\$&");return J6.call(Z)}if(Z instanceof Error)return B6(Z);return null}function I6(Z,$,Q,X,J,W){if(J.includes(Z))return"[Circular]";J=[...J],J.push(Z);let G=++X>$.maxDepth,Y=$.min;if($.callToJSON&&!G&&Z.toJSON&&typeof Z.toJSON==="function"&&!W)return A1(Z.toJSON(),$,Q,X,J,!0);let z=M6.call(Z);if(z==="[object Arguments]")return G?"[Arguments]":`${Y?"":"Arguments "}[${E0(Z,$,Q,X,J,A1)}]`;if(_$(z))return G?`[${Z.constructor.name}]`:`${!$.printBasicPrototype&&Z.constructor.name==="Array"?"":`${Z.constructor.name} `}[${E0(Z,$,Q,X,J,A1)}]`;if(z==="[object Map]")return G?"[Map]":`Map {${x0(Z.entries(),$,Q,X,J,A1," => ",Z.size)}}`;if(z==="[object Set]")return G?"[Set]":`Set {${W9(Z.values(),$,Q,X,J,A1,Z.size)}}`;return G||A$(Z)?`[${j0(Z)}]`:`${!$.printBasicPrototype&&j0(Z)==="Object"?"":`${j0(Z)} `}{${v0(Z,$,Q,X,J,A1)}}`}var x$={test:(Z)=>Z&&Z instanceof Error,serialize(Z,$,Q,X,J,W){if(J.includes(Z))return"[Circular]";J=[...J,Z];let G=++X>$.maxDepth,{message:Y,cause:z,...B}=Z,U={message:Y,...typeof z<"u"?{cause:z}:{},...Z instanceof AggregateError?{errors:Z.errors}:{},...B},K=Z.name!=="Error"?Z.name:j0(Z);return G?`[${K}]`:`${K} {${v0(U,$,Q,X,J,W,null)}}`}};function v$(Z){return Z.serialize!=null}function C6(Z,$,Q,X,J,W){let G;try{G=v$(Z)?Z.serialize($,Q,X,J,W,A1):Z.print($,(Y)=>A1(Y,Q,X,J,W),(Y)=>{let z=X+Q.indent;return z+Y.replaceAll(S$,` -${z}`)},{edgeSpacing:Q.spacingOuter,min:Q.min,spacing:Q.spacingInner},Q.colors)}catch(Y){throw new z9(Y.message,Y.stack)}if(typeof G!=="string")throw TypeError(`pretty-format: Plugin must return type "string" but instead returned "${typeof G}".`);return G}function L6(Z,$){for(let Q of Z)try{if(Q.test($))return Q}catch(X){throw new z9(X.message,X.stack)}return null}function A1(Z,$,Q,X,J,W){let G,Y=L6($.plugins,Z);if(Y!==null)G=C6(Y,Z,$,Q,X,J);else{let z=O6(Z,$.printFunctionName,$.escapeRegex,$.escapeString,$.singleQuote);if(z!==null)G=z;else G=I6(Z,$,Q,X,J,W)}if($._outputLengthPerDepth[X]??=0,$._outputLengthPerDepth[X]+=G.length,$._outputLengthPerDepth[X]>$.maxOutputLength)$.maxDepth=0;return G}var K9={comment:"gray",content:"reset",prop:"yellow",tag:"cyan",value:"green"},T6=Object.keys(K9),Q1={callToJSON:!0,compareKeys:void 0,escapeRegex:!1,escapeString:!0,highlight:!1,indent:2,maxDepth:Number.POSITIVE_INFINITY,maxOutputLength:1e6,maxWidth:Number.POSITIVE_INFINITY,min:!1,plugins:[],printBasicPrototype:!0,printFunctionName:!0,printShadowRoot:!0,theme:K9,singleQuote:!1,quoteKeys:!0,spacingInner:` +(()=>{var Y0=globalThis;if(typeof Y0.global>"u")Y0.global=globalThis;if(typeof Y0.__require>"u")Y0.__require=(Z)=>{throw Error(`Dynamic require of "${Z}" is not supported in the sandbox`)};if(!("Event"in globalThis))Reflect.set(globalThis,"Event",class{type;constructor(Q){this.type=Q}});if(!("EventTarget"in globalThis))Reflect.set(globalThis,"EventTarget",class{#Z=new Map;addEventListener(Q,X){let W=this.#Z.get(Q)??new Set;W.add(X),this.#Z.set(Q,W)}removeEventListener(Q,X){this.#Z.get(Q)?.delete(X)}dispatchEvent(Q){for(let X of this.#Z.get(Q.type)??[])X(Q);return!0}});var a0={reset:[0,0],bold:[1,22,"\x1B[22m\x1B[1m"],dim:[2,22,"\x1B[22m\x1B[2m"],italic:[3,23],underline:[4,24],inverse:[7,27],hidden:[8,28],strikethrough:[9,29],black:[30,39],red:[31,39],green:[32,39],yellow:[33,39],blue:[34,39],magenta:[35,39],cyan:[36,39],white:[37,39],gray:[90,39],bgBlack:[40,49],bgRed:[41,49],bgGreen:[42,49],bgYellow:[43,49],bgBlue:[44,49],bgMagenta:[45,49],bgCyan:[46,49],bgWhite:[47,49],blackBright:[90,39],redBright:[91,39],greenBright:[92,39],yellowBright:[93,39],blueBright:[94,39],magentaBright:[95,39],cyanBright:[96,39],whiteBright:[97,39],bgBlackBright:[100,49],bgRedBright:[101,49],bgGreenBright:[102,49],bgYellowBright:[103,49],bgBlueBright:[104,49],bgMagentaBright:[105,49],bgCyanBright:[106,49],bgWhiteBright:[107,49]};function c9(Z){return String(Z)}c9.open="";c9.close="";function _5(){let Z={isColorSupported:!1,reset:c9};for(let Q in a0)Z[Q]=c9;return Z}function E5(){let Z=typeof process<"u"?process:void 0,Q=(Z==null?void 0:Z.env)||{},X=Q.FORCE_TTY!=="false",W=(Z==null?void 0:Z.argv)||[];return!(("NO_COLOR"in Q)||W.includes("--no-color"))&&(("FORCE_COLOR"in Q)||W.includes("--color")||(Z==null?void 0:Z.platform)==="win32"||X&&Q.TERM!=="dumb"||("CI"in Q))||typeof window<"u"&&!!window.chrome}function x5({force:Z}={}){let Q=Z||E5(),X=(G,U,z,Y)=>{let K="",B=0;do K+=G.substring(B,Y)+z,B=Y+U.length,Y=G.indexOf(U,B);while(~Y);return K+G.substring(B)},W=(G,U,z=G)=>{let Y=(K)=>{let B=String(K),P=B.indexOf(U,G.length);return~P?G+X(B,U,z,P)+U:G+B+U};return Y.open=G,Y.close=U,Y},$={isColorSupported:Q},J=(G)=>`\x1B[${G}m`;for(let G in a0){let U=a0[G];$[G]=Q?W(J(U[0]),J(U[1]),U[2]):c9}return $}var b6=x5();function u6(){Object.assign(b6,_5())}var f=b6;var q1=Object.defineProperty,v5=(Z,Q,X)=>(Q in Z)?q1(Z,Q,{enumerable:!0,configurable:!0,writable:!0,value:X}):Z[Q]=X,R=(Z,Q)=>q1(Z,"name",{value:Q,configurable:!0}),H1=(Z,Q)=>{for(var X in Q)q1(Z,X,{get:Q[X],enumerable:!0})},o6=(Z,Q,X)=>v5(Z,typeof Q!=="symbol"?Q+"":Q,X),z9={};H1(z9,{addChainableMethod:()=>k1,addLengthGuard:()=>r9,addMethod:()=>C1,addProperty:()=>L1,checkError:()=>X9,compareByInspect:()=>K0,eql:()=>O7,events:()=>N0,expectTypes:()=>X7,flag:()=>k,getActual:()=>H0,getMessage:()=>D1,getName:()=>D0,getOperator:()=>E1,getOwnEnumerableProperties:()=>_1,getOwnEnumerablePropertySymbols:()=>j1,getPathInfo:()=>O1,hasProperty:()=>P0,inspect:()=>j,isNaN:()=>B0,isNumeric:()=>l,isProxyEnabled:()=>o9,isRegExp:()=>V0,objDisplay:()=>k9,overwriteChainableMethod:()=>S1,overwriteMethod:()=>A1,overwriteProperty:()=>T1,proxify:()=>y9,test:()=>P1,transferFlags:()=>K9,type:()=>h});var X9={};H1(X9,{compatibleConstructor:()=>s6,compatibleInstance:()=>a6,compatibleMessage:()=>t6,getConstructorName:()=>e6,getMessage:()=>Z7});function q0(Z){return Z instanceof Error||Object.prototype.toString.call(Z)==="[object Error]"}R(q0,"isErrorInstance");function r6(Z){return Object.prototype.toString.call(Z)==="[object RegExp]"}R(r6,"isRegExp");function a6(Z,Q){return q0(Q)&&Z===Q}R(a6,"compatibleInstance");function s6(Z,Q){if(q0(Q))return Z.constructor===Q.constructor||Z instanceof Q.constructor;else if((typeof Q==="object"||typeof Q==="function")&&Q.prototype)return Z.constructor===Q||Z instanceof Q;return!1}R(s6,"compatibleConstructor");function t6(Z,Q){let X=typeof Z==="string"?Z:Z.message;if(r6(Q))return Q.test(X);else if(typeof Q==="string")return X.indexOf(Q)!==-1;return!1}R(t6,"compatibleMessage");function e6(Z){let Q=Z;if(q0(Z))Q=Z.constructor.name;else if(typeof Z==="function"){if(Q=Z.name,Q==="")Q=new Z().name||Q}return Q}R(e6,"getConstructorName");function Z7(Z){let Q="";if(Z&&Z.message)Q=Z.message;else if(typeof Z==="string")Q=Z;return Q}R(Z7,"getMessage");function k(Z,Q,X){let W=Z.__flags||(Z.__flags=Object.create(null));if(arguments.length===3)W[Q]=X;else return W[Q]}R(k,"flag");function P1(Z,Q){let X=k(Z,"negate"),W=Q[0];return X?!W:W}R(P1,"test");function h(Z){if(typeof Z>"u")return"undefined";if(Z===null)return"null";let Q=Z[Symbol.toStringTag];if(typeof Q==="string")return Q;return Object.prototype.toString.call(Z).slice(8,-1)}R(h,"type");var h5="captureStackTrace"in Error,Q7=class Z extends Error{constructor(Q="Unspecified AssertionError",X,W){super(Q);if(o6(this,"message"),this.message=Q,h5)Error.captureStackTrace(this,W||Z);for(let $ in X)if(!($ in this))this[$]=X[$]}get name(){return"AssertionError"}get ok(){return!1}toJSON(Q){return{...this,name:this.name,message:this.message,ok:!1,stack:Q!==!1?this.stack:void 0}}};R(Q7,"AssertionError");var _=Q7;function X7(Z,Q){let X=k(Z,"message"),W=k(Z,"ssfi");X=X?X+": ":"",Z=k(Z,"object"),Q=Q.map(function(G){return G.toLowerCase()}),Q.sort();let $=Q.map(function(G,U){let z=~["a","e","i","o","u"].indexOf(G.charAt(0))?"an":"a";return(Q.length>1&&U===Q.length-1?"or ":"")+z+" "+G}).join(", "),J=h(Z).toLowerCase();if(!Q.some(function(G){return J===G}))throw new _(X+"object tested must be "+$+", but "+J+" given",void 0,W)}R(X7,"expectTypes");function H0(Z,Q){return Q.length>4?Q[4]:Z._obj}R(H0,"getActual");var f6={bold:["1","22"],dim:["2","22"],italic:["3","23"],underline:["4","24"],inverse:["7","27"],hidden:["8","28"],strike:["9","29"],black:["30","39"],red:["31","39"],green:["32","39"],yellow:["33","39"],blue:["34","39"],magenta:["35","39"],cyan:["36","39"],white:["37","39"],brightblack:["30;1","39"],brightred:["31;1","39"],brightgreen:["32;1","39"],brightyellow:["33;1","39"],brightblue:["34;1","39"],brightmagenta:["35;1","39"],brightcyan:["36;1","39"],brightwhite:["37;1","39"],grey:["90","39"]},y5={special:"cyan",number:"yellow",bigint:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",symbol:"green",date:"magenta",regexp:"red"},v9="…";function W7(Z,Q){let X=f6[y5[Q]]||f6[Q]||"";if(!X)return String(Z);return`\x1B[${X[0]}m${String(Z)}\x1B[${X[1]}m`}R(W7,"colorise");function $7({showHidden:Z=!1,depth:Q=2,colors:X=!1,customInspect:W=!0,showProxy:$=!1,maxArrayLength:J=1/0,breakLength:G=1/0,seen:U=[],truncate:z=1/0,stylize:Y=String}={},K){let B={showHidden:Boolean(Z),depth:Number(Q),colors:Boolean(X),customInspect:Boolean(W),showProxy:Boolean($),maxArrayLength:Number(J),breakLength:Number(G),truncate:Number(z),seen:U,inspect:K,stylize:Y};if(B.colors)B.stylize=W7;return B}R($7,"normaliseOptions");function J7(Z){return Z>="\uD800"&&Z<="\uDBFF"}R(J7,"isHighSurrogate");function N9(Z,Q,X=v9){Z=String(Z);let W=X.length,$=Z.length;if(W>Q&&$>W)return X;if($>Q&&$>W){let J=Q-W;if(J>0&&J7(Z[J-1]))J=J-1;return`${Z.slice(0,J)}${X}`}return Z}R(N9,"truncate");function J9(Z,Q,X,W=", "){X=X||Q.inspect;let $=Z.length;if($===0)return"";let J=Q.truncate,G="",U="",z="";for(let Y=0;Y<$;Y+=1){let K=Y+1===Z.length,B=Y+2===Z.length;z=`${v9}(${Z.length-Y})`;let P=Z[Y];Q.truncate=J-G.length-(K?0:W.length);let w=U||X(P,Q)+(K?"":W),N=G.length+w.length,H=N+z.length;if(K&&N>J&&G.length+z.length<=J)break;if(!K&&!B&&H>J)break;if(U=K?"":X(Z[Y+1],Q)+(B?"":W),!K&&B&&H>J&&N+U.length>J)break;if(G+=w,!K&&!B&&N+U.length>=J){z=`${v9}(${Z.length-Y-1})`;break}z=""}return`${G}${z}`}R(J9,"inspectList");function G7(Z){if(Z.match(/^[a-zA-Z_][a-zA-Z_0-9]*$/))return Z;return JSON.stringify(Z).replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'")}R(G7,"quoteComplexKey");function h9([Z,Q],X){if(X.truncate-=2,typeof Z==="string")Z=G7(Z);else if(typeof Z!=="number")Z=`[${X.inspect(Z,X)}]`;return X.truncate-=Z.length,Q=X.inspect(Q,X),`${Z}: ${Q}`}R(h9,"inspectProperty");function Y7(Z,Q){let X=Object.keys(Z).slice(Z.length);if(!Z.length&&!X.length)return"[]";Q.truncate-=4;let W=J9(Z,Q);Q.truncate-=W.length;let $="";if(X.length)$=J9(X.map((J)=>[J,Z[J]]),Q,h9);return`[ ${W}${$?`, ${$}`:""} ]`}R(Y7,"inspectArray");var b5=R((Z)=>{if(typeof Buffer==="function"&&Z instanceof Buffer)return"Buffer";if(Z[Symbol.toStringTag])return Z[Symbol.toStringTag];return Z.constructor.name},"getArrayName");function q9(Z,Q){let X=b5(Z);Q.truncate-=X.length+4;let W=Object.keys(Z).slice(Z.length);if(!Z.length&&!W.length)return`${X}[]`;let $="";for(let G=0;G[G,Z[G]]),Q,h9);return`${X}[ ${$}${J?`, ${J}`:""} ]`}R(q9,"inspectTypedArray");function U7(Z,Q){let X=Z.toJSON();if(X===null)return"Invalid Date";let W=X.split("T"),$=W[0];return Q.stylize(`${$}T${N9(W[1],Q.truncate-$.length-1)}`,"date")}R(U7,"inspectDate");function Z1(Z,Q){let X=Z[Symbol.toStringTag]||"Function",W=Z.name;if(!W)return Q.stylize(`[${X}]`,"special");return Q.stylize(`[${X} ${N9(W,Q.truncate-11)}]`,"special")}R(Z1,"inspectFunction");function z7([Z,Q],X){return X.truncate-=4,Z=X.inspect(Z,X),X.truncate-=Z.length,Q=X.inspect(Q,X),`${Z} => ${Q}`}R(z7,"inspectMapEntry");function K7(Z){let Q=[];return Z.forEach((X,W)=>{Q.push([W,X])}),Q}R(K7,"mapToEntries");function B7(Z,Q){if(Z.size===0)return"Map{}";return Q.truncate-=7,`Map{ ${J9(K7(Z),Q,z7)} }`}R(B7,"inspectMap");var u5=Number.isNaN||((Z)=>Z!==Z);function Q1(Z,Q){if(u5(Z))return Q.stylize("NaN","number");if(Z===1/0)return Q.stylize("Infinity","number");if(Z===-1/0)return Q.stylize("-Infinity","number");if(Z===0)return Q.stylize(1/Z===1/0?"+0":"-0","number");return Q.stylize(N9(String(Z),Q.truncate),"number")}R(Q1,"inspectNumber");function X1(Z,Q){let X=N9(Z.toString(),Q.truncate-1);if(X!==v9)X+="n";return Q.stylize(X,"bigint")}R(X1,"inspectBigInt");function V7(Z,Q){let X=Z.toString().split("/")[2],W=Q.truncate-(2+X.length),$=Z.source;return Q.stylize(`/${N9($,W)}/${X}`,"regexp")}R(V7,"inspectRegExp");function w7(Z){let Q=[];return Z.forEach((X)=>{Q.push(X)}),Q}R(w7,"arrayFromSet");function q7(Z,Q){if(Z.size===0)return"Set{}";return Q.truncate-=7,`Set{ ${J9(w7(Z),Q)} }`}R(q7,"inspectSet");var g6=new RegExp("['\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]","g"),f5={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r","'":"\\'","\\":"\\\\"},g5=16,m5=4;function H7(Z){return f5[Z]||`\\u${`0000${Z.charCodeAt(0).toString(g5)}`.slice(-m5)}`}R(H7,"escape");function W1(Z,Q){if(g6.test(Z))Z=Z.replace(g6,H7);return Q.stylize(`'${N9(Z,Q.truncate-2)}'`,"string")}R(W1,"inspectString");function $1(Z){if("description"in Symbol.prototype)return Z.description?`Symbol(${Z.description})`:"Symbol()";return Z.toString()}R($1,"inspectSymbol");var d5=R(()=>"Promise{…}","getPromiseValue"),c5=d5;function p9(Z,Q){let X=Object.getOwnPropertyNames(Z),W=Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(Z):[];if(X.length===0&&W.length===0)return"{}";if(Q.truncate-=4,Q.seen=Q.seen||[],Q.seen.includes(Z))return"[Circular]";Q.seen.push(Z);let $=J9(X.map((U)=>[U,Z[U]]),Q,h9),J=J9(W.map((U)=>[U,Z[U]]),Q,h9);Q.seen.pop();let G="";if($&&J)G=", ";return`{ ${$}${G}${J} }`}R(p9,"inspectObject");var s0=typeof Symbol<"u"&&Symbol.toStringTag?Symbol.toStringTag:!1;function P7(Z,Q){let X="";if(s0&&s0 in Z)X=Z[s0];if(X=X||Z.constructor.name,!X||X==="_class")X="";return Q.truncate-=X.length,`${X}${p9(Z,Q)}`}R(P7,"inspectClass");function N7(Z,Q){if(Z.length===0)return"Arguments[]";return Q.truncate-=13,`Arguments[ ${J9(Z,Q)} ]`}R(N7,"inspectArguments");var l5=["stack","line","column","name","message","fileName","lineNumber","columnNumber","number","description","cause"];function D7(Z,Q){let X=Object.getOwnPropertyNames(Z).filter((G)=>l5.indexOf(G)===-1),W=Z.name;Q.truncate-=W.length;let $="";if(typeof Z.message==="string")$=N9(Z.message,Q.truncate);else X.unshift("message");if($=$?`: ${$}`:"",Q.truncate-=$.length+5,Q.seen=Q.seen||[],Q.seen.includes(Z))return"[Circular]";Q.seen.push(Z);let J=J9(X.map((G)=>[G,Z[G]]),Q,h9);return`${W}${$}${J?` { ${J} }`:""}`}R(D7,"inspectObject");function F7([Z,Q],X){if(X.truncate-=3,!Q)return`${X.stylize(String(Z),"yellow")}`;return`${X.stylize(String(Z),"yellow")}=${X.stylize(`"${Q}"`,"string")}`}R(F7,"inspectAttribute");function U0(Z,Q){return J9(Z,Q,R7,` +`)}R(U0,"inspectNodeCollection");function R7(Z,Q){switch(Z.nodeType){case 1:return N1(Z,Q);case 3:return Q.inspect(Z.data,Q);default:return Q.inspect(Z,Q)}}R(R7,"inspectNode");function N1(Z,Q){let X=Z.getAttributeNames(),W=Z.tagName.toLowerCase(),$=Q.stylize(`<${W}`,"special"),J=Q.stylize(">","special"),G=Q.stylize(``,"special");Q.truncate-=W.length*2+5;let U="";if(X.length>0)U+=" ",U+=J9(X.map((K)=>[K,Z.getAttribute(K)]),Q,F7," ");Q.truncate-=U.length;let z=Q.truncate,Y=U0(Z.children,Q);if(Y&&Y.length>z)Y=`${v9}(${Z.children.length})`;return`${$}${U}${J}${Y}${G}`}R(N1,"inspectHTML");var p5=typeof Symbol==="function"&&typeof Symbol.for==="function",t0=p5?Symbol.for("chai/inspect"):"@@chai/inspect",e0=Symbol.for("nodejs.util.inspect.custom"),m6=new WeakMap,d6={},c6={undefined:R((Z,Q)=>Q.stylize("undefined","undefined"),"undefined"),null:R((Z,Q)=>Q.stylize("null","null"),"null"),boolean:R((Z,Q)=>Q.stylize(String(Z),"boolean"),"boolean"),Boolean:R((Z,Q)=>Q.stylize(String(Z),"boolean"),"Boolean"),number:Q1,Number:Q1,bigint:X1,BigInt:X1,string:W1,String:W1,function:Z1,Function:Z1,symbol:$1,Symbol:$1,Array:Y7,Date:U7,Map:B7,Set:q7,RegExp:V7,Promise:c5,WeakSet:R((Z,Q)=>Q.stylize("WeakSet{…}","special"),"WeakSet"),WeakMap:R((Z,Q)=>Q.stylize("WeakMap{…}","special"),"WeakMap"),Arguments:N7,Int8Array:q9,Uint8Array:q9,Uint8ClampedArray:q9,Int16Array:q9,Uint16Array:q9,Int32Array:q9,Uint32Array:q9,Float32Array:q9,Float64Array:q9,Generator:R(()=>"","Generator"),DataView:R(()=>"","DataView"),ArrayBuffer:R(()=>"","ArrayBuffer"),Error:D7,HTMLCollection:U0,NodeList:U0},i5=R((Z,Q,X,W)=>{if(t0 in Z&&typeof Z[t0]==="function")return Z[t0](Q);if(e0 in Z&&typeof Z[e0]==="function")return Z[e0](Q.depth,Q,W);if("inspect"in Z&&typeof Z.inspect==="function")return Z.inspect(Q.depth,Q);if("constructor"in Z&&m6.has(Z.constructor))return m6.get(Z.constructor)(Z,Q);if(d6[X])return d6[X](Z,Q);return""},"inspectCustom"),n5=Object.prototype.toString;function i9(Z,Q={}){let X=$7(Q,i9),{customInspect:W}=X,$=Z===null?"null":typeof Z;if($==="object")$=n5.call(Z).slice(8,-1);if($ in c6)return c6[$](Z,X);if(W&&Z){let G=i5(Z,X,$,i9);if(G){if(typeof G==="string")return G;return i9(G,X)}}let J=Z?Object.getPrototypeOf(Z):!1;if(J===Object.prototype||J===null)return p9(Z,X);if(Z&&typeof HTMLElement==="function"&&Z instanceof HTMLElement)return N1(Z,X);if("constructor"in Z){if(Z.constructor!==Object)return P7(Z,X);return p9(Z,X)}if(Z===Object(Z))return p9(Z,X);return X.stylize(String(Z),$)}R(i9,"inspect");var e={includeStack:!1,showDiff:!0,truncateThreshold:40,useProxy:!0,proxyExcludedKeys:["then","catch","inspect","toJSON"],deepEqual:null};function j(Z,Q,X,W){let $={colors:W,depth:typeof X>"u"?2:X,showHidden:Q,truncate:e.truncateThreshold?e.truncateThreshold:1/0};return i9(Z,$)}R(j,"inspect");function k9(Z){let Q=j(Z),X=Object.prototype.toString.call(Z);if(e.truncateThreshold&&Q.length>=e.truncateThreshold)if(X==="[object Function]")return!Z.name||Z.name===""?"[Function]":"[Function: "+Z.name+"]";else if(X==="[object Array]")return"[ Array("+Z.length+") ]";else if(X==="[object Object]"){let W=Object.keys(Z);return"{ Object ("+(W.length>2?W.splice(0,2).join(", ")+", ...":W.join(", "))+") }"}else return Q;else return Q}R(k9,"objDisplay");function D1(Z,Q){let X=k(Z,"negate"),W=k(Z,"object"),$=Q[3],J=H0(Z,Q),G=X?Q[2]:Q[1],U=k(Z,"message");if(typeof G==="function")G=G();return G=G||"",G=G.replace(/#\{this\}/g,function(){return k9(W)}).replace(/#\{act\}/g,function(){return k9(J)}).replace(/#\{exp\}/g,function(){return k9($)}),U?U+": "+G:G}R(D1,"getMessage");function K9(Z,Q,X){let W=Z.__flags||(Z.__flags=Object.create(null));if(!Q.__flags)Q.__flags=Object.create(null);X=arguments.length===3?X:!0;for(let $ in W)if(X||$!=="object"&&$!=="ssfi"&&$!=="lockSsfi"&&$!="message")Q.__flags[$]=W[$]}R(K9,"transferFlags");function J1(Z){if(typeof Z>"u")return"undefined";if(Z===null)return"null";let Q=Z[Symbol.toStringTag];if(typeof Q==="string")return Q;let X=8,W=-1;return Object.prototype.toString.call(Z).slice(X,W)}R(J1,"type");function F1(){this._key="chai/deep-eql__"+Math.random()+Date.now()}R(F1,"FakeMap");F1.prototype={get:R(function(Q){return Q[this._key]},"get"),set:R(function(Q,X){if(Object.isExtensible(Q))Object.defineProperty(Q,this._key,{value:X,configurable:!0})},"set")};var M7=typeof WeakMap==="function"?WeakMap:F1;function G1(Z,Q,X){if(!X||S9(Z)||S9(Q))return null;var W=X.get(Z);if(W){var $=W.get(Q);if(typeof $==="boolean")return $}return null}R(G1,"memoizeCompare");function l9(Z,Q,X,W){if(!X||S9(Z)||S9(Q))return;var $=X.get(Z);if($)$.set(Q,W);else $=new M7,$.set(Q,W),X.set(Z,$)}R(l9,"memoizeSet");var O7=n9;function n9(Z,Q,X){if(X&&X.comparator)return Y1(Z,Q,X);var W=R1(Z,Q);if(W!==null)return W;return Y1(Z,Q,X)}R(n9,"deepEqual");function R1(Z,Q){if(Z===Q)return Z!==0||1/Z===1/Q;if(Z!==Z&&Q!==Q)return!0;if(S9(Z)||S9(Q))return!1;return null}R(R1,"simpleEqual");function Y1(Z,Q,X){X=X||{},X.memoize=X.memoize===!1?!1:X.memoize||new M7;var W=X&&X.comparator,$=G1(Z,Q,X.memoize);if($!==null)return $;var J=G1(Q,Z,X.memoize);if(J!==null)return J;if(W){var G=W(Z,Q);if(G===!1||G===!0)return l9(Z,Q,X.memoize,G),G;var U=R1(Z,Q);if(U!==null)return U}var z=J1(Z);if(z!==J1(Q))return l9(Z,Q,X.memoize,!1),!1;l9(Z,Q,X.memoize,!0);var Y=I7(Z,Q,z,X);return l9(Z,Q,X.memoize,Y),Y}R(Y1,"extensiveDeepEqual");function I7(Z,Q,X,W){switch(X){case"String":case"Number":case"Boolean":case"Date":return n9(Z.valueOf(),Q.valueOf());case"Promise":case"Symbol":case"function":case"WeakMap":case"WeakSet":return Z===Q;case"Error":return M1(Z,Q,["name","message","code"],W);case"Arguments":case"Int8Array":case"Uint8Array":case"Uint8ClampedArray":case"Int16Array":case"Uint16Array":case"Int32Array":case"Uint32Array":case"Float32Array":case"Float64Array":case"Array":return O9(Z,Q,W);case"RegExp":return L7(Z,Q);case"Generator":return C7(Z,Q,W);case"DataView":return O9(new Uint8Array(Z.buffer),new Uint8Array(Q.buffer),W);case"ArrayBuffer":return O9(new Uint8Array(Z),new Uint8Array(Q),W);case"Set":return U1(Z,Q,W);case"Map":return U1(Z,Q,W);case"Temporal.PlainDate":case"Temporal.PlainTime":case"Temporal.PlainDateTime":case"Temporal.Instant":case"Temporal.ZonedDateTime":case"Temporal.PlainYearMonth":case"Temporal.PlainMonthDay":return Z.equals(Q);case"Temporal.Duration":return Z.total("nanoseconds")===Q.total("nanoseconds");case"Temporal.TimeZone":case"Temporal.Calendar":return Z.toString()===Q.toString();default:return A7(Z,Q,W)}}R(I7,"extensiveDeepEqualByType");function L7(Z,Q){return Z.toString()===Q.toString()}R(L7,"regexpEqual");function U1(Z,Q,X){try{if(Z.size!==Q.size)return!1;if(Z.size===0)return!0}catch(J){return!1}var W=[],$=[];return Z.forEach(R(function(G,U){W.push([G,U])},"gatherEntries")),Q.forEach(R(function(G,U){$.push([G,U])},"gatherEntries")),O9(W.sort(),$.sort(),X)}R(U1,"entriesEqual");function O9(Z,Q,X){var W=Z.length;if(W!==Q.length)return!1;if(W===0)return!0;var $=-1;while(++$"u"||Z===null)return!1;return Q in Object(Z)}R(P0,"hasProperty");function k7(Z){return Z.replace(/([^\\])\[/g,"$1.[").match(/(\\\.|[^.]+?)+/g).map((W)=>{if(W==="constructor"||W==="__proto__"||W==="prototype")return{};let J=/^\[(\d+)\]$/.exec(W),G=null;if(J)G={i:parseFloat(J[1])};else G={p:W.replace(/\\([.[\]])/g,"$1")};return G})}R(k7,"parsePath");function w1(Z,Q,X){let W=Z,$=null;X=typeof X>"u"?Q.length:X;for(let J=0;J"u")W=W[G.i];else W=W[G.p];if(J===X-1)$=W}}return $}R(w1,"internalGetPathValue");function O1(Z,Q){let X=k7(Q),W=X[X.length-1],$={parent:X.length>1?w1(Z,X,X.length-1):Z,name:W.p||W.i,value:w1(Z,X)};return $.exists=P0($.parent,$.name),$}R(O1,"getPathInfo");var S7=class Z{constructor(Q,X,W,$){return o6(this,"__flags",{}),k(this,"ssfi",W||Z),k(this,"lockSsfi",$),k(this,"object",Q),k(this,"message",X),k(this,"eql",e.deepEqual||O7),y9(this)}static get includeStack(){return console.warn("Assertion.includeStack is deprecated, use chai.config.includeStack instead."),e.includeStack}static set includeStack(Q){console.warn("Assertion.includeStack is deprecated, use chai.config.includeStack instead."),e.includeStack=Q}static get showDiff(){return console.warn("Assertion.showDiff is deprecated, use chai.config.showDiff instead."),e.showDiff}static set showDiff(Q){console.warn("Assertion.showDiff is deprecated, use chai.config.showDiff instead."),e.showDiff=Q}static addProperty(Q,X){L1(this.prototype,Q,X)}static addMethod(Q,X){C1(this.prototype,Q,X)}static addChainableMethod(Q,X,W){k1(this.prototype,Q,X,W)}static overwriteProperty(Q,X){T1(this.prototype,Q,X)}static overwriteMethod(Q,X){A1(this.prototype,Q,X)}static overwriteChainableMethod(Q,X,W){S1(this.prototype,Q,X,W)}assert(Q,X,W,$,J,G){let U=P1(this,arguments);if(G!==!1)G=!0;if($===void 0&&J===void 0)G=!1;if(e.showDiff!==!0)G=!1;if(!U){X=D1(this,arguments);let Y={actual:H0(this,arguments),expected:$,showDiff:G},K=E1(this,arguments);if(K)Y.operator=K;throw new _(X,Y,e.includeStack?this.assert:k(this,"ssfi"))}}get _obj(){return k(this,"object")}set _obj(Q){k(this,"object",Q)}};R(S7,"Assertion");var q=S7,N0=new EventTarget,j7=class extends Event{constructor(Q,X,W){super(Q);this.name=String(X),this.fn=W}};R(j7,"PluginEvent");var I1=j7;function o9(){return e.useProxy&&typeof Proxy<"u"&&typeof Reflect<"u"}R(o9,"isProxyEnabled");function L1(Z,Q,X){X=X===void 0?function(){}:X,Object.defineProperty(Z,Q,{get:R(function W(){if(!o9()&&!k(this,"lockSsfi"))k(this,"ssfi",W);let $=X.call(this);if($!==void 0)return $;let J=new q;return K9(this,J),J},"propertyGetter"),configurable:!0}),N0.dispatchEvent(new I1("addProperty",Q,X))}R(L1,"addProperty");var o5=Object.getOwnPropertyDescriptor(function(){},"length");function r9(Z,Q,X){if(!o5.configurable)return Z;return Object.defineProperty(Z,"length",{get:R(function(){if(X)throw Error("Invalid Chai property: "+Q+'.length. Due to a compatibility issue, "length" cannot directly follow "'+Q+'". Use "'+Q+'.lengthOf" instead.');throw Error("Invalid Chai property: "+Q+'.length. See docs for proper usage of "'+Q+'".')},"get")}),Z}R(r9,"addLengthGuard");function _7(Z){let Q=Object.getOwnPropertyNames(Z);function X($){if(Q.indexOf($)===-1)Q.push($)}R(X,"addProperty");let W=Object.getPrototypeOf(Z);while(W!==null)Object.getOwnPropertyNames(W).forEach(X),W=Object.getPrototypeOf(W);return Q}R(_7,"getProperties");var l6=["__flags","__methods","_obj","assert"];function y9(Z,Q){if(!o9())return Z;return new Proxy(Z,{get:R(function X(W,$){if(typeof $==="string"&&e.proxyExcludedKeys.indexOf($)===-1&&!Reflect.has(W,$)){if(Q)throw Error("Invalid Chai property: "+Q+"."+$+'. See docs for proper usage of "'+Q+'".');let J=null,G=4;if(_7(W).forEach(function(U){if(!Object.prototype.hasOwnProperty(U)&&l6.indexOf(U)===-1){let z=E7($,U,G);if(z=X)return X;let W=[];for(let $=0;$<=Z.length;$++)W[$]=Array(Q.length+1).fill(0),W[$][0]=$;for(let $=0;$=X){W[$][G]=X;continue}W[$][G]=Math.min(W[$-1][G]+1,W[$][G-1]+1,W[$-1][G-1]+(J===Q.charCodeAt(G-1)?0:1))}}return W[Z.length][Q.length]}R(E7,"stringDistanceCapped");function C1(Z,Q,X){let W=R(function(){if(!k(this,"lockSsfi"))k(this,"ssfi",W);let $=X.apply(this,arguments);if($!==void 0)return $;let J=new q;return K9(this,J),J},"methodWrapper");r9(W,Q,!1),Z[Q]=y9(W,Q),N0.dispatchEvent(new I1("addMethod",Q,X))}R(C1,"addMethod");function T1(Z,Q,X){let W=Object.getOwnPropertyDescriptor(Z,Q),$=R(function(){},"_super");if(W&&typeof W.get==="function")$=W.get;Object.defineProperty(Z,Q,{get:R(function J(){if(!o9()&&!k(this,"lockSsfi"))k(this,"ssfi",J);let G=k(this,"lockSsfi");k(this,"lockSsfi",!0);let U=X($).call(this);if(k(this,"lockSsfi",G),U!==void 0)return U;let z=new q;return K9(this,z),z},"overwritingPropertyGetter"),configurable:!0})}R(T1,"overwriteProperty");function A1(Z,Q,X){let W=Z[Q],$=R(function(){throw Error(Q+" is not a function")},"_super");if(W&&typeof W==="function")$=W;let J=R(function(){if(!k(this,"lockSsfi"))k(this,"ssfi",J);let G=k(this,"lockSsfi");k(this,"lockSsfi",!0);let U=X($).apply(this,arguments);if(k(this,"lockSsfi",G),U!==void 0)return U;let z=new q;return K9(this,z),z},"overwritingMethodWrapper");r9(J,Q,!1),Z[Q]=y9(J,Q)}R(A1,"overwriteMethod");var r5=typeof Object.setPrototypeOf==="function",p6=R(function(){},"testFn"),a5=Object.getOwnPropertyNames(p6).filter(function(Z){let Q=Object.getOwnPropertyDescriptor(p6,Z);if(typeof Q!=="object")return!0;return!Q.configurable}),s5=Function.prototype.call,t5=Function.prototype.apply,x7=class extends I1{constructor(Q,X,W,$){super(Q,X,W);this.chainingBehavior=$}};R(x7,"PluginAddChainableMethodEvent");var e5=x7;function k1(Z,Q,X,W){if(typeof W!=="function")W=R(function(){},"chainingBehavior");let $={method:X,chainingBehavior:W};if(!Z.__methods)Z.__methods={};Z.__methods[Q]=$,Object.defineProperty(Z,Q,{get:R(function(){$.chainingBehavior.call(this);let G=R(function(){if(!k(this,"lockSsfi"))k(this,"ssfi",G);let U=$.method.apply(this,arguments);if(U!==void 0)return U;let z=new q;return K9(this,z),z},"chainableMethodWrapper");if(r9(G,Q,!0),r5){let U=Object.create(this);U.call=s5,U.apply=t5,Object.setPrototypeOf(G,U)}else Object.getOwnPropertyNames(Z).forEach(function(z){if(a5.indexOf(z)!==-1)return;let Y=Object.getOwnPropertyDescriptor(Z,z);Object.defineProperty(G,z,Y)});return K9(this,G),y9(G)},"chainableMethodGetter"),configurable:!0}),N0.dispatchEvent(new e5("addChainableMethod",Q,X,W))}R(k1,"addChainableMethod");function S1(Z,Q,X,W){let $=Z.__methods[Q],J=$.chainingBehavior;$.chainingBehavior=R(function(){let z=W(J).call(this);if(z!==void 0)return z;let Y=new q;return K9(this,Y),Y},"overwritingChainableMethodGetter");let G=$.method;$.method=R(function(){let z=X(G).apply(this,arguments);if(z!==void 0)return z;let Y=new q;return K9(this,Y),Y},"overwritingChainableMethodWrapper")}R(S1,"overwriteChainableMethod");function K0(Z,Q){return j(Z)1&&w===B.length)throw P;return}}this.assert(K,"expected #{this} to "+z+"include "+j(Z),"expected #{this} to not "+z+"include "+j(Z))}R(s9,"include");q.addChainableMethod("include",s9,a9);q.addChainableMethod("contain",s9,a9);q.addChainableMethod("contains",s9,a9);q.addChainableMethod("includes",s9,a9);q.addProperty("ok",function(){this.assert(D(this,"object"),"expected #{this} to be truthy","expected #{this} to be falsy")});q.addProperty("true",function(){this.assert(D(this,"object")===!0,"expected #{this} to be true","expected #{this} to be false",D(this,"negate")?!1:!0)});q.addProperty("numeric",function(){let Z=D(this,"object");this.assert(["Number","BigInt"].includes(h(Z)),"expected #{this} to be numeric","expected #{this} to not be numeric",D(this,"negate")?!1:!0)});q.addProperty("callable",function(){let Z=D(this,"object"),Q=D(this,"ssfi"),X=D(this,"message"),W=X?`${X}: `:"",$=D(this,"negate"),J=$?`${W}expected ${j(Z)} not to be a callable function`:`${W}expected ${j(Z)} to be a callable function`,G=["Function","AsyncFunction","GeneratorFunction","AsyncGeneratorFunction"].includes(h(Z));if(G&&$||!G&&!$)throw new _(J,void 0,Q)});q.addProperty("false",function(){this.assert(D(this,"object")===!1,"expected #{this} to be false","expected #{this} to be true",D(this,"negate")?!0:!1)});q.addProperty("null",function(){this.assert(D(this,"object")===null,"expected #{this} to be null","expected #{this} not to be null")});q.addProperty("undefined",function(){this.assert(D(this,"object")===void 0,"expected #{this} to be undefined","expected #{this} not to be undefined")});q.addProperty("NaN",function(){this.assert(B0(D(this,"object")),"expected #{this} to be NaN","expected #{this} not to be NaN")});function v1(){let Z=D(this,"object");this.assert(Z!==null&&Z!==void 0,"expected #{this} to exist","expected #{this} to not exist")}R(v1,"assertExist");q.addProperty("exist",v1);q.addProperty("exists",v1);q.addProperty("empty",function(){let Z=D(this,"object"),Q=D(this,"ssfi"),X=D(this,"message"),W;switch(X=X?X+": ":"",h(Z).toLowerCase()){case"array":case"string":W=Z.length;break;case"map":case"set":W=Z.size;break;case"weakmap":case"weakset":throw new _(X+".empty was passed a weak collection",void 0,Q);case"function":{let $=X+".empty was passed a function "+D0(Z);throw new _($.trim(),void 0,Q)}default:if(Z!==Object(Z))throw new _(X+".empty was passed non-string primitive "+j(Z),void 0,Q);W=Object.keys(Z).length}this.assert(W===0,"expected #{this} to be empty","expected #{this} not to be empty")});function h1(){let Z=D(this,"object"),Q=h(Z);this.assert(Q==="Arguments","expected #{this} to be arguments but got "+Q,"expected #{this} to not be arguments")}R(h1,"checkArguments");q.addProperty("arguments",h1);q.addProperty("Arguments",h1);function F0(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object");if(D(this,"deep")){let W=D(this,"lockSsfi");D(this,"lockSsfi",!0),this.eql(Z),D(this,"lockSsfi",W)}else this.assert(Z===X,"expected #{this} to equal #{exp}","expected #{this} to not equal #{exp}",Z,this._obj,!0)}R(F0,"assertEqual");q.addMethod("equal",F0);q.addMethod("equals",F0);q.addMethod("eq",F0);function y1(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"eql");this.assert(X(Z,D(this,"object")),"expected #{this} to deeply equal #{exp}","expected #{this} to not deeply equal #{exp}",Z,this._obj,!0)}R(y1,"assertEql");q.addMethod("eql",y1);q.addMethod("eqls",y1);function R0(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=D(this,"doLength"),$=D(this,"message"),J=$?$+": ":"",G=D(this,"ssfi"),U=h(X).toLowerCase(),z=h(Z).toLowerCase();if(W&&U!=="map"&&U!=="set")new q(X,$,G,!0).to.have.property("length");if(!W&&U==="date"&&z!=="date")throw new _(J+"the argument to above must be a date",void 0,G);else if(!l(Z)&&(W||l(X)))throw new _(J+"the argument to above must be a number",void 0,G);else if(!W&&U!=="date"&&!l(X)){let Y=U==="string"?"'"+X+"'":X;throw new _(J+"expected "+Y+" to be a number or a date",void 0,G)}if(W){let Y="length",K;if(U==="map"||U==="set")Y="size",K=X.size;else K=X.length;this.assert(K>Z,"expected #{this} to have a "+Y+" above #{exp} but got #{act}","expected #{this} to not have a "+Y+" above #{exp}",Z,K)}else this.assert(X>Z,"expected #{this} to be above #{exp}","expected #{this} to be at most #{exp}",Z)}R(R0,"assertAbove");q.addMethod("above",R0);q.addMethod("gt",R0);q.addMethod("greaterThan",R0);function M0(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=D(this,"doLength"),$=D(this,"message"),J=$?$+": ":"",G=D(this,"ssfi"),U=h(X).toLowerCase(),z=h(Z).toLowerCase(),Y,K=!0;if(W&&U!=="map"&&U!=="set")new q(X,$,G,!0).to.have.property("length");if(!W&&U==="date"&&z!=="date")Y=J+"the argument to least must be a date";else if(!l(Z)&&(W||l(X)))Y=J+"the argument to least must be a number";else if(!W&&U!=="date"&&!l(X)){let B=U==="string"?"'"+X+"'":X;Y=J+"expected "+B+" to be a number or a date"}else K=!1;if(K)throw new _(Y,void 0,G);if(W){let B="length",P;if(U==="map"||U==="set")B="size",P=X.size;else P=X.length;this.assert(P>=Z,"expected #{this} to have a "+B+" at least #{exp} but got #{act}","expected #{this} to have a "+B+" below #{exp}",Z,P)}else this.assert(X>=Z,"expected #{this} to be at least #{exp}","expected #{this} to be below #{exp}",Z)}R(M0,"assertLeast");q.addMethod("least",M0);q.addMethod("gte",M0);q.addMethod("greaterThanOrEqual",M0);function O0(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=D(this,"doLength"),$=D(this,"message"),J=$?$+": ":"",G=D(this,"ssfi"),U=h(X).toLowerCase(),z=h(Z).toLowerCase(),Y,K=!0;if(W&&U!=="map"&&U!=="set")new q(X,$,G,!0).to.have.property("length");if(!W&&U==="date"&&z!=="date")Y=J+"the argument to below must be a date";else if(!l(Z)&&(W||l(X)))Y=J+"the argument to below must be a number";else if(!W&&U!=="date"&&!l(X)){let B=U==="string"?"'"+X+"'":X;Y=J+"expected "+B+" to be a number or a date"}else K=!1;if(K)throw new _(Y,void 0,G);if(W){let B="length",P;if(U==="map"||U==="set")B="size",P=X.size;else P=X.length;this.assert(P=Z&&H<=Q,"expected #{this} to have a "+N+" within "+w,"expected #{this} to not have a "+N+" within "+w)}else this.assert(W>=Z&&W<=Q,"expected #{this} to be within "+w,"expected #{this} to not be within "+w)});function b1(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=D(this,"ssfi"),$=D(this,"message"),J;try{J=X instanceof Z}catch(U){if(U instanceof TypeError)throw $=$?$+": ":"",new _($+"The instanceof assertion needs a constructor but "+h(Z)+" was given.",void 0,W);throw U}let G=D0(Z);if(G==null)G="an unnamed constructor";this.assert(J,"expected #{this} to be an instance of "+G,"expected #{this} to not be an instance of "+G)}R(b1,"assertInstanceOf");q.addMethod("instanceof",b1);q.addMethod("instanceOf",b1);function u1(Z,Q,X){if(X)D(this,"message",X);let W=D(this,"nested"),$=D(this,"own"),J=D(this,"message"),G=D(this,"object"),U=D(this,"ssfi"),z=typeof Z;if(J=J?J+": ":"",W){if(z!=="string")throw new _(J+"the argument to property must be a string when using nested syntax",void 0,U)}else if(z!=="string"&&z!=="number"&&z!=="symbol")throw new _(J+"the argument to property must be a string, number, or symbol",void 0,U);if(W&&$)throw new _(J+'The "nested" and "own" flags cannot be combined.',void 0,U);if(G===null||G===void 0)throw new _(J+"Target cannot be null or undefined.",void 0,U);let Y=D(this,"deep"),K=D(this,"negate"),B=W?O1(G,Z):null,P=W?B.value:G[Z],w=Y?D(this,"eql"):(F,M)=>F===M,N="";if(Y)N+="deep ";if($)N+="own ";if(W)N+="nested ";N+="property ";let H;if($)H=Object.prototype.hasOwnProperty.call(G,Z);else if(W)H=B.exists;else H=P0(G,Z);if(!K||arguments.length===1)this.assert(H,"expected #{this} to have "+N+j(Z),"expected #{this} to not have "+N+j(Z));if(arguments.length>1)this.assert(H&&w(Q,P),"expected #{this} to have "+N+j(Z)+" of #{exp}, but got #{act}","expected #{this} to not have "+N+j(Z)+" of #{act}",Q,P);D(this,"object",P)}R(u1,"assertProperty");q.addMethod("property",u1);function f1(Z,Q,X){D(this,"own",!0),u1.apply(this,arguments)}R(f1,"assertOwnProperty");q.addMethod("ownProperty",f1);q.addMethod("haveOwnProperty",f1);function g1(Z,Q,X){if(typeof Q==="string")X=Q,Q=null;if(X)D(this,"message",X);let W=D(this,"object"),$=Object.getOwnPropertyDescriptor(Object(W),Z),J=D(this,"eql");if($&&Q)this.assert(J(Q,$),"expected the own property descriptor for "+j(Z)+" on #{this} to match "+j(Q)+", got "+j($),"expected the own property descriptor for "+j(Z)+" on #{this} to not match "+j(Q),Q,$,!0);else this.assert($,"expected #{this} to have an own property descriptor for "+j(Z),"expected #{this} to not have an own property descriptor for "+j(Z));D(this,"object",$)}R(g1,"assertOwnPropertyDescriptor");q.addMethod("ownPropertyDescriptor",g1);q.addMethod("haveOwnPropertyDescriptor",g1);function m1(){D(this,"doLength",!0)}R(m1,"assertLengthChain");function d1(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=h(X).toLowerCase(),$=D(this,"message"),J=D(this,"ssfi"),G="length",U;switch(W){case"map":case"set":G="size",U=X.size;break;default:new q(X,$,J,!0).to.have.property("length"),U=X.length}this.assert(U==Z,"expected #{this} to have a "+G+" of #{exp} but got #{act}","expected #{this} to not have a "+G+" of #{act}",Z,U)}R(d1,"assertLength");q.addChainableMethod("length",d1,m1);q.addChainableMethod("lengthOf",d1,m1);function c1(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object");this.assert(Z.exec(X),"expected #{this} to match "+Z,"expected #{this} not to match "+Z)}R(c1,"assertMatch");q.addMethod("match",c1);q.addMethod("matches",c1);q.addMethod("string",function(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=D(this,"message"),$=D(this,"ssfi");new q(X,W,$,!0).is.a("string"),this.assert(~X.indexOf(Z),"expected #{this} to contain "+j(Z),"expected #{this} to not contain "+j(Z))});function l1(Z){let Q=D(this,"object"),X=h(Q),W=h(Z),$=D(this,"ssfi"),J=D(this,"deep"),G,U="",z,Y=!0,K=D(this,"message");K=K?K+": ":"";let B=K+"when testing keys against an object or an array you must give a single Array|Object|String argument or multiple String arguments";if(X==="Map"||X==="Set"){if(U=J?"deeply ":"",z=[],Q.forEach(function(M,O){z.push(O)}),W!=="Array")Z=Array.prototype.slice.call(arguments)}else{switch(z=_1(Q),W){case"Array":if(arguments.length>1)throw new _(B,void 0,$);break;case"Object":if(arguments.length>1)throw new _(B,void 0,$);Z=Object.keys(Z);break;default:Z=Array.prototype.slice.call(arguments)}Z=Z.map(function(M){return typeof M==="symbol"?M:String(M)})}if(!Z.length)throw new _(K+"keys required",void 0,$);let P=Z.length,w=D(this,"any"),N=D(this,"all"),H=Z,F=J?D(this,"eql"):(M,O)=>M===O;if(!w&&!N)N=!0;if(w)Y=H.some(function(M){return z.some(function(O){return F(M,O)})});if(N){if(Y=H.every(function(M){return z.some(function(O){return F(M,O)})}),!D(this,"contains"))Y=Y&&Z.length==z.length}if(P>1){Z=Z.map(function(O){return j(O)});let M=Z.pop();if(N)G=Z.join(", ")+", and "+M;if(w)G=Z.join(", ")+", or "+M}else G=j(Z[0]);G=(P>1?"keys ":"key ")+G,G=(D(this,"contains")?"contain ":"have ")+G,this.assert(Y,"expected #{this} to "+U+G,"expected #{this} to not "+U+G,H.slice(0).sort(K0),z.sort(K0),!0)}R(l1,"assertKeys");q.addMethod("keys",l1);q.addMethod("key",l1);function L0(Z,Q,X){if(X)D(this,"message",X);let W=D(this,"object"),$=D(this,"ssfi"),J=D(this,"message"),G=D(this,"negate")||!1;if(new q(W,J,$,!0).is.a("function"),V0(Z)||typeof Z==="string")Q=Z,Z=null;let U,z=!1;try{W()}catch(w){z=!0,U=w}let Y=Z===void 0&&Q===void 0,K=Boolean(Z&&Q),B=!1,P=!1;if(Y||!Y&&!G){let w="an error";if(Z instanceof Error)w="#{exp}";else if(Z)w=X9.getConstructorName(Z);let N=U;if(U instanceof Error)N=U.toString();else if(typeof U==="string")N=U;else if(U&&(typeof U==="object"||typeof U==="function"))try{N=X9.getConstructorName(U)}catch(H){}this.assert(z,"expected #{this} to throw "+w,"expected #{this} to not throw an error but #{act} was thrown",Z&&Z.toString(),N)}if(Z&&U){if(Z instanceof Error){if(X9.compatibleInstance(U,Z)===G)if(K&&G)B=!0;else this.assert(G,"expected #{this} to throw #{exp} but #{act} was thrown","expected #{this} to not throw #{exp}"+(U&&!G?" but #{act} was thrown":""),Z.toString(),U.toString())}if(X9.compatibleConstructor(U,Z)===G)if(K&&G)B=!0;else this.assert(G,"expected #{this} to throw #{exp} but #{act} was thrown","expected #{this} to not throw #{exp}"+(U?" but #{act} was thrown":""),Z instanceof Error?Z.toString():Z&&X9.getConstructorName(Z),U instanceof Error?U.toString():U&&X9.getConstructorName(U))}if(U&&Q!==void 0&&Q!==null){let w="including";if(V0(Q))w="matching";if(X9.compatibleMessage(U,Q)===G)if(K&&G)P=!0;else this.assert(G,"expected #{this} to throw error "+w+" #{exp} but got #{act}","expected #{this} to throw error not "+w+" #{exp}",Q,X9.getMessage(U))}if(B&&P)this.assert(G,"expected #{this} to throw #{exp} but #{act} was thrown","expected #{this} to not throw #{exp}"+(U?" but #{act} was thrown":""),Z instanceof Error?Z.toString():Z&&X9.getConstructorName(Z),U instanceof Error?U.toString():U&&X9.getConstructorName(U));D(this,"object",U)}R(L0,"assertThrows");q.addMethod("throw",L0);q.addMethod("throws",L0);q.addMethod("Throw",L0);function p1(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=D(this,"itself"),$=typeof X==="function"&&!W?X.prototype[Z]:X[Z];this.assert(typeof $==="function","expected #{this} to respond to "+j(Z),"expected #{this} to not respond to "+j(Z))}R(p1,"respondTo");q.addMethod("respondTo",p1);q.addMethod("respondsTo",p1);q.addProperty("itself",function(){D(this,"itself",!0)});function i1(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=Z(X);this.assert(W,"expected #{this} to satisfy "+k9(Z),"expected #{this} to not satisfy"+k9(Z),D(this,"negate")?!1:!0,W)}R(i1,"satisfy");q.addMethod("satisfy",i1);q.addMethod("satisfies",i1);function n1(Z,Q,X){if(X)D(this,"message",X);let W=D(this,"object"),$=D(this,"message"),J=D(this,"ssfi");new q(W,$,J,!0).is.numeric;let G="A `delta` value is required for `closeTo`";if(Q==null)throw new _($?`${$}: ${G}`:G,void 0,J);if(new q(Q,$,J,!0).is.numeric,G="A `expected` value is required for `closeTo`",Z==null)throw new _($?`${$}: ${G}`:G,void 0,J);new q(Z,$,J,!0).is.numeric;let U=R((Y)=>Y<0?-Y:Y,"abs"),z=R((Y)=>parseFloat(parseFloat(Y).toPrecision(12)),"strip");this.assert(z(U(W-Z))<=Q,"expected #{this} to be close to "+Z+" +/- "+Q,"expected #{this} not to be close to "+Z+" +/- "+Q)}R(n1,"closeTo");q.addMethod("closeTo",n1);q.addMethod("approximately",n1);function y7(Z,Q,X,W,$){let J=Array.from(Q),G=Array.from(Z);if(!W){if(G.length!==J.length)return!1;J=J.slice()}return G.every(function(U,z){if($)return X?X(U,J[z]):U===J[z];if(!X){let Y=J.indexOf(U);if(Y===-1)return!1;if(!W)J.splice(Y,1);return!0}return J.some(function(Y,K){if(!X(U,Y))return!1;if(!W)J.splice(K,1);return!0})})}R(y7,"isSubsetOf");q.addMethod("members",function(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=D(this,"message"),$=D(this,"ssfi");new q(X,W,$,!0).to.be.iterable,new q(Z,W,$,!0).to.be.iterable;let J=D(this,"contains"),G=D(this,"ordered"),U,z,Y;if(J)U=G?"an ordered superset":"a superset",z="expected #{this} to be "+U+" of #{exp}",Y="expected #{this} to not be "+U+" of #{exp}";else U=G?"ordered members":"members",z="expected #{this} to have the same "+U+" as #{exp}",Y="expected #{this} to not have the same "+U+" as #{exp}";let K=D(this,"deep")?D(this,"eql"):void 0;this.assert(y7(Z,X,K,J,G),z,Y,Z,X,!0)});q.addProperty("iterable",function(Z){if(Z)D(this,"message",Z);let Q=D(this,"object");this.assert(Q!=null&&Q[Symbol.iterator],"expected #{this} to be an iterable","expected #{this} to not be an iterable",Q)});function b7(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=D(this,"message"),$=D(this,"ssfi"),J=D(this,"contains"),G=D(this,"deep"),U=D(this,"eql");if(new q(Z,W,$,!0).to.be.an("array"),J)this.assert(Z.some(function(z){return X.indexOf(z)>-1}),"expected #{this} to contain one of #{exp}","expected #{this} to not contain one of #{exp}",Z,X);else if(G)this.assert(Z.some(function(z){return U(X,z)}),"expected #{this} to deeply equal one of #{exp}","expected #{this} to deeply equal one of #{exp}",Z,X);else this.assert(Z.indexOf(X)>-1,"expected #{this} to be one of #{exp}","expected #{this} to not be one of #{exp}",Z,X)}R(b7,"oneOf");q.addMethod("oneOf",b7);function o1(Z,Q,X){if(X)D(this,"message",X);let W=D(this,"object"),$=D(this,"message"),J=D(this,"ssfi");new q(W,$,J,!0).is.a("function");let G;if(!Q)new q(Z,$,J,!0).is.a("function"),G=Z();else new q(Z,$,J,!0).to.have.property(Q),G=Z[Q];W();let U=Q===void 0||Q===null?Z():Z[Q],z=Q===void 0||Q===null?G:"."+Q;D(this,"deltaMsgObj",z),D(this,"initialDeltaValue",G),D(this,"finalDeltaValue",U),D(this,"deltaBehavior","change"),D(this,"realDelta",U!==G),this.assert(G!==U,"expected "+z+" to change","expected "+z+" to not change")}R(o1,"assertChanges");q.addMethod("change",o1);q.addMethod("changes",o1);function r1(Z,Q,X){if(X)D(this,"message",X);let W=D(this,"object"),$=D(this,"message"),J=D(this,"ssfi");new q(W,$,J,!0).is.a("function");let G;if(!Q)new q(Z,$,J,!0).is.a("function"),G=Z();else new q(Z,$,J,!0).to.have.property(Q),G=Z[Q];new q(G,$,J,!0).is.a("number"),W();let U=Q===void 0||Q===null?Z():Z[Q],z=Q===void 0||Q===null?G:"."+Q;D(this,"deltaMsgObj",z),D(this,"initialDeltaValue",G),D(this,"finalDeltaValue",U),D(this,"deltaBehavior","increase"),D(this,"realDelta",U-G),this.assert(U-G>0,"expected "+z+" to increase","expected "+z+" to not increase")}R(r1,"assertIncreases");q.addMethod("increase",r1);q.addMethod("increases",r1);function a1(Z,Q,X){if(X)D(this,"message",X);let W=D(this,"object"),$=D(this,"message"),J=D(this,"ssfi");new q(W,$,J,!0).is.a("function");let G;if(!Q)new q(Z,$,J,!0).is.a("function"),G=Z();else new q(Z,$,J,!0).to.have.property(Q),G=Z[Q];new q(G,$,J,!0).is.a("number"),W();let U=Q===void 0||Q===null?Z():Z[Q],z=Q===void 0||Q===null?G:"."+Q;D(this,"deltaMsgObj",z),D(this,"initialDeltaValue",G),D(this,"finalDeltaValue",U),D(this,"deltaBehavior","decrease"),D(this,"realDelta",G-U),this.assert(U-G<0,"expected "+z+" to decrease","expected "+z+" to not decrease")}R(a1,"assertDecreases");q.addMethod("decrease",a1);q.addMethod("decreases",a1);function u7(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"deltaMsgObj"),W=D(this,"initialDeltaValue"),$=D(this,"finalDeltaValue"),J=D(this,"deltaBehavior"),G=D(this,"realDelta"),U;if(J==="change")U=Math.abs($-W)===Math.abs(Z);else U=G===Math.abs(Z);this.assert(U,"expected "+X+" to "+J+" by "+Z,"expected "+X+" to not "+J+" by "+Z)}R(u7,"assertDelta");q.addMethod("by",u7);q.addProperty("extensible",function(){let Z=D(this,"object"),Q=Z===Object(Z)&&Object.isExtensible(Z);this.assert(Q,"expected #{this} to be extensible","expected #{this} to not be extensible")});q.addProperty("sealed",function(){let Z=D(this,"object"),Q=Z===Object(Z)?Object.isSealed(Z):!0;this.assert(Q,"expected #{this} to be sealed","expected #{this} to not be sealed")});q.addProperty("frozen",function(){let Z=D(this,"object"),Q=Z===Object(Z)?Object.isFrozen(Z):!0;this.assert(Q,"expected #{this} to be frozen","expected #{this} to not be frozen")});q.addProperty("finite",function(Z){let Q=D(this,"object");this.assert(typeof Q==="number"&&isFinite(Q),"expected #{this} to be a finite number","expected #{this} to not be a finite number")});function w0(Z,Q){if(Z===Q)return!0;if(typeof Q!==typeof Z)return!1;if(typeof Z!=="object"||Z===null)return Z===Q;if(!Q)return!1;if(Array.isArray(Z)){if(!Array.isArray(Q))return!1;return Z.every(function(X){return Q.some(function(W){return w0(X,W)})})}if(Z instanceof Date)if(Q instanceof Date)return Z.getTime()===Q.getTime();else return!1;return Object.keys(Z).every(function(X){let W=Z[X],$=Q[X];if(typeof W==="object"&&W!==null&&$!==null)return w0(W,$);if(typeof W==="function")return W($);return $===W})}R(w0,"compareSubset");q.addMethod("containSubset",function(Z){let Q=k(this,"object"),X=e.showDiff;this.assert(w0(Z,Q),"expected #{act} to contain subset #{exp}","expected #{act} to not contain subset #{exp}",Z,Q,X)});function I9(Z,Q){return new q(Z,Q)}R(I9,"expect");I9.fail=function(Z,Q,X,W){if(arguments.length<2)X=Z,Z=void 0;throw X=X||"expect.fail()",new _(X,{actual:Z,expected:Q,operator:W},I9.fail)};var f7={};H1(f7,{Should:()=>QZ,should:()=>ZZ});function s1(){function Z(){if(this instanceof String||this instanceof Number||this instanceof Boolean||typeof Symbol==="function"&&this instanceof Symbol||typeof BigInt==="function"&&this instanceof BigInt)return new q(this.valueOf(),null,Z);return new q(this,null,Z)}R(Z,"shouldGetter");function Q(W){Object.defineProperty(this,"should",{value:W,enumerable:!0,configurable:!0,writable:!0})}R(Q,"shouldSetter"),Object.defineProperty(Object.prototype,"should",{set:Q,get:Z,configurable:!0});let X={};return X.fail=function(W,$,J,G){if(arguments.length<2)J=W,W=void 0;throw J=J||"should.fail()",new _(J,{actual:W,expected:$,operator:G},X.fail)},X.equal=function(W,$,J){new q(W,J).to.equal($)},X.Throw=function(W,$,J,G){new q(W,G).to.Throw($,J)},X.exist=function(W,$){new q(W,$).to.exist},X.not={},X.not.equal=function(W,$,J){new q(W,J).to.not.equal($)},X.not.Throw=function(W,$,J,G){new q(W,G).to.not.Throw($,J)},X.not.exist=function(W,$){new q(W,$).to.not.exist},X.throw=X.Throw,X.not.throw=X.not.Throw,X}R(s1,"loadShould");var ZZ=s1,QZ=s1;function V(Z,Q){new q(null,null,V,!0).assert(Z,Q,"[ negation message unavailable ]")}R(V,"assert");V.fail=function(Z,Q,X,W){if(arguments.length<2)X=Z,Z=void 0;throw X=X||"assert.fail()",new _(X,{actual:Z,expected:Q,operator:W},V.fail)};V.isOk=function(Z,Q){new q(Z,Q,V.isOk,!0).is.ok};V.isNotOk=function(Z,Q){new q(Z,Q,V.isNotOk,!0).is.not.ok};V.equal=function(Z,Q,X){let W=new q(Z,X,V.equal,!0);W.assert(Q==k(W,"object"),"expected #{this} to equal #{exp}","expected #{this} to not equal #{act}",Q,Z,!0)};V.notEqual=function(Z,Q,X){let W=new q(Z,X,V.notEqual,!0);W.assert(Q!=k(W,"object"),"expected #{this} to not equal #{exp}","expected #{this} to equal #{act}",Q,Z,!0)};V.strictEqual=function(Z,Q,X){new q(Z,X,V.strictEqual,!0).to.equal(Q)};V.notStrictEqual=function(Z,Q,X){new q(Z,X,V.notStrictEqual,!0).to.not.equal(Q)};V.deepEqual=V.deepStrictEqual=function(Z,Q,X){new q(Z,X,V.deepEqual,!0).to.eql(Q)};V.notDeepEqual=function(Z,Q,X){new q(Z,X,V.notDeepEqual,!0).to.not.eql(Q)};V.isAbove=function(Z,Q,X){new q(Z,X,V.isAbove,!0).to.be.above(Q)};V.isAtLeast=function(Z,Q,X){new q(Z,X,V.isAtLeast,!0).to.be.least(Q)};V.isBelow=function(Z,Q,X){new q(Z,X,V.isBelow,!0).to.be.below(Q)};V.isAtMost=function(Z,Q,X){new q(Z,X,V.isAtMost,!0).to.be.most(Q)};V.isTrue=function(Z,Q){new q(Z,Q,V.isTrue,!0).is.true};V.isNotTrue=function(Z,Q){new q(Z,Q,V.isNotTrue,!0).to.not.equal(!0)};V.isFalse=function(Z,Q){new q(Z,Q,V.isFalse,!0).is.false};V.isNotFalse=function(Z,Q){new q(Z,Q,V.isNotFalse,!0).to.not.equal(!1)};V.isNull=function(Z,Q){new q(Z,Q,V.isNull,!0).to.equal(null)};V.isNotNull=function(Z,Q){new q(Z,Q,V.isNotNull,!0).to.not.equal(null)};V.isNaN=function(Z,Q){new q(Z,Q,V.isNaN,!0).to.be.NaN};V.isNotNaN=function(Z,Q){new q(Z,Q,V.isNotNaN,!0).not.to.be.NaN};V.exists=function(Z,Q){new q(Z,Q,V.exists,!0).to.exist};V.notExists=function(Z,Q){new q(Z,Q,V.notExists,!0).to.not.exist};V.isUndefined=function(Z,Q){new q(Z,Q,V.isUndefined,!0).to.equal(void 0)};V.isDefined=function(Z,Q){new q(Z,Q,V.isDefined,!0).to.not.equal(void 0)};V.isCallable=function(Z,Q){new q(Z,Q,V.isCallable,!0).is.callable};V.isNotCallable=function(Z,Q){new q(Z,Q,V.isNotCallable,!0).is.not.callable};V.isObject=function(Z,Q){new q(Z,Q,V.isObject,!0).to.be.a("object")};V.isNotObject=function(Z,Q){new q(Z,Q,V.isNotObject,!0).to.not.be.a("object")};V.isArray=function(Z,Q){new q(Z,Q,V.isArray,!0).to.be.an("array")};V.isNotArray=function(Z,Q){new q(Z,Q,V.isNotArray,!0).to.not.be.an("array")};V.isString=function(Z,Q){new q(Z,Q,V.isString,!0).to.be.a("string")};V.isNotString=function(Z,Q){new q(Z,Q,V.isNotString,!0).to.not.be.a("string")};V.isNumber=function(Z,Q){new q(Z,Q,V.isNumber,!0).to.be.a("number")};V.isNotNumber=function(Z,Q){new q(Z,Q,V.isNotNumber,!0).to.not.be.a("number")};V.isNumeric=function(Z,Q){new q(Z,Q,V.isNumeric,!0).is.numeric};V.isNotNumeric=function(Z,Q){new q(Z,Q,V.isNotNumeric,!0).is.not.numeric};V.isFinite=function(Z,Q){new q(Z,Q,V.isFinite,!0).to.be.finite};V.isBoolean=function(Z,Q){new q(Z,Q,V.isBoolean,!0).to.be.a("boolean")};V.isNotBoolean=function(Z,Q){new q(Z,Q,V.isNotBoolean,!0).to.not.be.a("boolean")};V.typeOf=function(Z,Q,X){new q(Z,X,V.typeOf,!0).to.be.a(Q)};V.notTypeOf=function(Z,Q,X){new q(Z,X,V.notTypeOf,!0).to.not.be.a(Q)};V.instanceOf=function(Z,Q,X){new q(Z,X,V.instanceOf,!0).to.be.instanceOf(Q)};V.notInstanceOf=function(Z,Q,X){new q(Z,X,V.notInstanceOf,!0).to.not.be.instanceOf(Q)};V.include=function(Z,Q,X){new q(Z,X,V.include,!0).include(Q)};V.notInclude=function(Z,Q,X){new q(Z,X,V.notInclude,!0).not.include(Q)};V.deepInclude=function(Z,Q,X){new q(Z,X,V.deepInclude,!0).deep.include(Q)};V.notDeepInclude=function(Z,Q,X){new q(Z,X,V.notDeepInclude,!0).not.deep.include(Q)};V.nestedInclude=function(Z,Q,X){new q(Z,X,V.nestedInclude,!0).nested.include(Q)};V.notNestedInclude=function(Z,Q,X){new q(Z,X,V.notNestedInclude,!0).not.nested.include(Q)};V.deepNestedInclude=function(Z,Q,X){new q(Z,X,V.deepNestedInclude,!0).deep.nested.include(Q)};V.notDeepNestedInclude=function(Z,Q,X){new q(Z,X,V.notDeepNestedInclude,!0).not.deep.nested.include(Q)};V.ownInclude=function(Z,Q,X){new q(Z,X,V.ownInclude,!0).own.include(Q)};V.notOwnInclude=function(Z,Q,X){new q(Z,X,V.notOwnInclude,!0).not.own.include(Q)};V.deepOwnInclude=function(Z,Q,X){new q(Z,X,V.deepOwnInclude,!0).deep.own.include(Q)};V.notDeepOwnInclude=function(Z,Q,X){new q(Z,X,V.notDeepOwnInclude,!0).not.deep.own.include(Q)};V.match=function(Z,Q,X){new q(Z,X,V.match,!0).to.match(Q)};V.notMatch=function(Z,Q,X){new q(Z,X,V.notMatch,!0).to.not.match(Q)};V.property=function(Z,Q,X){new q(Z,X,V.property,!0).to.have.property(Q)};V.notProperty=function(Z,Q,X){new q(Z,X,V.notProperty,!0).to.not.have.property(Q)};V.propertyVal=function(Z,Q,X,W){new q(Z,W,V.propertyVal,!0).to.have.property(Q,X)};V.notPropertyVal=function(Z,Q,X,W){new q(Z,W,V.notPropertyVal,!0).to.not.have.property(Q,X)};V.deepPropertyVal=function(Z,Q,X,W){new q(Z,W,V.deepPropertyVal,!0).to.have.deep.property(Q,X)};V.notDeepPropertyVal=function(Z,Q,X,W){new q(Z,W,V.notDeepPropertyVal,!0).to.not.have.deep.property(Q,X)};V.ownProperty=function(Z,Q,X){new q(Z,X,V.ownProperty,!0).to.have.own.property(Q)};V.notOwnProperty=function(Z,Q,X){new q(Z,X,V.notOwnProperty,!0).to.not.have.own.property(Q)};V.ownPropertyVal=function(Z,Q,X,W){new q(Z,W,V.ownPropertyVal,!0).to.have.own.property(Q,X)};V.notOwnPropertyVal=function(Z,Q,X,W){new q(Z,W,V.notOwnPropertyVal,!0).to.not.have.own.property(Q,X)};V.deepOwnPropertyVal=function(Z,Q,X,W){new q(Z,W,V.deepOwnPropertyVal,!0).to.have.deep.own.property(Q,X)};V.notDeepOwnPropertyVal=function(Z,Q,X,W){new q(Z,W,V.notDeepOwnPropertyVal,!0).to.not.have.deep.own.property(Q,X)};V.nestedProperty=function(Z,Q,X){new q(Z,X,V.nestedProperty,!0).to.have.nested.property(Q)};V.notNestedProperty=function(Z,Q,X){new q(Z,X,V.notNestedProperty,!0).to.not.have.nested.property(Q)};V.nestedPropertyVal=function(Z,Q,X,W){new q(Z,W,V.nestedPropertyVal,!0).to.have.nested.property(Q,X)};V.notNestedPropertyVal=function(Z,Q,X,W){new q(Z,W,V.notNestedPropertyVal,!0).to.not.have.nested.property(Q,X)};V.deepNestedPropertyVal=function(Z,Q,X,W){new q(Z,W,V.deepNestedPropertyVal,!0).to.have.deep.nested.property(Q,X)};V.notDeepNestedPropertyVal=function(Z,Q,X,W){new q(Z,W,V.notDeepNestedPropertyVal,!0).to.not.have.deep.nested.property(Q,X)};V.lengthOf=function(Z,Q,X){new q(Z,X,V.lengthOf,!0).to.have.lengthOf(Q)};V.hasAnyKeys=function(Z,Q,X){new q(Z,X,V.hasAnyKeys,!0).to.have.any.keys(Q)};V.hasAllKeys=function(Z,Q,X){new q(Z,X,V.hasAllKeys,!0).to.have.all.keys(Q)};V.containsAllKeys=function(Z,Q,X){new q(Z,X,V.containsAllKeys,!0).to.contain.all.keys(Q)};V.doesNotHaveAnyKeys=function(Z,Q,X){new q(Z,X,V.doesNotHaveAnyKeys,!0).to.not.have.any.keys(Q)};V.doesNotHaveAllKeys=function(Z,Q,X){new q(Z,X,V.doesNotHaveAllKeys,!0).to.not.have.all.keys(Q)};V.hasAnyDeepKeys=function(Z,Q,X){new q(Z,X,V.hasAnyDeepKeys,!0).to.have.any.deep.keys(Q)};V.hasAllDeepKeys=function(Z,Q,X){new q(Z,X,V.hasAllDeepKeys,!0).to.have.all.deep.keys(Q)};V.containsAllDeepKeys=function(Z,Q,X){new q(Z,X,V.containsAllDeepKeys,!0).to.contain.all.deep.keys(Q)};V.doesNotHaveAnyDeepKeys=function(Z,Q,X){new q(Z,X,V.doesNotHaveAnyDeepKeys,!0).to.not.have.any.deep.keys(Q)};V.doesNotHaveAllDeepKeys=function(Z,Q,X){new q(Z,X,V.doesNotHaveAllDeepKeys,!0).to.not.have.all.deep.keys(Q)};V.throws=function(Z,Q,X,W){if(typeof Q==="string"||Q instanceof RegExp)X=Q,Q=null;let $=new q(Z,W,V.throws,!0).to.throw(Q,X);return k($,"object")};V.doesNotThrow=function(Z,Q,X,W){if(typeof Q==="string"||Q instanceof RegExp)X=Q,Q=null;new q(Z,W,V.doesNotThrow,!0).to.not.throw(Q,X)};V.operator=function(Z,Q,X,W){let $;switch(Q){case"==":$=Z==X;break;case"===":$=Z===X;break;case">":$=Z>X;break;case">=":$=Z>=X;break;case"<":$=ZY instanceof DataView,z=U(Z)?Z.byteLength:Z.length;if(z>0){G+=Q.spacingOuter;let Y=X+Q.indent;for(let K=0;K0){U+=Q.spacingOuter;let Y=X+Q.indent;for(let K=0;K{let G=Z.toString();if(G==="ArrayContaining"||G==="ArrayNotContaining"){if(++W>Q.maxDepth)return`[${G}]`;return`${G+C0}[${k0(Z.sample,Q,X,W,$,J)}]`}if(G==="ObjectContaining"||G==="ObjectNotContaining"){if(++W>Q.maxDepth)return`[${G}]`;return`${G+C0}{${j0(Z.sample,Q,X,W,$,J)}}`}if(G==="StringMatching"||G==="StringNotMatching")return G+C0+J(Z.sample,Q,X,W,$);if(G==="StringContaining"||G==="StringNotContaining")return G+C0+J(Z.sample,Q,X,W,$);if(typeof Z.toAsymmetricMatcher!=="function")throw TypeError(`Asymmetric matcher ${Z.constructor.name} does not implement toAsymmetricMatcher()`);return Z.toAsymmetricMatcher()},UZ=(Z)=>Z&&Z.$$typeof===GZ,zZ={serialize:YZ,test:UZ},KZ=" ",Z8=new Set(["DOMStringMap","NamedNodeMap"]),BZ=/^(?:HTML\w*Collection|NodeList)$/;function VZ(Z){return Z8.has(Z)||BZ.test(Z)}var wZ=(Z)=>Z&&Z.constructor&&!!Z.constructor.name&&VZ(Z.constructor.name);function qZ(Z){return Z.constructor.name==="NamedNodeMap"}var HZ=(Z,Q,X,W,$,J)=>{let G=Z.constructor.name;if(++W>Q.maxDepth)return`[${G}]`;return(Q.min?"":G+KZ)+(Z8.has(G)?`{${j0(qZ(Z)?[...Z].reduce((U,z)=>{return U[z.name]=z.value,U},{}):{...Z},Q,X,W,$,J)}}`:`[${k0([...Z],Q,X,W,$,J)}]`)},PZ={serialize:HZ,test:wZ};function Q8(Z){return Z.replaceAll("<","<").replaceAll(">",">")}function X6(Z,Q,X,W,$,J,G){let U=W+X.indent,z=X.colors;return Z.map((Y)=>{let K=Q[Y];if(typeof K==="string"&&K[0]==="_"&&K.startsWith("__vitest_")&&/__vitest_\d+__/.test(K))return"";let B=G(K,X,U,$,J);if(typeof K!=="string"){if(B.includes(` +`))B=X.spacingOuter+U+B+X.spacingOuter+W;B=`{${B}}`}return`${X.spacingInner+W+z.prop.open+Y+z.prop.close}=${z.value.open}${B}${z.value.close}`}).join("")}function _0(Z,Q,X,W,$,J){return Z.map((G)=>Q.spacingOuter+X+(typeof G==="string"?X8(G,Q):J(G,Q,X,W,$))).join("")}function NZ(Z,Q,X,W,$,J){if(Q.printShadowRoot===!1)return"";return[`${Q.spacingOuter+X}#shadow-root`,_0(Z,Q,X+Q.indent,W,$,J)].join("")}function X8(Z,Q){let X=Q.colors.content;return X.open+Q8(Z)+X.close}function DZ(Z,Q){let X=Q.colors.comment;return`${X.open}${X.close}`}function W6(Z,Q,X,W,$){let J=W.colors.tag;return`${J.open}<${Z}${Q&&J.close+Q+W.spacingOuter+$+J.open}${X?`>${J.close}${X}${W.spacingOuter}${$}${J.open}${J.close}`}function $6(Z,Q){let X=Q.colors.tag;return`${X.open}<${Z}${X.close} …${X.open} />${X.close}`}var FZ=1,J6=3,W8=8,$8=11,RZ=/^(?:(?:HTML|SVG)\w*)?Element$/;function MZ(Z){try{return typeof Z.hasAttribute==="function"&&Z.hasAttribute("is")}catch{return!1}}function OZ(Z){let Q=Z.constructor.name,{nodeType:X,tagName:W}=Z,$=typeof W==="string"&&W.includes("-")||MZ(Z);return X===FZ&&(RZ.test(Q)||$)||X===J6&&Q==="Text"||X===W8&&Q==="Comment"||X===$8&&Q==="DocumentFragment"}var J8=(Z)=>Z?.constructor?.name&&OZ(Z);function IZ(Z){return Z.nodeType===J6}function LZ(Z){return Z.nodeType===W8}function T0(Z){return Z.nodeType===$8}function m7(Z,Q){let X=Z.filter((W)=>{if(W.nodeType===J6)return(W.data||"").trim().length>0;return!0});if(Q)X=X.filter(Q);return X}function G8(Z,Q,X,W,$,J,G){if(IZ(Z))return X8(Z.data,Q);if(LZ(Z))return DZ(Z.data,Q);let U=T0(Z)?"DocumentFragment":Z.tagName.toLowerCase();if(++W>Q.maxDepth)return $6(U,Q);let z=Array.prototype.slice.call(Z.childNodes||Z.children),Y=T0(Z)||!Z.shadowRoot?[]:Array.prototype.slice.call(Z.shadowRoot.children),K=G?m7(z,G):z,B=G?m7(Y,G):Y;return W6(U,X6(T0(Z)?[]:Array.from(Z.attributes,(P)=>P.name).sort(),T0(Z)?{}:[...Z.attributes].reduce((P,w)=>{return P[w.name]=w.value,P},{}),Q,X+Q.indent,W,$,J),(B.length>0?NZ(B,Q,X+Q.indent,W,$,J):"")+_0(K,Q,X+Q.indent,W,$,J),Q,X)}var CZ=(Z,Q,X,W,$,J)=>G8(Z,Q,X,W,$,J);function Y8(Z){return{test:J8,serialize:(Q,X,W,$,J,G)=>G8(Q,X,W,$,J,G,Z)}}var TZ={serialize:CZ,test:J8},AZ="@@__IMMUTABLE_ITERABLE__@@",kZ="@@__IMMUTABLE_LIST__@@",SZ="@@__IMMUTABLE_KEYED__@@",jZ="@@__IMMUTABLE_MAP__@@",d7="@@__IMMUTABLE_ORDERED__@@",_Z="@@__IMMUTABLE_RECORD__@@",EZ="@@__IMMUTABLE_SEQ__@@",xZ="@@__IMMUTABLE_SET__@@",vZ="@@__IMMUTABLE_STACK__@@",b9=(Z)=>`Immutable.${Z}`,E0=(Z)=>`[${Z}]`,t9=" ",c7="…";function hZ(Z,Q,X,W,$,J,G){return++W>Q.maxDepth?E0(b9(G)):`${b9(G)+t9}{${S0(Z.entries(),Q,X,W,$,J)}}`}function yZ(Z){let Q=0;return{next(){if(QQ.maxDepth?E0(G):`${G+t9}{${S0(yZ(Z),Q,X,W,$,J)}}`}function uZ(Z,Q,X,W,$,J){let G=b9("Seq");if(++W>Q.maxDepth)return E0(G);if(Z[SZ])return`${G+t9}{${Z._iter||Z._object?S0(Z.entries(),Q,X,W,$,J):c7}}`;return`${G+t9}[${Z._iter||Z._array||Z._collection||Z._iterable?Q6(Z.values(),Q,X,W,$,J):c7}]`}function t1(Z,Q,X,W,$,J,G){return++W>Q.maxDepth?E0(b9(G)):`${b9(G)+t9}[${Q6(Z.values(),Q,X,W,$,J)}]`}var fZ=(Z,Q,X,W,$,J)=>{if(Z[jZ])return hZ(Z,Q,X,W,$,J,Z[d7]?"OrderedMap":"Map");if(Z[kZ])return t1(Z,Q,X,W,$,J,"List");if(Z[xZ])return t1(Z,Q,X,W,$,J,Z[d7]?"OrderedSet":"Set");if(Z[vZ])return t1(Z,Q,X,W,$,J,"Stack");if(Z[EZ])return uZ(Z,Q,X,W,$,J);return bZ(Z,Q,X,W,$,J)},gZ=(Z)=>Z&&(Z[AZ]===!0||Z[_Z]===!0),mZ={serialize:fZ,test:gZ};function U8(Z){return Z&&Z.__esModule&&Object.prototype.hasOwnProperty.call(Z,"default")?Z.default:Z}var e1={exports:{}},y={},l7;function dZ(){if(l7)return y;l7=1;var Z=Symbol.for("react.transitional.element"),Q=Symbol.for("react.portal"),X=Symbol.for("react.fragment"),W=Symbol.for("react.strict_mode"),$=Symbol.for("react.profiler"),J=Symbol.for("react.consumer"),G=Symbol.for("react.context"),U=Symbol.for("react.forward_ref"),z=Symbol.for("react.suspense"),Y=Symbol.for("react.suspense_list"),K=Symbol.for("react.memo"),B=Symbol.for("react.lazy"),P=Symbol.for("react.view_transition"),w=Symbol.for("react.client.reference");function N(H){if(typeof H==="object"&&H!==null){var F=H.$$typeof;switch(F){case Z:switch(H=H.type,H){case X:case $:case W:case z:case Y:case P:return H;default:switch(H=H&&H.$$typeof,H){case G:case U:case B:case K:return H;case J:return H;default:return F}}case Q:return F}}}return y.ContextConsumer=J,y.ContextProvider=G,y.Element=Z,y.ForwardRef=U,y.Fragment=X,y.Lazy=B,y.Memo=K,y.Portal=Q,y.Profiler=$,y.StrictMode=W,y.Suspense=z,y.SuspenseList=Y,y.isContextConsumer=function(H){return N(H)===J},y.isContextProvider=function(H){return N(H)===G},y.isElement=function(H){return typeof H==="object"&&H!==null&&H.$$typeof===Z},y.isForwardRef=function(H){return N(H)===U},y.isFragment=function(H){return N(H)===X},y.isLazy=function(H){return N(H)===B},y.isMemo=function(H){return N(H)===K},y.isPortal=function(H){return N(H)===Q},y.isProfiler=function(H){return N(H)===$},y.isStrictMode=function(H){return N(H)===W},y.isSuspense=function(H){return N(H)===z},y.isSuspenseList=function(H){return N(H)===Y},y.isValidElementType=function(H){return typeof H==="string"||typeof H==="function"||H===X||H===$||H===W||H===z||H===Y||typeof H==="object"&&H!==null&&(H.$$typeof===B||H.$$typeof===K||H.$$typeof===G||H.$$typeof===J||H.$$typeof===U||H.$$typeof===w||H.getModuleId!==void 0)?!0:!1},y.typeOf=N,y}var p7;function cZ(){if(p7)return e1.exports;return p7=1,e1.exports=dZ(),e1.exports}var z8=cZ(),lZ=U8(z8),pZ=e7({__proto__:null,default:lZ},[z8]),Z6={exports:{}},x={},i7;function iZ(){if(i7)return x;i7=1;var Z=Symbol.for("react.element"),Q=Symbol.for("react.portal"),X=Symbol.for("react.fragment"),W=Symbol.for("react.strict_mode"),$=Symbol.for("react.profiler"),J=Symbol.for("react.provider"),G=Symbol.for("react.context"),U=Symbol.for("react.server_context"),z=Symbol.for("react.forward_ref"),Y=Symbol.for("react.suspense"),K=Symbol.for("react.suspense_list"),B=Symbol.for("react.memo"),P=Symbol.for("react.lazy"),w=Symbol.for("react.offscreen"),N;N=Symbol.for("react.module.reference");function H(F){if(typeof F==="object"&&F!==null){var M=F.$$typeof;switch(M){case Z:switch(F=F.type,F){case X:case $:case W:case Y:case K:return F;default:switch(F=F&&F.$$typeof,F){case U:case G:case z:case P:case B:case J:return F;default:return M}}case Q:return M}}}return x.ContextConsumer=G,x.ContextProvider=J,x.Element=Z,x.ForwardRef=z,x.Fragment=X,x.Lazy=P,x.Memo=B,x.Portal=Q,x.Profiler=$,x.StrictMode=W,x.Suspense=Y,x.SuspenseList=K,x.isAsyncMode=function(){return!1},x.isConcurrentMode=function(){return!1},x.isContextConsumer=function(F){return H(F)===G},x.isContextProvider=function(F){return H(F)===J},x.isElement=function(F){return typeof F==="object"&&F!==null&&F.$$typeof===Z},x.isForwardRef=function(F){return H(F)===z},x.isFragment=function(F){return H(F)===X},x.isLazy=function(F){return H(F)===P},x.isMemo=function(F){return H(F)===B},x.isPortal=function(F){return H(F)===Q},x.isProfiler=function(F){return H(F)===$},x.isStrictMode=function(F){return H(F)===W},x.isSuspense=function(F){return H(F)===Y},x.isSuspenseList=function(F){return H(F)===K},x.isValidElementType=function(F){return typeof F==="string"||typeof F==="function"||F===X||F===$||F===W||F===Y||F===K||F===w||typeof F==="object"&&F!==null&&(F.$$typeof===P||F.$$typeof===B||F.$$typeof===J||F.$$typeof===G||F.$$typeof===z||F.$$typeof===N||F.getModuleId!==void 0)?!0:!1},x.typeOf=H,x}var n7;function nZ(){if(n7)return Z6.exports;return n7=1,Z6.exports=iZ(),Z6.exports}var K8=nZ(),oZ=U8(K8),rZ=e7({__proto__:null,default:oZ},[K8]),aZ=["isAsyncMode","isConcurrentMode","isContextConsumer","isContextProvider","isElement","isForwardRef","isFragment","isLazy","isMemo","isPortal","isProfiler","isStrictMode","isSuspense","isSuspenseList","isValidElementType"],j9=Object.fromEntries(aZ.map((Z)=>[Z,(Q)=>rZ[Z](Q)||pZ[Z](Q)]));function B8(Z,Q=[]){if(Array.isArray(Z))for(let X of Z)B8(X,Q);else if(Z!=null&&Z!==!1&&Z!=="")Q.push(Z);return Q}function o7(Z){let Q=Z.type;if(typeof Q==="string")return Q;if(typeof Q==="function")return Q.displayName||Q.name||"Unknown";if(j9.isFragment(Z))return"React.Fragment";if(j9.isSuspense(Z))return"React.Suspense";if(typeof Q==="object"&&Q!==null){if(j9.isContextProvider(Z))return"Context.Provider";if(j9.isContextConsumer(Z))return"Context.Consumer";if(j9.isForwardRef(Z)){if(Q.displayName)return Q.displayName;let X=Q.render.displayName||Q.render.name||"";return X===""?"ForwardRef":`ForwardRef(${X})`}if(j9.isMemo(Z)){let X=Q.displayName||Q.type.displayName||Q.type.name||"";return X===""?"Memo":`Memo(${X})`}}return"UNDEFINED"}function sZ(Z){let{props:Q}=Z;return Object.keys(Q).filter((X)=>X!=="children"&&Q[X]!==void 0).sort()}var tZ=(Z,Q,X,W,$,J)=>++W>Q.maxDepth?$6(o7(Z),Q):W6(o7(Z),X6(sZ(Z),Z.props,Q,X+Q.indent,W,$,J),_0(B8(Z.props.children),Q,X+Q.indent,W,$,J),Q,X),eZ=(Z)=>Z!=null&&j9.isElement(Z),Z4={serialize:tZ,test:eZ},Q4=typeof Symbol==="function"&&Symbol.for?Symbol.for("react.test.json"):245830487;function X4(Z){let{props:Q}=Z;return Q?Object.keys(Q).filter((X)=>Q[X]!==void 0).sort():[]}var W4=(Z,Q,X,W,$,J)=>++W>Q.maxDepth?$6(Z.type,Q):W6(Z.type,Z.props?X6(X4(Z),Z.props,Q,X+Q.indent,W,$,J):"",Z.children?_0(Z.children,Q,X+Q.indent,W,$,J):"",Q,X),$4=(Z)=>Z&&Z.$$typeof===Q4,J4={serialize:W4,test:$4},V8=Object.prototype.toString,G4=Date.prototype.toISOString,Y4=Error.prototype.toString,r7=RegExp.prototype.toString;function A0(Z){return typeof Z.constructor==="function"&&Z.constructor.name||"Object"}function U4(Z){return typeof window<"u"&&Z===window}var z4=/^Symbol\((.*)\)(.*)$/,K4=/\n/g;class G6 extends Error{constructor(Z,Q){super(Z);this.stack=Q,this.name=this.constructor.name}}function B4(Z){return Z==="[object Array]"||Z==="[object ArrayBuffer]"||Z==="[object DataView]"||Z==="[object Float32Array]"||Z==="[object Float64Array]"||Z==="[object Int8Array]"||Z==="[object Int16Array]"||Z==="[object Int32Array]"||Z==="[object Uint8Array]"||Z==="[object Uint8ClampedArray]"||Z==="[object Uint16Array]"||Z==="[object Uint32Array]"}function V4(Z){return Object.is(Z,-0)?"-0":String(Z)}function w4(Z){return String(`${Z}n`)}function a7(Z,Q){if(!Q)return"[Function]";return`[Function ${Z.name||"anonymous"}]`}function s7(Z){return String(Z).replace(z4,"Symbol($1)")}function t7(Z){return`[${Y4.call(Z)}]`}function w8(Z,Q,X,W,$){if(Z===!0||Z===!1)return`${Z}`;if(Z===void 0)return"undefined";if(Z===null)return"null";let J=typeof Z;if(J==="number")return V4(Z);if(J==="bigint")return w4(Z);if(J==="string"){let U=$?"'":'"';if(W){let z=$?/['\\]/g:/["\\]/g;return`${U}${Z.replaceAll(z,"\\$&")}${U}`}return`${U}${Z}${U}`}if(J==="function")return a7(Z,Q);if(J==="symbol")return s7(Z);let G=V8.call(Z);if(G==="[object WeakMap]")return"WeakMap {}";if(G==="[object WeakSet]")return"WeakSet {}";if(G==="[object Function]"||G==="[object GeneratorFunction]")return a7(Z,Q);if(G==="[object Symbol]")return s7(Z);if(G==="[object Date]")return Number.isNaN(+Z)?"Date { NaN }":G4.call(Z);if(G==="[object Error]")return t7(Z);if(G==="[object RegExp]"){if(X)return r7.call(Z).replaceAll(/[$()*+.?[\\\]^{|}]/g,"\\$&");return r7.call(Z)}if(Z instanceof Error)return t7(Z);return null}function q8(Z,Q,X,W,$,J){if($.includes(Z))return"[Circular]";$=[...$],$.push(Z);let G=++W>Q.maxDepth,U=Q.min;if(Q.callToJSON&&!G&&Z.toJSON&&typeof Z.toJSON==="function"&&!J)return C9(Z.toJSON(),Q,X,W,$,!0);let z=V8.call(Z);if(z==="[object Arguments]")return G?"[Arguments]":`${U?"":"Arguments "}[${k0(Z,Q,X,W,$,C9)}]`;if(B4(z))return G?`[${Z.constructor.name}]`:`${!Q.printBasicPrototype&&Z.constructor.name==="Array"?"":`${Z.constructor.name} `}[${k0(Z,Q,X,W,$,C9)}]`;if(z==="[object Map]")return G?"[Map]":`Map {${S0(Z.entries(),Q,X,W,$,C9," => ",Z.size)}}`;if(z==="[object Set]")return G?"[Set]":`Set {${Q6(Z.values(),Q,X,W,$,C9,Z.size)}}`;return G||U4(Z)?`[${A0(Z)}]`:`${!Q.printBasicPrototype&&A0(Z)==="Object"?"":`${A0(Z)} `}{${j0(Z,Q,X,W,$,C9)}}`}var q4={test:(Z)=>Z&&Z instanceof Error,serialize(Z,Q,X,W,$,J){if($.includes(Z))return"[Circular]";$=[...$,Z];let G=++W>Q.maxDepth,{message:U,cause:z,...Y}=Z,K={message:U,...typeof z<"u"?{cause:z}:{},...Z instanceof AggregateError?{errors:Z.errors}:{},...Y},B=Z.name!=="Error"?Z.name:A0(Z);return G?`[${B}]`:`${B} {${j0(K,Q,X,W,$,J,null)}}`}};function H4(Z){return Z.serialize!=null}function H8(Z,Q,X,W,$,J){let G;try{G=H4(Z)?Z.serialize(Q,X,W,$,J,C9):Z.print(Q,(U)=>C9(U,X,W,$,J),(U)=>{let z=W+X.indent;return z+U.replaceAll(K4,` +${z}`)},{edgeSpacing:X.spacingOuter,min:X.min,spacing:X.spacingInner},X.colors)}catch(U){throw new G6(U.message,U.stack)}if(typeof G!=="string")throw TypeError(`pretty-format: Plugin must return type "string" but instead returned "${typeof G}".`);return G}function P8(Z,Q){for(let X of Z)try{if(X.test(Q))return X}catch(W){throw new G6(W.message,W.stack)}return null}function C9(Z,Q,X,W,$,J){let G,U=P8(Q.plugins,Z);if(U!==null)G=H8(U,Z,Q,X,W,$);else{let z=w8(Z,Q.printFunctionName,Q.escapeRegex,Q.escapeString,Q.singleQuote);if(z!==null)G=z;else G=q8(Z,Q,X,W,$,J)}if(Q._outputLengthPerDepth[W]??=0,Q._outputLengthPerDepth[W]+=G.length,Q._outputLengthPerDepth[W]>Q.maxOutputLength)Q.maxDepth=0;return G}var Y6={comment:"gray",content:"reset",prop:"yellow",tag:"cyan",value:"green"},N8=Object.keys(Y6),Z9={callToJSON:!0,compareKeys:void 0,escapeRegex:!1,escapeString:!0,highlight:!1,indent:2,maxDepth:Number.POSITIVE_INFINITY,maxOutputLength:1e6,maxWidth:Number.POSITIVE_INFINITY,min:!1,plugins:[],printBasicPrototype:!0,printFunctionName:!0,printShadowRoot:!0,theme:Y6,singleQuote:!1,quoteKeys:!0,spacingInner:` `,spacingOuter:` -`};function h$(Z){for(let $ of Object.keys(Z))if(!Object.hasOwn(Q1,$))throw Error(`pretty-format: Unknown option "${$}".`);if(Z.min&&Z.indent!==void 0&&Z.indent!==0)throw Error('pretty-format: Options "min" and "indent" cannot be used together.')}function y$(){return T6.reduce((Z,$)=>{let Q=K9[$],X=Q&&f[Q];if(X&&typeof X.close==="string"&&typeof X.open==="string")Z[$]=X;else throw Error(`pretty-format: Option "theme" has a key "${$}" whose value "${Q}" is undefined in ansi-styles.`);return Z},Object.create(null))}function b$(){return T6.reduce((Z,$)=>{return Z[$]={close:"",open:""},Z},Object.create(null))}function u$(Z){return Z?.printFunctionName??Q1.printFunctionName}function f$(Z){return Z?.escapeRegex??Q1.escapeRegex}function g$(Z){return Z?.escapeString??Q1.escapeString}function m$(Z){return{callToJSON:Z?.callToJSON??Q1.callToJSON,colors:Z?.highlight?y$():b$(),compareKeys:typeof Z?.compareKeys==="function"||Z?.compareKeys===null?Z.compareKeys:Q1.compareKeys,escapeRegex:f$(Z),escapeString:g$(Z),indent:Z?.min?"":d$(Z?.indent??Q1.indent),maxDepth:Z?.maxDepth??Q1.maxDepth,maxWidth:Z?.maxWidth??Q1.maxWidth,min:Z?.min??Q1.min,plugins:Z?.plugins??Q1.plugins,printBasicPrototype:Z?.printBasicPrototype??!Z?.min,printFunctionName:u$(Z),printShadowRoot:Z?.printShadowRoot??!0,spacingInner:Z?.spacingInner??(Z?.min?" ":` +`};function P4(Z){for(let Q of Object.keys(Z))if(!Object.hasOwn(Z9,Q))throw Error(`pretty-format: Unknown option "${Q}".`);if(Z.min&&Z.indent!==void 0&&Z.indent!==0)throw Error('pretty-format: Options "min" and "indent" cannot be used together.')}function N4(){return N8.reduce((Z,Q)=>{let X=Y6[Q],W=X&&f[X];if(W&&typeof W.close==="string"&&typeof W.open==="string")Z[Q]=W;else throw Error(`pretty-format: Option "theme" has a key "${Q}" whose value "${X}" is undefined in ansi-styles.`);return Z},Object.create(null))}function D4(){return N8.reduce((Z,Q)=>{return Z[Q]={close:"",open:""},Z},Object.create(null))}function F4(Z){return Z?.printFunctionName??Z9.printFunctionName}function R4(Z){return Z?.escapeRegex??Z9.escapeRegex}function M4(Z){return Z?.escapeString??Z9.escapeString}function O4(Z){return{callToJSON:Z?.callToJSON??Z9.callToJSON,colors:Z?.highlight?N4():D4(),compareKeys:typeof Z?.compareKeys==="function"||Z?.compareKeys===null?Z.compareKeys:Z9.compareKeys,escapeRegex:R4(Z),escapeString:M4(Z),indent:Z?.min?"":I4(Z?.indent??Z9.indent),maxDepth:Z?.maxDepth??Z9.maxDepth,maxWidth:Z?.maxWidth??Z9.maxWidth,min:Z?.min??Z9.min,plugins:Z?.plugins??Z9.plugins,printBasicPrototype:Z?.printBasicPrototype??!Z?.min,printFunctionName:F4(Z),printShadowRoot:Z?.printShadowRoot??!0,spacingInner:Z?.spacingInner??(Z?.min?" ":` `),spacingOuter:Z?.spacingOuter??(Z?.min?"":` -`),singleQuote:Z?.singleQuote??Q1.singleQuote,quoteKeys:Z?.quoteKeys??Q1.quoteKeys,maxOutputLength:Z?.maxOutputLength??Q1.maxOutputLength,_outputLengthPerDepth:[]}}function d$(Z){return Array.from({length:Z+1}).join(" ")}function X1(Z,$){if($)h$($);let Q=m$($),X=L6(Q.plugins,Z);if(X!==null)return C6(X,Z,Q,"",0,[]);let J=O6(Z,Q.printFunctionName,Q.escapeRegex,Q.escapeString,Q.singleQuote);if(J!==null)return J;return I6(Z,Q,"",0,[])}var $0={AsymmetricMatcher:kZ,DOMCollection:hZ,DOMElement:pZ,Immutable:G$,ReactElement:R$,ReactTestComponent:C$,Error:x$};var{AsymmetricMatcher:A6,DOMCollection:k6,DOMElement:c$,Immutable:S6,ReactElement:_6,ReactTestComponent:j6}=$0,l$=[j6,_6,c$,k6,S6,A6];function a(Z,$=10,{maxLength:Q,filterNode:X,...J}={}){let W=Q??1e4,G,Y=typeof X==="string"?p$(X):X,z=Y?[j6,_6,P6(Y),k6,S6,A6]:l$;try{G=X1(Z,{maxDepth:$,escapeString:!1,plugins:z,...J})}catch{G=X1(Z,{callToJSON:!1,maxDepth:$,escapeString:!1,plugins:z,...J})}return G.length>=W&&$>1?a(Z,Math.floor(Math.min($,Number.MAX_SAFE_INTEGER)/2),{maxLength:Q,filterNode:X,...J}):G}function p$(Z){return(X)=>{if(X.nodeType===8)return!1;if(X.nodeType===1&&X.matches)try{return!X.matches(Z)}catch{return!0}return!0}}function E6(Z,$){let{truncate:Q,multiline:X,...J}=$??{},W={singleQuote:!0,quoteKeys:!1,min:!0,spacingInner:" ",spacingOuter:" ",printBasicPrototype:!1,compareKeys:null,...X?{min:!1,spacingInner:void 0,spacingOuter:void 0}:{}},G=Q??0,Y=a(Z,void 0,{...W,...J,maxLength:G||void 0});if(G===0||Y.length<=G)return Y;let z=Object.prototype.toString.call(Z);if(typeof Z==="string"){let B=G-1;if(B>0&&o$(Y[B-1]))B=B-1;return`'${Y.slice(1,B)}…'`}if(z==="[object Array]"||z==="[object Object]"||z==="[object Set]"||z==="[object Map]")return i$(Z,G,{...W,...J,maxDepth:1});return a(Z,void 0,{...W,...J,maxDepth:0})}function i$(Z,$,Q){function X(W){return a(Z,void 0,{...Q,maxWidth:W})}let J=n$(0,$,(W)=>X(W).length<=$);return X(J)}function n$(Z,$,Q){while(Z+1<$){let X=Math.floor((Z+$)/2);if(Q(X))Z=X;else $=X}return Z}function o$(Z){return Z>="\uD800"&&Z<="\uDBFF"}function N1(Z,$,Q){let X=typeof Z;if(!Q.includes(X))throw TypeError(`${$} value must be ${Q.join(" or ")}, received "${X}"`)}function Q0(Z){return Z!=null&&typeof Z==="object"&&!Array.isArray(Z)}function r$(Z){return Z===Object.prototype||Z===Function.prototype||Z===RegExp.prototype}function g1(Z){return Object.prototype.toString.apply(Z).slice(8,-1)}function a$(Z,$){let Q=typeof $==="function"?$:(X)=>$.add(X);Object.getOwnPropertyNames(Z).forEach(Q),Object.getOwnPropertySymbols(Z).forEach(Q)}function w9(Z){let $=new Set;if(r$(Z))return[];return a$(Z,$),Array.from($)}var x6={forceWritable:!1};function q9(Z,$=x6){return V9(Z,new WeakMap,$)}function V9(Z,$,Q=x6){let X,J;if($.has(Z))return $.get(Z);if(Array.isArray(Z)){J=Array.from({length:X=Z.length}),$.set(Z,J);while(X--)J[X]=V9(Z[X],$,Q);return J}if(Object.prototype.toString.call(Z)==="[object Object]"){J=Object.create(Object.getPrototypeOf(Z)),$.set(Z,J);let W=w9(Z);for(let G of W){let Y=Object.getOwnPropertyDescriptor(Z,G);if(!Y)continue;let z=V9(Z[G],$,Q);if(Q.forceWritable)Object.defineProperty(J,G,{enumerable:Y.enumerable,configurable:!0,writable:!0,value:z});else if("get"in Y)Object.defineProperty(J,G,{...Y,get(){return z}});else Object.defineProperty(J,G,{...Y,value:z})}return J}return Z}function H9(){}function m1(Z){let $=Z%10,Q=Z%100;if($===1&&Q!==11)return`${Z}st`;if($===2&&Q!==12)return`${Z}nd`;if($===3&&Q!==13)return`${Z}rd`;return`${Z}th`}var s=-1,o=1,c=0;class m{0;1;constructor(Z,$){this[0]=Z,this[1]=$}}function s$(Z,$){if(!Z||!$||Z.charAt(0)!==$.charAt(0))return 0;let Q=0,X=Math.min(Z.length,$.length),J=X,W=0;while(QX)Z=Z.substring(Q-X);else if(Q0?Q[X-1]:-1,G=0,Y=0,z=0,B=0,J=null,$=!0}W++}if($)r6(Z);$Q(Z),W=1;while(W=w){if(P>=U.length/2||P>=K.length/2)Z.splice(W,0,new m(c,K.substring(0,P))),Z[W-1][1]=U.substring(0,U.length-P),Z[W+1][1]=K.substring(P),W++}else if(w>=U.length/2||w>=K.length/2)Z.splice(W,0,new m(c,U.substring(0,w))),Z[W-1][0]=o,Z[W-1][1]=K.substring(0,K.length-w),Z[W+1][0]=s,Z[W+1][1]=U.substring(w),W++;W++}W++}}var h6=/[^a-z0-9]/i,y6=/\s/,b6=/[\r\n]/,e$=/\n\r?\n$/,ZQ=/^\r?\n\r?\n/;function $Q(Z){let $=1;while($=B)B=U,G=Q,Y=X,z=J}if(Z[$-1][1]!==G){if(G)Z[$-1][1]=G;else Z.splice($-1,1),$--;if(Z[$][1]=Y,z)Z[$+1][1]=z;else Z.splice($+1,1),$--}}$++}}function r6(Z){Z.push(new m(c,""));let $=0,Q=0,X=0,J="",W="",G;while($1){if(Q!==0&&X!==0){if(G=s$(W,J),G!==0){if($-Q-X>0&&Z[$-Q-X-1][0]===c)Z[$-Q-X-1][1]+=W.substring(0,G);else Z.splice(0,0,new m(c,W.substring(0,G))),$++;W=W.substring(G),J=J.substring(G)}if(G=o6(W,J),G!==0)Z[$][1]=W.substring(W.length-G)+Z[$][1],W=W.substring(0,W.length-G),J=J.substring(0,J.length-G)}if($-=Q+X,Z.splice($,Q+X),J.length)Z.splice($,0,new m(s,J)),$++;if(W.length)Z.splice($,0,new m(o,W)),$++;$++}else if($!==0&&Z[$-1][0]===c)Z[$-1][1]+=Z[$][1],Z.splice($,1);else $++;X=0,Q=0,J="",W="";break}if(Z.at(-1)?.[1]==="")Z.pop();let Y=!1;$=1;while(${let M=0;while(w{let M=0;while(w<=N&&H<=R&&O(N,R))N-=1,R-=1,M+=1;return M},J=(w,N,H,R,O,M,T)=>{let I=0,C=-w,A=M[I],L=A;M[I]+=Q(A+1,N,R+A-C+1,H,O);let S=w{let I=0,C=w,A=M[I],L=A;M[I]-=X(N,A-1,H,R+A-C-1,O);let S=w{let S=R-N,b=H-N,u=O-R-b,v=-u-(w-1),e=-u+(w-1),r=$,g=w{let S=O-H,b=H-N,u=O-R-b,v=u-w,e=u+w,r=$,g=w{let A=R-N,L=O-H,S=H-N,b=O-R,t=b-S,u=S,v=S;if(T[0]=N-1,I[0]=H,t%2===0){let e=(w||t)/2,r=(S+b)/2;for(let g=1;g<=r;g+=1)if(u=J(g,H,O,A,M,T,u),g{if(O-R{z1(H1,I1,G1)},isCommon:(H1,G1)=>S1(G1,H1)}}let O1=N,U1=H;N=R,H=O,R=O1,O=U1}let{foundSubsequence:L,isCommon:S}=T[M?1:0];z(w,N,H,R,O,S,I,C,A);let{nChangePreceding:b,aEndPreceding:t,bEndPreceding:u,nCommonPreceding:v,aCommonPreceding:e,bCommonPreceding:r,nCommonFollowing:g,aCommonFollowing:d,bCommonFollowing:n,nChangeFollowing:D1,aStartFollowing:Z1,bStartFollowing:Y1}=A;if(N{if(typeof N!=="number")throw TypeError(`${Z}: ${w} typeof ${typeof N} is not a number`);if(!Number.isSafeInteger(N))throw RangeError(`${Z}: ${w} value ${N} is not a safe integer`);if(N<0)throw RangeError(`${Z}: ${w} value ${N} is a negative integer`)},K=(w,N)=>{let H=typeof N;if(H!=="function")throw TypeError(`${Z}: ${w} typeof ${H} is not a function`)};function P(w,N,H,R){U("aLength",w),U("bLength",N),K("isCommon",H),K("foundSubsequence",R);let O=Q(0,w,0,N,H);if(O!==0)R(O,0,0);if(w!==O||N!==O){let M=O,T=O,I=X(M,w-1,T,N-1,H),C=w-I,A=N-I,L=O+I;if(w!==L&&N!==L)B(0,M,C,T,A,!1,[{foundSubsequence:R,isCommon:H}],[$],[$],{aCommonFollowing:$,aCommonPreceding:$,aEndPreceding:$,aStartFollowing:$,bCommonFollowing:$,bCommonPreceding:$,bEndPreceding:$,bStartFollowing:$,nChangeFollowing:$,nChangePreceding:$,nCommonFollowing:$,nCommonPreceding:$});if(I!==0)R(I,C,A)}}return u0}var WQ=JQ(),s6=XQ(WQ);function GQ(Z,$){return Z.replace(/\s+$/,(Q)=>$(Q))}function F9(Z,$,Q,X,J,W){return Z.length!==0?Q(`${X} ${GQ(Z,J)}`):X!==" "?Q(X):$&&W.length!==0?Q(`${X} ${W}`):""}function t6(Z,$,{aColor:Q,aIndicator:X,changeLineTrailingSpaceColor:J,emptyFirstOrLastLinePlaceholder:W}){return F9(Z,$,Q,X,J,W)}function e6(Z,$,{bColor:Q,bIndicator:X,changeLineTrailingSpaceColor:J,emptyFirstOrLastLinePlaceholder:W}){return F9(Z,$,Q,X,J,W)}function Z7(Z,$,{commonColor:Q,commonIndicator:X,commonLineTrailingSpaceColor:J,emptyFirstOrLastLinePlaceholder:W}){return F9(Z,$,Q,X,J,W)}function f6(Z,$,Q,X,{patchColor:J}){return J(`@@ -${Z+1},${$-Z} +${Q+1},${X-Q} @@`)}function BQ(Z,$){let Q=Z.length,X=$.contextLines,J=X+X,W=Q,G=!1,Y=0,z=0;while(z!==Q){let I=z;while(z!==Q&&Z[z][0]===c)z+=1;if(I!==z)if(I===0){if(z>X)W-=z-X,G=!0}else if(z===Q){let C=z-I;if(C>X)W-=C-X,G=!0}else{let C=z-I;if(C>J)W-=C-J,Y+=1}while(z!==Q&&Z[z][0]!==c)z+=1}let B=Y!==0||G;if(Y!==0)W+=Y+1;else if(G)W+=1;let U=W-1,K=[],P=0;if(B)K.push("");let w=0,N=0,H=0,R=0,O=(I)=>{let C=K.length;K.push(Z7(I,C===0||C===U,$)),H+=1,R+=1},M=(I)=>{let C=K.length;K.push(t6(I,C===0||C===U,$)),H+=1},T=(I)=>{let C=K.length;K.push(e6(I,C===0||C===U,$)),R+=1};z=0;while(z!==Q){let I=z;while(z!==Q&&Z[z][0]===c)z+=1;if(I!==z)if(I===0){if(z>X)I=z-X,w=I,N=I,H=w,R=N;for(let C=I;C!==z;C+=1)O(Z[C][1])}else if(z===Q){let C=z-I>X?I+X:z;for(let A=I;A!==C;A+=1)O(Z[A][1])}else{let C=z-I;if(C>J){let A=I+X;for(let S=I;S!==A;S+=1)O(Z[S][1]);K[P]=f6(w,H,N,R,$),P=K.length,K.push("");let L=C-J;w=H+L,N=R+L,H=w,R=N;for(let S=z-X;S!==z;S+=1)O(Z[S][1])}else for(let A=I;A!==z;A+=1)O(Z[A][1])}while(z!==Q&&Z[z][0]===s)M(Z[z][1]),z+=1;while(z!==Q&&Z[z][0]===o)T(Z[z][1]),z+=1}if(B)K[P]=f6(w,H,N,R,$);return K.join(` -`)}function YQ(Z,$){return Z.map((Q,X,J)=>{let W=Q[1],G=X===0||X===J.length-1;switch(Q[0]){case s:return t6(W,G,$);case o:return e6(W,G,$);default:return Z7(W,G,$)}}).join(` -`)}var P9=(Z)=>Z,$7=5,UQ=0;function zQ(){return{aAnnotation:"Expected",aColor:f.green,aIndicator:"-",bAnnotation:"Received",bColor:f.red,bIndicator:"+",changeColor:f.inverse,changeLineTrailingSpaceColor:P9,commonColor:f.dim,commonIndicator:" ",commonLineTrailingSpaceColor:P9,compareKeys:void 0,contextLines:$7,emptyFirstOrLastLinePlaceholder:"",expand:!1,includeChangeCounts:!1,omitAnnotationLines:!1,patchColor:f.yellow,printBasicPrototype:!1,truncateThreshold:UQ,truncateAnnotation:"... Diff result is truncated",truncateAnnotationColor:P9}}function KQ(Z){return Z&&typeof Z==="function"?Z:void 0}function VQ(Z){return typeof Z==="number"&&Number.isSafeInteger(Z)&&Z>=0?Z:$7}function x1(Z={}){return{...zQ(),...Z,compareKeys:KQ(Z.compareKeys),contextLines:VQ(Z.contextLines)}}function d1(Z){return Z.length===1&&Z[0].length===0}function wQ(Z){let $=0,Q=0;return Z.forEach((X)=>{switch(X[0]){case s:$+=1;break;case o:Q+=1;break}}),{a:$,b:Q}}function qQ({aAnnotation:Z,aColor:$,aIndicator:Q,bAnnotation:X,bColor:J,bIndicator:W,includeChangeCounts:G,omitAnnotationLines:Y},z){if(Y)return"";let B="",U="";if(G){let w=String(z.a),N=String(z.b),H=X.length-Z.length,R=" ".repeat(Math.max(0,H)),O=" ".repeat(Math.max(0,-H)),M=N.length-w.length,T=" ".repeat(Math.max(0,M)),I=" ".repeat(Math.max(0,-M));B=`${R} ${Q} ${T}${w}`,U=`${O} ${W} ${I}${N}`}let K=`${Q} ${Z}${B}`,P=`${W} ${X}${U}`;return`${$(K)} -${J(P)} +`),singleQuote:Z?.singleQuote??Z9.singleQuote,quoteKeys:Z?.quoteKeys??Z9.quoteKeys,maxOutputLength:Z?.maxOutputLength??Z9.maxOutputLength,_outputLengthPerDepth:[]}}function I4(Z){return Array.from({length:Z+1}).join(" ")}function Q9(Z,Q){if(Q)P4(Q);let X=O4(Q),W=P8(X.plugins,Z);if(W!==null)return H8(W,Z,X,"",0,[]);let $=w8(Z,X.printFunctionName,X.escapeRegex,X.escapeString,X.singleQuote);if($!==null)return $;return q8(Z,X,"",0,[])}var e9={AsymmetricMatcher:zZ,DOMCollection:PZ,DOMElement:TZ,Immutable:mZ,ReactElement:Z4,ReactTestComponent:J4,Error:q4};var{AsymmetricMatcher:D8,DOMCollection:F8,DOMElement:L4,Immutable:R8,ReactElement:M8,ReactTestComponent:O8}=e9,C4=[O8,M8,L4,F8,R8,D8];function o(Z,Q=10,{maxLength:X,filterNode:W,...$}={}){let J=X??1e4,G,U=typeof W==="string"?T4(W):W,z=U?[O8,M8,Y8(U),F8,R8,D8]:C4;try{G=Q9(Z,{maxDepth:Q,escapeString:!1,plugins:z,...$})}catch{G=Q9(Z,{callToJSON:!1,maxDepth:Q,escapeString:!1,plugins:z,...$})}return G.length>=J&&Q>1?o(Z,Math.floor(Math.min(Q,Number.MAX_SAFE_INTEGER)/2),{maxLength:X,filterNode:W,...$}):G}function T4(Z){return(W)=>{if(W.nodeType===8)return!1;if(W.nodeType===1&&W.matches)try{return!W.matches(Z)}catch{return!0}return!0}}function I8(Z,Q){let{truncate:X,multiline:W,...$}=Q??{},J={singleQuote:!0,quoteKeys:!1,min:!0,spacingInner:" ",spacingOuter:" ",printBasicPrototype:!1,compareKeys:null,...W?{min:!1,spacingInner:void 0,spacingOuter:void 0}:{}},G=X??0,U=o(Z,void 0,{...J,...$,maxLength:G||void 0});if(G===0||U.length<=G)return U;let z=Object.prototype.toString.call(Z);if(typeof Z==="string"){let Y=G-1;if(Y>0&&S4(U[Y-1]))Y=Y-1;return`'${U.slice(1,Y)}…'`}if(z==="[object Array]"||z==="[object Object]"||z==="[object Set]"||z==="[object Map]")return A4(Z,G,{...J,...$,maxDepth:1});return o(Z,void 0,{...J,...$,maxDepth:0})}function A4(Z,Q,X){function W(J){return o(Z,void 0,{...X,maxWidth:J})}let $=k4(0,Q,(J)=>W(J).length<=Q);return W($)}function k4(Z,Q,X){while(Z+1="\uD800"&&Z<="\uDBFF"}function H9(Z,Q,X){let W=typeof Z;if(!X.includes(W))throw TypeError(`${Q} value must be ${X.join(" or ")}, received "${W}"`)}function Z0(Z){return Z!=null&&typeof Z==="object"&&!Array.isArray(Z)}function j4(Z){return Z===Object.prototype||Z===Function.prototype||Z===RegExp.prototype}function u9(Z){return Object.prototype.toString.apply(Z).slice(8,-1)}function _4(Z,Q){let X=typeof Q==="function"?Q:(W)=>Q.add(W);Object.getOwnPropertyNames(Z).forEach(X),Object.getOwnPropertySymbols(Z).forEach(X)}function z6(Z){let Q=new Set;if(j4(Z))return[];return _4(Z,Q),Array.from(Q)}var L8={forceWritable:!1};function K6(Z,Q=L8){return U6(Z,new WeakMap,Q)}function U6(Z,Q,X=L8){let W,$;if(Q.has(Z))return Q.get(Z);if(Array.isArray(Z)){$=Array.from({length:W=Z.length}),Q.set(Z,$);while(W--)$[W]=U6(Z[W],Q,X);return $}if(Object.prototype.toString.call(Z)==="[object Object]"){$=Object.create(Object.getPrototypeOf(Z)),Q.set(Z,$);let J=z6(Z);for(let G of J){let U=Object.getOwnPropertyDescriptor(Z,G);if(!U)continue;let z=U6(Z[G],Q,X);if(X.forceWritable)Object.defineProperty($,G,{enumerable:U.enumerable,configurable:!0,writable:!0,value:z});else if("get"in U)Object.defineProperty($,G,{...U,get(){return z}});else Object.defineProperty($,G,{...U,value:z})}return $}return Z}function B6(){}function f9(Z){let Q=Z%10,X=Z%100;if(Q===1&&X!==11)return`${Z}st`;if(Q===2&&X!==12)return`${Z}nd`;if(Q===3&&X!==13)return`${Z}rd`;return`${Z}th`}var r=-1,i=1,c=0;class m{0;1;constructor(Z,Q){this[0]=Z,this[1]=Q}}function E4(Z,Q){if(!Z||!Q||Z.charAt(0)!==Q.charAt(0))return 0;let X=0,W=Math.min(Z.length,Q.length),$=W,J=0;while(X<$){if(Z.substring(J,$)===Q.substring(J,$))X=$,J=X;else W=$;$=Math.floor((W-X)/2+X)}return $}function f8(Z,Q){if(!Z||!Q||Z.charAt(Z.length-1)!==Q.charAt(Q.length-1))return 0;let X=0,W=Math.min(Z.length,Q.length),$=W,J=0;while(X<$){if(Z.substring(Z.length-$,Z.length-J)===Q.substring(Q.length-$,Q.length-J))X=$,J=X;else W=$;$=Math.floor((W-X)/2+X)}return $}function C8(Z,Q){let X=Z.length,W=Q.length;if(X===0||W===0)return 0;if(X>W)Z=Z.substring(X-W);else if(X0?X[W-1]:-1,G=0,U=0,z=0,Y=0,$=null,Q=!0}J++}if(Q)g8(Z);y4(Z),J=1;while(J=w){if(P>=K.length/2||P>=B.length/2)Z.splice(J,0,new m(c,B.substring(0,P))),Z[J-1][1]=K.substring(0,K.length-P),Z[J+1][1]=B.substring(P),J++}else if(w>=K.length/2||w>=B.length/2)Z.splice(J,0,new m(c,K.substring(0,w))),Z[J-1][0]=i,Z[J-1][1]=B.substring(0,B.length-w),Z[J+1][0]=r,Z[J+1][1]=K.substring(w),J++;J++}J++}}var T8=/[^a-z0-9]/i,A8=/\s/,k8=/[\r\n]/,v4=/\n\r?\n$/,h4=/^\r?\n\r?\n/;function y4(Z){let Q=1;while(Q=Y)Y=K,G=X,U=W,z=$}if(Z[Q-1][1]!==G){if(G)Z[Q-1][1]=G;else Z.splice(Q-1,1),Q--;if(Z[Q][1]=U,z)Z[Q+1][1]=z;else Z.splice(Q+1,1),Q--}}Q++}}function g8(Z){Z.push(new m(c,""));let Q=0,X=0,W=0,$="",J="",G;while(Q1){if(X!==0&&W!==0){if(G=E4(J,$),G!==0){if(Q-X-W>0&&Z[Q-X-W-1][0]===c)Z[Q-X-W-1][1]+=J.substring(0,G);else Z.splice(0,0,new m(c,J.substring(0,G))),Q++;J=J.substring(G),$=$.substring(G)}if(G=f8(J,$),G!==0)Z[Q][1]=J.substring(J.length-G)+Z[Q][1],J=J.substring(0,J.length-G),$=$.substring(0,$.length-G)}if(Q-=X+W,Z.splice(Q,X+W),$.length)Z.splice(Q,0,new m(r,$)),Q++;if(J.length)Z.splice(Q,0,new m(i,J)),Q++;Q++}else if(Q!==0&&Z[Q-1][0]===c)Z[Q-1][1]+=Z[Q][1],Z.splice(Q,1);else Q++;W=0,X=0,$="",J="";break}if(Z.at(-1)?.[1]==="")Z.pop();let U=!1;Q=1;while(Q{let O=0;while(w{let O=0;while(w<=N&&H<=F&&M(N,F))N-=1,F-=1,O+=1;return O},$=(w,N,H,F,M,O,A)=>{let I=0,L=-w,T=O[I],C=T;O[I]+=X(T+1,N,F+T-L+1,H,M);let S=w{let I=0,L=w,T=O[I],C=T;O[I]-=W(N,T-1,H,F+T-L-1,M);let S=w{let S=F-N,b=H-N,u=M-F-b,v=-u-(w-1),s=-u+(w-1),n=Q,g=w{let S=M-H,b=H-N,u=M-F-b,v=u-w,s=u+w,n=Q,g=w{let T=F-N,C=M-H,S=H-N,b=M-F,a=b-S,u=S,v=S;if(A[0]=N-1,I[0]=H,a%2===0){let s=(w||a)/2,n=(S+b)/2;for(let g=1;g<=n;g+=1)if(u=$(g,H,M,T,O,A,u),g{if(M-F{U9(w9,M9,$9)},isCommon:(w9,$9)=>A9($9,w9)}}let R9=N,Y9=H;N=F,H=M,F=R9,M=Y9}let{foundSubsequence:C,isCommon:S}=A[O?1:0];z(w,N,H,F,M,S,I,L,T);let{nChangePreceding:b,aEndPreceding:a,bEndPreceding:u,nCommonPreceding:v,aCommonPreceding:s,bCommonPreceding:n,nCommonFollowing:g,aCommonFollowing:d,bCommonFollowing:p,nChangeFollowing:P9,aStartFollowing:t,bStartFollowing:G9}=T;if(N{if(typeof N!=="number")throw TypeError(`${Z}: ${w} typeof ${typeof N} is not a number`);if(!Number.isSafeInteger(N))throw RangeError(`${Z}: ${w} value ${N} is not a safe integer`);if(N<0)throw RangeError(`${Z}: ${w} value ${N} is a negative integer`)},B=(w,N)=>{let H=typeof N;if(H!=="function")throw TypeError(`${Z}: ${w} typeof ${H} is not a function`)};function P(w,N,H,F){K("aLength",w),K("bLength",N),B("isCommon",H),B("foundSubsequence",F);let M=X(0,w,0,N,H);if(M!==0)F(M,0,0);if(w!==M||N!==M){let O=M,A=M,I=W(O,w-1,A,N-1,H),L=w-I,T=N-I,C=M+I;if(w!==C&&N!==C)Y(0,O,L,A,T,!1,[{foundSubsequence:F,isCommon:H}],[Q],[Q],{aCommonFollowing:Q,aCommonPreceding:Q,aEndPreceding:Q,aStartFollowing:Q,bCommonFollowing:Q,bCommonPreceding:Q,bEndPreceding:Q,bStartFollowing:Q,nChangeFollowing:Q,nChangePreceding:Q,nCommonFollowing:Q,nCommonPreceding:Q});if(I!==0)F(I,L,T)}}return v0}var g4=f4(),d8=u4(g4);function m4(Z,Q){return Z.replace(/\s+$/,(X)=>Q(X))}function P6(Z,Q,X,W,$,J){return Z.length!==0?X(`${W} ${m4(Z,$)}`):W!==" "?X(W):Q&&J.length!==0?X(`${W} ${J}`):""}function c8(Z,Q,{aColor:X,aIndicator:W,changeLineTrailingSpaceColor:$,emptyFirstOrLastLinePlaceholder:J}){return P6(Z,Q,X,W,$,J)}function l8(Z,Q,{bColor:X,bIndicator:W,changeLineTrailingSpaceColor:$,emptyFirstOrLastLinePlaceholder:J}){return P6(Z,Q,X,W,$,J)}function p8(Z,Q,{commonColor:X,commonIndicator:W,commonLineTrailingSpaceColor:$,emptyFirstOrLastLinePlaceholder:J}){return P6(Z,Q,X,W,$,J)}function j8(Z,Q,X,W,{patchColor:$}){return $(`@@ -${Z+1},${Q-Z} +${X+1},${W-X} @@`)}function d4(Z,Q){let X=Z.length,W=Q.contextLines,$=W+W,J=X,G=!1,U=0,z=0;while(z!==X){let I=z;while(z!==X&&Z[z][0]===c)z+=1;if(I!==z)if(I===0){if(z>W)J-=z-W,G=!0}else if(z===X){let L=z-I;if(L>W)J-=L-W,G=!0}else{let L=z-I;if(L>$)J-=L-$,U+=1}while(z!==X&&Z[z][0]!==c)z+=1}let Y=U!==0||G;if(U!==0)J+=U+1;else if(G)J+=1;let K=J-1,B=[],P=0;if(Y)B.push("");let w=0,N=0,H=0,F=0,M=(I)=>{let L=B.length;B.push(p8(I,L===0||L===K,Q)),H+=1,F+=1},O=(I)=>{let L=B.length;B.push(c8(I,L===0||L===K,Q)),H+=1},A=(I)=>{let L=B.length;B.push(l8(I,L===0||L===K,Q)),F+=1};z=0;while(z!==X){let I=z;while(z!==X&&Z[z][0]===c)z+=1;if(I!==z)if(I===0){if(z>W)I=z-W,w=I,N=I,H=w,F=N;for(let L=I;L!==z;L+=1)M(Z[L][1])}else if(z===X){let L=z-I>W?I+W:z;for(let T=I;T!==L;T+=1)M(Z[T][1])}else{let L=z-I;if(L>$){let T=I+W;for(let S=I;S!==T;S+=1)M(Z[S][1]);B[P]=j8(w,H,N,F,Q),P=B.length,B.push("");let C=L-$;w=H+C,N=F+C,H=w,F=N;for(let S=z-W;S!==z;S+=1)M(Z[S][1])}else for(let T=I;T!==z;T+=1)M(Z[T][1])}while(z!==X&&Z[z][0]===r)O(Z[z][1]),z+=1;while(z!==X&&Z[z][0]===i)A(Z[z][1]),z+=1}if(Y)B[P]=j8(w,H,N,F,Q);return B.join(` +`)}function c4(Z,Q){return Z.map((X,W,$)=>{let J=X[1],G=W===0||W===$.length-1;switch(X[0]){case r:return c8(J,G,Q);case i:return l8(J,G,Q);default:return p8(J,G,Q)}}).join(` +`)}var V6=(Z)=>Z,i8=5,l4=0;function p4(){return{aAnnotation:"Expected",aColor:f.green,aIndicator:"-",bAnnotation:"Received",bColor:f.red,bIndicator:"+",changeColor:f.inverse,changeLineTrailingSpaceColor:V6,commonColor:f.dim,commonIndicator:" ",commonLineTrailingSpaceColor:V6,compareKeys:void 0,contextLines:i8,emptyFirstOrLastLinePlaceholder:"",expand:!1,includeChangeCounts:!1,omitAnnotationLines:!1,patchColor:f.yellow,printBasicPrototype:!1,truncateThreshold:l4,truncateAnnotation:"... Diff result is truncated",truncateAnnotationColor:V6}}function i4(Z){return Z&&typeof Z==="function"?Z:void 0}function n4(Z){return typeof Z==="number"&&Number.isSafeInteger(Z)&&Z>=0?Z:i8}function _9(Z={}){return{...p4(),...Z,compareKeys:i4(Z.compareKeys),contextLines:n4(Z.contextLines)}}function g9(Z){return Z.length===1&&Z[0].length===0}function o4(Z){let Q=0,X=0;return Z.forEach((W)=>{switch(W[0]){case r:Q+=1;break;case i:X+=1;break}}),{a:Q,b:X}}function r4({aAnnotation:Z,aColor:Q,aIndicator:X,bAnnotation:W,bColor:$,bIndicator:J,includeChangeCounts:G,omitAnnotationLines:U},z){if(U)return"";let Y="",K="";if(G){let w=String(z.a),N=String(z.b),H=W.length-Z.length,F=" ".repeat(Math.max(0,H)),M=" ".repeat(Math.max(0,-H)),O=N.length-w.length,A=" ".repeat(Math.max(0,O)),I=" ".repeat(Math.max(0,-O));Y=`${F} ${X} ${A}${w}`,K=`${M} ${J} ${I}${N}`}let B=`${X} ${Z}${Y}`,P=`${J} ${W}${K}`;return`${Q(B)} +${$(P)} -`}function M9(Z,$,Q){return qQ(Q,wQ(Z))+(Q.expand?YQ(Z,Q):BQ(Z,Q))+($?Q.truncateAnnotationColor(` -${Q.truncateAnnotation}`):"")}function c0(Z,$,Q){let X=x1(Q),[J,W]=Q7(d1(Z)?[]:Z,d1($)?[]:$,X);return M9(J,W,X)}function HQ(Z,$,Q,X,J){if(d1(Z)&&d1(Q))Z=[],Q=[];if(d1($)&&d1(X))$=[],X=[];if(Z.length!==Q.length||$.length!==X.length)return c0(Z,$,J);let[W,G]=Q7(Q,X,J),Y=0,z=0;return W.forEach((B)=>{switch(B[0]){case s:B[1]=Z[Y],Y+=1;break;case o:B[1]=$[z],z+=1;break;default:B[1]=$[z],Y+=1,z+=1}}),M9(W,G,x1(J))}function Q7(Z,$,Q){let X=Q?.truncateThreshold??!1,J=Math.max(Math.floor(Q?.truncateThreshold??0),0),W=X?Math.min(Z.length,J):Z.length,G=X?Math.min($.length,J):$.length,Y=W!==Z.length||G!==$.length,z=(w,N)=>Z[w]===$[N],B=[],U=0,K=0;s6(W,G,z,(w,N,H)=>{for(;U!==N;U+=1)B.push(new m(s,Z[U]));for(;K!==H;K+=1)B.push(new m(o,$[K]));for(;w!==0;w-=1,U+=1,K+=1)B.push(new m(c,$[K]))});for(;U!==W;U+=1)B.push(new m(s,Z[U]));for(;K!==G;K+=1)B.push(new m(o,$[K]));return[B,Y]}function g6(Z){if(Z===void 0)return"undefined";else if(Z===null)return"null";else if(Array.isArray(Z))return"array";else if(typeof Z==="boolean")return"boolean";else if(typeof Z==="function")return"function";else if(typeof Z==="number")return"number";else if(typeof Z==="string")return"string";else if(typeof Z==="bigint")return"bigint";else if(typeof Z==="object"){if(Z!=null){if(Z.constructor===RegExp)return"regexp";else if(Z.constructor===Map)return"map";else if(Z.constructor===Set)return"set";else if(Z.constructor===Date)return"date"}return"object"}else if(typeof Z==="symbol")return"symbol";throw Error(`value of unknown type: ${Z}`)}function m6(Z){return Z.includes(`\r +`}function N6(Z,Q,X){return r4(X,o4(Z))+(X.expand?c4(Z,X):d4(Z,X))+(Q?X.truncateAnnotationColor(` +${X.truncateAnnotation}`):"")}function f0(Z,Q,X){let W=_9(X),[$,J]=n8(g9(Z)?[]:Z,g9(Q)?[]:Q,W);return N6($,J,W)}function a4(Z,Q,X,W,$){if(g9(Z)&&g9(X))Z=[],X=[];if(g9(Q)&&g9(W))Q=[],W=[];if(Z.length!==X.length||Q.length!==W.length)return f0(Z,Q,$);let[J,G]=n8(X,W,$),U=0,z=0;return J.forEach((Y)=>{switch(Y[0]){case r:Y[1]=Z[U],U+=1;break;case i:Y[1]=Q[z],z+=1;break;default:Y[1]=Q[z],U+=1,z+=1}}),N6(J,G,_9($))}function n8(Z,Q,X){let W=X?.truncateThreshold??!1,$=Math.max(Math.floor(X?.truncateThreshold??0),0),J=W?Math.min(Z.length,$):Z.length,G=W?Math.min(Q.length,$):Q.length,U=J!==Z.length||G!==Q.length,z=(w,N)=>Z[w]===Q[N],Y=[],K=0,B=0;d8(J,G,z,(w,N,H)=>{for(;K!==N;K+=1)Y.push(new m(r,Z[K]));for(;B!==H;B+=1)Y.push(new m(i,Q[B]));for(;w!==0;w-=1,K+=1,B+=1)Y.push(new m(c,Q[B]))});for(;K!==J;K+=1)Y.push(new m(r,Z[K]));for(;B!==G;B+=1)Y.push(new m(i,Q[B]));return[Y,U]}function _8(Z){if(Z===void 0)return"undefined";else if(Z===null)return"null";else if(Array.isArray(Z))return"array";else if(typeof Z==="boolean")return"boolean";else if(typeof Z==="function")return"function";else if(typeof Z==="number")return"number";else if(typeof Z==="string")return"string";else if(typeof Z==="bigint")return"bigint";else if(typeof Z==="object"){if(Z!=null){if(Z.constructor===RegExp)return"regexp";else if(Z.constructor===Map)return"map";else if(Z.constructor===Set)return"set";else if(Z.constructor===Date)return"date"}return"object"}else if(typeof Z==="symbol")return"symbol";throw Error(`value of unknown type: ${Z}`)}function E8(Z){return Z.includes(`\r `)?`\r `:` -`}function PQ(Z,$,Q){let X=Q?.truncateThreshold??!1,J=Math.max(Math.floor(Q?.truncateThreshold??0),0),W=Z.length,G=$.length;if(X){let w=Z.includes(` -`),N=$.includes(` -`),H=m6(Z),R=m6($),O=w?`${Z.split(H,J).join(H)} -`:Z,M=N?`${$.split(R,J).join(R)} -`:$;W=O.length,G=M.length}let Y=W!==Z.length||G!==$.length,z=(w,N)=>Z[w]===$[N],B=0,U=0,K=[];if(s6(W,G,z,(w,N,H)=>{if(B!==N)K.push(new m(s,Z.slice(B,N)));if(U!==H)K.push(new m(o,$.slice(U,H)));B=N+w,U=H+w,K.push(new m(c,$.slice(H,U)))}),B!==W)K.push(new m(s,Z.slice(B)));if(U!==G)K.push(new m(o,$.slice(U)));return[K,Y]}function NQ(Z,$,Q){return $.reduce((X,J)=>X+(J[0]===c?J[1]:J[0]===Z&&J[1].length!==0?Q(J[1]):""),"")}class D9{op;line;lines;changeColor;constructor(Z,$){this.op=Z,this.line=[],this.lines=[],this.changeColor=$}pushSubstring(Z){this.pushDiff(new m(this.op,Z))}pushLine(){this.lines.push(this.line.length!==1?new m(this.op,NQ(this.op,this.line,this.changeColor)):this.line[0][0]===this.op?this.line[0]:new m(this.op,this.line[0][1])),this.line.length=0}isLineEmpty(){return this.line.length===0}pushDiff(Z){this.line.push(Z)}align(Z){let $=Z[1];if($.includes(` -`)){let Q=$.split(` -`),X=Q.length-1;Q.forEach((J,W)=>{if(WZ[w]===Q[N],Y=0,K=0,B=[];if(d8(J,G,z,(w,N,H)=>{if(Y!==N)B.push(new m(r,Z.slice(Y,N)));if(K!==H)B.push(new m(i,Q.slice(K,H)));Y=N+w,K=H+w,B.push(new m(c,Q.slice(H,K)))}),Y!==J)B.push(new m(r,Z.slice(Y)));if(K!==G)B.push(new m(i,Q.slice(K)));return[B,U]}function t4(Z,Q,X){return Q.reduce((W,$)=>W+($[0]===c?$[1]:$[0]===Z&&$[1].length!==0?X($[1]):""),"")}class q6{op;line;lines;changeColor;constructor(Z,Q){this.op=Z,this.line=[],this.lines=[],this.changeColor=Q}pushSubstring(Z){this.pushDiff(new m(this.op,Z))}pushLine(){this.lines.push(this.line.length!==1?new m(this.op,t4(this.op,this.line,this.changeColor)):this.line[0][0]===this.op?this.line[0]:new m(this.op,this.line[0][1])),this.line.length=0}isLineEmpty(){return this.line.length===0}pushDiff(Z){this.line.push(Z)}align(Z){let Q=Z[1];if(Q.includes(` `)){let X=Q.split(` -`),J=X.length-1;X.forEach((W,G)=>{if(G===0){let Y=new m($,W);if(this.deleteBuffer.isLineEmpty()&&this.insertBuffer.isLineEmpty())this.flushChangeLines(),this.pushDiffCommonLine(Y);else this.pushDiffChangeLines(Y),this.flushChangeLines()}else if(G{switch(W[0]){case s:Q.align(W);break;case o:X.align(W);break;default:J.align(W)}}),J.getLines()}function RQ(Z,$){if($){let Q=Z.length-1;return Z.some((X,J)=>X[0]===c&&(J!==Q||X[1]!==` -`))}return Z.some((Q)=>Q[0]===c)}function FQ(Z,$,Q){if(Z!==$&&Z.length!==0&&$.length!==0){let X=Z.includes(` -`)||$.includes(` -`),[J,W]=J7(X?`${Z} -`:Z,X?`${$} -`:$,!0,Q);if(RQ(J,X)){let G=x1(Q),Y=DQ(J,G.changeColor);return M9(Y,W,G)}}return c0(Z.split(` -`),$.split(` -`),Q)}function J7(Z,$,Q,X){let[J,W]=PQ(Z,$,X);if(Q)t$(J);return[J,W]}function R9(Z,$){let{commonColor:Q}=x1($);return Q(Z)}var{AsymmetricMatcher:MQ,DOMCollection:OQ,DOMElement:IQ,Immutable:CQ,ReactElement:LQ,ReactTestComponent:TQ}=$0,W7=[TQ,LQ,IQ,OQ,CQ,MQ,$0.Error],m0={maxDepth:20,plugins:W7},G7={callToJSON:!1,maxDepth:8,plugins:W7},X0=(Z,$)=>$;function v1(Z,$,Q,X=X0){if(Object.is(Z,$))return"";let J=g6(Z),W=J,G=!1;if(J==="object"&&typeof Z.asymmetricMatch==="function"){if(Z.$$typeof!==Symbol.for("jest.asymmetricMatcher"))return;if(typeof Z.getExpectedType!=="function")return;W=Z.getExpectedType(),G=W==="string"}if(W!==g6($)){let O=function(I){return I.length<=R?I:`${I.slice(0,R)}...`},{aAnnotation:Y,aColor:z,aIndicator:B,bAnnotation:U,bColor:K,bIndicator:P}=x1(Q),w=d0(G7,Q),N=X1(Z,w),H=X1($,w),R=1e5;N=X("expected",O(N)),H=X("actual",O(H));let M=`${z(`${B} ${Y}:`)} -${N}`,T=`${K(`${P} ${U}:`)} -${H}`;return`${M} +`),W=X.length-1;X.forEach(($,J)=>{if(J{if(G===0){let U=new m(Q,J);if(this.deleteBuffer.isLineEmpty()&&this.insertBuffer.isLineEmpty())this.flushChangeLines(),this.pushDiffCommonLine(U);else this.pushDiffChangeLines(U),this.flushChangeLines()}else if(G<$)this.pushDiffCommonLine(new m(Q,J));else if(J.length!==0)this.pushDiffChangeLines(new m(Q,J))})}else this.pushDiffChangeLines(Z)}getLines(){return this.flushChangeLines(),this.lines}}function e4(Z,Q){let X=new q6(r,Q),W=new q6(i,Q),$=new o8(X,W);return Z.forEach((J)=>{switch(J[0]){case r:X.align(J);break;case i:W.align(J);break;default:$.align(J)}}),$.getLines()}function ZQ(Z,Q){if(Q){let X=Z.length-1;return Z.some((W,$)=>W[0]===c&&($!==X||W[1]!==` +`))}return Z.some((X)=>X[0]===c)}function QQ(Z,Q,X){if(Z!==Q&&Z.length!==0&&Q.length!==0){let W=Z.includes(` +`)||Q.includes(` +`),[$,J]=r8(W?`${Z} +`:Z,W?`${Q} +`:Q,!0,X);if(ZQ($,W)){let G=_9(X),U=e4($,G.changeColor);return N6(U,J,G)}}return f0(Z.split(` +`),Q.split(` +`),X)}function r8(Z,Q,X,W){let[$,J]=s4(Z,Q,W);if(X)x4($);return[$,J]}function H6(Z,Q){let{commonColor:X}=_9(Q);return X(Z)}var{AsymmetricMatcher:XQ,DOMCollection:WQ,DOMElement:$Q,Immutable:JQ,ReactElement:GQ,ReactTestComponent:YQ}=e9,a8=[YQ,GQ,$Q,WQ,JQ,XQ,e9.Error],b0={maxDepth:20,plugins:a8},s8={callToJSON:!1,maxDepth:8,plugins:a8},Q0=(Z,Q)=>Q;function E9(Z,Q,X,W=Q0){if(Object.is(Z,Q))return"";let $=_8(Z),J=$,G=!1;if($==="object"&&typeof Z.asymmetricMatch==="function"){if(Z.$$typeof!==Symbol.for("jest.asymmetricMatcher"))return;if(typeof Z.getExpectedType!=="function")return;J=Z.getExpectedType(),G=J==="string"}if(J!==_8(Q)){let M=function(I){return I.length<=F?I:`${I.slice(0,F)}...`},{aAnnotation:U,aColor:z,aIndicator:Y,bAnnotation:K,bColor:B,bIndicator:P}=_9(X),w=u0(s8,X),N=Q9(Z,w),H=Q9(Q,w),F=1e5;N=W("expected",M(N)),H=W("actual",M(H));let O=`${z(`${Y} ${U}:`)} +${N}`,A=`${B(`${P} ${K}:`)} +${H}`;return`${O} -${T}`}if(G)return;switch(J){case"string":return c0(Z.split(` -`),$.split(` -`),Q);case"boolean":case"number":return kQ(Z,$,Q,X);case"map":return N9(d6(Z),d6($),Q,X);case"set":return N9(c6(Z),c6($),Q,X);default:return N9(Z,$,Q,X)}}function AQ(Z){return($,Q)=>{return Z[$]=Q,Q}}function kQ(Z,$,Q,X=X0){let J=X("expected",X1(Z,m0)),W=X("actual",X1($,m0));return J===W?"":c0(J.split(` -`),W.split(` -`),Q)}function d6(Z){return new Map(Array.from(Z.entries()).sort())}function c6(Z){return new Set(Array.from(Z.values()).sort())}function N9(Z,$,Q,X=X0){let J,W=!1;try{let Y=d0(m0,Q);J=l6(Z,$,Y,Q,X)}catch{W=!0}let G=R9(a6,Q);if(J===void 0||J===G){let Y=d0(G7,Q);if(J=l6(Z,$,Y,Q,X),J!==G&&!W)J=`${R9(QQ,Q)} +${A}`}if(G)return;switch($){case"string":return f0(Z.split(` +`),Q.split(` +`),X);case"boolean":case"number":return zQ(Z,Q,X,W);case"map":return w6(x8(Z),x8(Q),X,W);case"set":return w6(v8(Z),v8(Q),X,W);default:return w6(Z,Q,X,W)}}function UQ(Z){return(Q,X)=>{return Z[Q]=X,X}}function zQ(Z,Q,X,W=Q0){let $=W("expected",Q9(Z,b0)),J=W("actual",Q9(Q,b0));return $===J?"":f0($.split(` +`),J.split(` +`),X)}function x8(Z){return new Map(Array.from(Z.entries()).sort())}function v8(Z){return new Set(Array.from(Z.values()).sort())}function w6(Z,Q,X,W=Q0){let $,J=!1;try{let U=u0(b0,X);$=h8(Z,Q,U,X,W)}catch{J=!0}let G=H6(m8,X);if($===void 0||$===G){let U=u0(s8,X);if($=h8(Z,Q,U,X,W),$!==G&&!J)$=`${H6(b4,X)} -${J}`}return J}function B7(Z){return d0(m0,Z)}function d0(Z,$){let{compareKeys:Q,printBasicPrototype:X,maxDepth:J}=x1($);return{...Z,compareKeys:Q,printBasicPrototype:X,maxDepth:J??Z.maxDepth}}function l6(Z,$,Q,X,J=X0){let W={...Q,indent:0},G=X1(Z,W),Y=X1($,W);if(G===Y)return R9(a6,X);else{let z=J("expected",X1(Z,Q)),B=J("actual",X1($,Q));return HQ(z.split(` -`),B.split(` -`),G.split(` +${$}`}return $}function t8(Z){return u0(b0,Z)}function u0(Z,Q){let{compareKeys:X,printBasicPrototype:W,maxDepth:$}=_9(Q);return{...Z,compareKeys:X,printBasicPrototype:W,maxDepth:$??Z.maxDepth}}function h8(Z,Q,X,W,$=Q0){let J={...X,indent:0},G=Q9(Z,J),U=Q9(Q,J);if(G===U)return H6(m8,W);else{let z=$("expected",Q9(Z,X)),Y=$("actual",Q9(Q,X));return a4(z.split(` `),Y.split(` -`),X)}}var p6=20000;function i6(Z){return g1(Z)==="Object"&&typeof Z.asymmetricMatch==="function"}function f0(Z,$){let Q=g1(Z),X=g1($);return Q===X&&(Q==="Object"||Q==="Array")}function l0(Z,$,Q,X){let{aAnnotation:J,bAnnotation:W}=x1(Q);if(typeof $==="string"&&typeof Z==="string"&&$.length>0&&Z.length>0&&$.length<=p6&&Z.length<=p6&&$!==Z){if($.includes(` +`),G.split(` +`),U.split(` +`),W)}}var y8=20000;function b8(Z){return u9(Z)==="Object"&&typeof Z.asymmetricMatch==="function"}function h0(Z,Q){let X=u9(Z),W=u9(Q);return X===W&&(X==="Object"||X==="Array")}function g0(Z,Q,X,W){let{aAnnotation:$,bAnnotation:J}=_9(X);if(typeof Q==="string"&&typeof Z==="string"&&Q.length>0&&Z.length>0&&Q.length<=y8&&Z.length<=y8&&Q!==Z){if(Q.includes(` `)||Z.includes(` -`))return FQ($,Z,Q);let[P]=J7($,Z,!0),w=P.some((O)=>O[0]===c),N=SQ(J,W),H=N(J)+EQ(n6(P,s,w)),R=N(W)+jQ(n6(P,o,w));return`${H} -${R}`}let G=q9($,{forceWritable:!0}),Y=q9(Z,{forceWritable:!0}),{replacedExpected:z,replacedActual:B}=g0(Y,G),U=X?AQ(X):X0;return v1(z,B,Q,U)}function g0(Z,$,Q=new WeakSet,X=new WeakSet){if(Z instanceof Error&&$ instanceof Error&&typeof Z.cause<"u"&&typeof $.cause>"u")return delete Z.cause,{replacedActual:Z,replacedExpected:$};if(!f0(Z,$))return{replacedActual:Z,replacedExpected:$};if(Q.has(Z)||X.has($))return{replacedActual:Z,replacedExpected:$};return Q.add(Z),X.add($),w9($).forEach((J)=>{let W=$[J],G=Z[J];if(i6(W)){if(W.asymmetricMatch(G))$[J]=G;else if("sample"in W&&W.sample!==void 0&&f0(G,W.sample)){let Y=g0(G,W.sample,Q,X);Z[J]=Y.replacedActual,$[J]=Y.replacedExpected}}else if(i6(G)){if(G.asymmetricMatch(W))Z[J]=W;else if("sample"in G&&G.sample!==void 0&&f0(G.sample,W)){let Y=g0(G.sample,W,Q,X);Z[J]=Y.replacedActual,$[J]=Y.replacedExpected}}else if(f0(G,W)){let Y=g0(G,W,Q,X);Z[J]=Y.replacedActual,$[J]=Y.replacedExpected}}),{replacedActual:Z,replacedExpected:$}}function SQ(...Z){let $=Z.reduce((Q,X)=>X.length>Q?X.length:Q,0);return(Q)=>`${Q}: ${" ".repeat($-Q.length)}`}var _Q="·";function Y7(Z){return Z.replace(/\s+$/gm,($)=>_Q.repeat($.length))}function jQ(Z){return f.red(Y7(a(Z)))}function EQ(Z){return f.green(Y7(a(Z)))}function n6(Z,$,Q){return Z.reduce((X,J)=>X+(J[0]===c?J[1]:J[0]===$?Q?f.inverse(J[1]):J[1]:""),"")}function F1(Z){return typeof Z==="function"&&"_isMockFunction"in Z&&Z._isMockFunction===!0}var xQ=new Set,O9=new Set,U7=new Set,vQ=new FinalizationRegistry((Z)=>{U7.delete(Z)}),z7=new WeakMap,K7=new WeakMap;function I9(Z={}){let{originalImplementation:$,restore:Q,mockImplementation:X,resetToMockImplementation:J,resetToMockName:W}=Z;if(Q)xQ.add(Q);let G=iQ($),Y=nQ(),z=yQ({config:G,state:Y,...Z}),B=(X||$)?.length??0;if(Object.defineProperty(z,"length",{writable:!0,enumerable:!1,value:B,configurable:!0}),W)G.mockName=z.name||"vi.fn()";z7.set(z,G),K7.set(Y,z);let U=new WeakRef(z);U7.add(U),vQ.register(z,U),z._isMockFunction=!0,z.getMockImplementation=()=>{return G.onceMockImplementations[0]||G.mockImplementation};let K=()=>{if(!Z.prototypeMembers?.length)H7(z,G.onceMockImplementations[0]||G.mockImplementation||$)};if(Object.defineProperty(z,"mock",{configurable:!1,enumerable:!0,writable:!1,value:Y}),z.mockImplementation=function(w){return G.mockImplementation=w,K(),z},z.mockImplementationOnce=function(w){return G.onceMockImplementations.push(w),K(),z},z.withImplementation=function(w,N){let{mockImplementation:H,onceMockImplementations:R}=G,O=()=>{G.mockImplementation=H,G.onceMockImplementations=R,K()};G.mockImplementation=w,G.onceMockImplementations=[],K();let M=N();if(typeof M==="object"&&typeof M?.then==="function")return M.then(()=>{return O(),z});else O();return z},z.mockReturnThis=function(){return z.mockImplementation(function(){return this})},z.mockReturnValue=function(w){return z.mockImplementation(function(){if(new.target)c1("mockReturnValue");return w})},z.mockReturnValueOnce=function(w){return z.mockImplementationOnce(function(){if(new.target)c1("mockReturnValueOnce");return w})},z.mockThrow=function(w){return z.mockImplementation(function(){throw w})},z.mockThrowOnce=function(w){return z.mockImplementationOnce(function(){throw w})},z.mockResolvedValue=function(w){return z.mockImplementation(function(){if(new.target)c1("mockResolvedValue");return Promise.resolve(w)})},z.mockResolvedValueOnce=function(w){return z.mockImplementationOnce(function(){if(new.target)c1("mockResolvedValueOnce");return Promise.resolve(w)})},z.mockRejectedValue=function(w){return z.mockImplementation(function(){if(new.target)c1("mockRejectedValue");return Promise.reject(w)})},z.mockRejectedValueOnce=function(w){return z.mockImplementationOnce(function(){if(new.target)c1("mockRejectedValueOnce");return Promise.reject(w)})},z.mockClear=function(){return Y.calls=[],Y.contexts=[],Y.instances=[],Y.invocationCallOrder=[],Y.results=[],Y.settledResults=[],O9.delete(z),z},z.mockReset=function(){return z.mockClear(),G.mockImplementation=J?X:void 0,G.mockName=W?z.name||"vi.fn()":"vi.fn()",G.onceMockImplementations=[],K(),z},z.mockRestore=function(){return z.mockReset(),Q?.()},z.mockName=function(w){if(typeof w==="string")G.mockName=w;return z},z.getMockName=function(){return G.mockName||"vi.fn()"},Symbol.dispose)z[Symbol.dispose]=()=>z.mockRestore();if(X)z.mockImplementation(X);else K();return z}function V7(Z){if(Z!=null&&F1(Z))return Z;return I9({mockImplementation:Z,resetToMockImplementation:!0})}function w7(Z,$,Q){p0(Z!=null,"The vi.spyOn() function could not find an object to spy upon. The first argument must be defined."),p0(typeof Z==="object"||typeof Z==="function","Vitest cannot spy on a primitive value.");let[X,J]=q7(Z,$)||[];p0(J||$ in Z,`The property "${String($)}" is not defined on the ${typeof Z}.`);let W=Q||"value",G=!1;if(W==="value"&&J&&J.value==null&&J.get)W="get",G=!0;let Y;if(J){if(Y=J[W],Y==null&&W==="value")Y=Z[$]}else if(W!=="value")Y=()=>Z[$];else Y=Z[$];let z=G&&Y?Y():Y,B=typeof z;if(p0(B==="function"||W!=="value"&&Y==null,`vi.spyOn() can only spy on a function. Received ${B}.`),F1(z))return z;let U=(w)=>{let{value:N,...H}=J||{configurable:!0,writable:!0};if(W!=="value")delete H.writable;H[W]=w,Object.defineProperty(Z,$,H)},P=I9({restore:()=>{if(X!==Z)Reflect.deleteProperty(Z,$);else if(J&&!Y)Object.defineProperty(Z,$,J);else U(Y)},originalImplementation:z,resetToMockName:!0});try{U(G?()=>P:P)}catch(w){if(w instanceof TypeError&&Symbol.toStringTag&&Z[Symbol.toStringTag]==="Module"&&(w.message.includes("Cannot redefine property")||w.message.includes("Cannot replace module namespace")||w.message.includes("can't redefine non-configurable property")))throw TypeError(`Cannot spy on export "${String($)}". Module namespace is not configurable in ESM. See: https://vitest.dev/guide/mocking/modules#mocking-a-module`,{cause:w});throw w}return P}function q7(Z,$){let Q=Object.getOwnPropertyDescriptor(Z,$);if(Q)return[Z,Q];let X=Object.getPrototypeOf(Z);while(X!==null){let J=Object.getOwnPropertyDescriptor(X,$);if(J)return[X,J];X=Object.getPrototypeOf(X)}}function p0(Z,$){if(!Z)throw Error($)}var hQ=1;function yQ({state:Z,config:$,name:Q,prototypeState:X,prototypeConfig:J,keepMembersImplementation:W,mockImplementation:G,prototypeMembers:Y=[]}){let z=$.mockOriginal,B=G,U=Q||z?.name||"Mock",K=function(){},P=X&&K7.get(X),w={[U]:function(...R){if(O9.add(w[U]),P)O9.add(P);bQ(R,Z,X),uQ(hQ++,Z,X);let O={type:"incomplete",value:void 0},M={type:"incomplete",value:void 0};fQ(O,Z,X),gQ(M,Z,X);let T=new.target?void 0:this,[I,C]=mQ(T,Z,X),[A,L]=dQ(T,Z,X),S=$.onceMockImplementations.shift()||$.mockImplementation||J?.onceMockImplementations.shift()||J?.mockImplementation||z||K,b,t,u=!1;try{if(new.target){if(Y.length===0)H7(N,S===K?void 0:S);b=Reflect.construct(S,R,new.target);for(let v of Y){let e=b[v];if(e!==N.prototype[v])continue;let r=F1(e),g=r?e.mock:void 0,d=r?z7.get(e):void 0;b[v]=I9({originalImplementation:W?d?.mockOriginal:void 0,prototypeState:g,prototypeConfig:d,keepMembersImplementation:W})}}else b=S.apply(this,R)}catch(v){if(t=v,u=!0,v instanceof TypeError&&v.message.includes("is not a constructor"))console.warn(`[vitest] The ${w[U].getMockName()} mock did not use 'function' or 'class' in its implementation, see https://vitest.dev/api/vi#vi-spyon for examples.`);throw v}finally{if(u)O.type="throw",O.value=t,M.type="rejected",M.value=t;else{if(O.type="return",O.value=b,new.target){if(Z.contexts[A-1]=b,Z.instances[I-1]=b,L!=null&&X)X.contexts[L-1]=b;if(C!=null&&X)X.instances[C-1]=b}if(b instanceof Promise)b.then((v)=>{M.type="fulfilled",M.value=v},(v)=>{M.type="rejected",M.value=v});else M.type="fulfilled",M.value=b}}return b}},N=w[U],H=z||B;if(H)cQ(N,H);return N}function H7(Z,$){let Q=Z.prototype;if(Q==null)return;let X=$?.prototype??Object.prototype;if(Q!==X&&Object.getPrototypeOf(Q)!==X)Object.setPrototypeOf(Q,X)}function bQ(Z,$,Q){$.calls.push(Z),Q?.calls.push(Z)}function uQ(Z,$,Q){$.invocationCallOrder.push(Z),Q?.invocationCallOrder.push(Z)}function fQ(Z,$,Q){$.results.push(Z),Q?.results.push(Z)}function gQ(Z,$,Q){$.settledResults.push(Z),Q?.settledResults.push(Z)}function mQ(Z,$,Q){let X=$.instances.push(Z),J=Q?.instances.push(Z);return[X,J]}function dQ(Z,$,Q){let X=$.contexts.push(Z),J=Q?.contexts.push(Z);return[X,J]}function cQ(Z,$){let{properties:Q,descriptors:X}=pQ($);for(let J of Q){let W=X[J];if(q7(Z,J))continue;Object.defineProperty(Z,J,W)}}var lQ=new Set(["length","name","prototype",Symbol.for("nodejs.util.promisify.custom")]);function pQ(Z){let $=new Set,Q={};while(Z&&Z!==Object.prototype&&Z!==Function.prototype){let X=[...Object.getOwnPropertyNames(Z),...Object.getOwnPropertySymbols(Z)];for(let J of X){if(Q[J]||lQ.has(J))continue;$.add(J),Q[J]=Object.getOwnPropertyDescriptor(Z,J)}Z=Object.getPrototypeOf(Z)}return{properties:$,descriptors:Q}}function iQ(Z){return{mockImplementation:void 0,mockOriginal:Z,mockName:"vi.fn()",onceMockImplementations:[]}}function nQ(){let Z={calls:[],contexts:[],instances:[],invocationCallOrder:[],settledResults:[],results:[],get lastCall(){return Z.calls.at(-1)}};return Z}function c1(Z){throw TypeError(`Cannot use \`${Z}\` when called with \`new\`. Use \`mockImplementation\` with a \`class\` keyword instead. See https://vitest.dev/api/mock#class-support for more information.`)}function oQ(Z){return Z&&(Z["@@__IMMUTABLE_ITERABLE__@@"]||Z["@@__IMMUTABLE_RECORD__@@"])}var rQ=Object.getPrototypeOf({});function P7(Z){if(Z instanceof Error)return`: ${Z.message}`;if(typeof Z==="string")return`: ${Z}`;return""}function M1(Z,$=new WeakMap){if(!Z||typeof Z==="string")return Z;if(Z instanceof Error&&"toJSON"in Z&&typeof Z.toJSON==="function"){let Q=Z.toJSON();if(Q&&Q!==Z&&typeof Q==="object"){if(typeof Z.message==="string")J0(()=>Q.message??=N7(Z.message));if(typeof Z.stack==="string")J0(()=>Q.stack??=Z.stack);if(typeof Z.name==="string")J0(()=>Q.name??=Z.name);if(Z.cause!=null)J0(()=>Q.cause??=M1(Z.cause,$))}return M1(Q,$)}if(typeof Z==="function")return`Function<${Z.name||"anonymous"}>`;if(typeof Z==="symbol")return Z.toString();if(typeof Z!=="object")return Z;if(typeof Buffer<"u"&&Z instanceof Buffer)return``;if(typeof Uint8Array<"u"&&Z instanceof Uint8Array)return``;if(oQ(Z))return M1(Z.toJSON(),$);if(Z instanceof Promise||Z.constructor&&Z.constructor.prototype==="AsyncFunction")return"Promise";if(typeof Element<"u"&&Z instanceof Element)return Z.tagName;if(typeof Z.toJSON==="function")return M1(Z.toJSON(),$);if($.has(Z))return $.get(Z);if(Array.isArray(Z)){let Q=Array(Z.length);return $.set(Z,Q),Z.forEach((X,J)=>{try{Q[J]=M1(X,$)}catch(W){Q[J]=P7(W)}}),Q}else{let Q=Object.create(null);$.set(Z,Q);let X=Z;while(X&&X!==rQ)Object.getOwnPropertyNames(X).forEach((J)=>{if(J in Q)return;try{Q[J]=M1(Z[J],$)}catch(W){delete Q[J],Q[J]=P7(W)}}),X=Object.getPrototypeOf(X);if(Z instanceof Error)J0(()=>Q.message=N7(Z.message));return Q}}function J0(Z){try{return Z()}catch{}}function N7(Z){return Z.replace(/\(0\s?,\s?__vite_ssr_import_\d+__.(\w+)\)/g,"$1").replace(/__(vite_ssr_import|vi_import)_\d+__\./g,"").replace(/getByTestId('__vitest_\d+__')/g,"page")}function C9(Z,$,Q=new WeakSet){if(!Z||typeof Z!=="object")return{message:String(Z)};let X=Z;if(X.showDiff||X.showDiff===void 0&&X.expected!==void 0&&X.actual!==void 0){let J={...$,...X.diffOptions};X.diff=l0(X.actual,X.expected,J,X),X.expected=D7(X.expected,J),X.actual=D7(X.actual,J)}try{if(!Q.has(X)&&typeof X.cause==="object")Q.add(X),X.cause=C9(X.cause,$,Q)}catch{}try{return M1(X)}catch(J){return M1(Error(`Failed to fully serialize error: ${J?.message} -Inner error message: ${X?.message}`))}}function D7(Z,$){if(typeof Z!=="string")return X1(Z,B7($));return Z}var i0=Symbol.for("matchers-object"),o0=Symbol.for("$$jest-matchers-object"),A7=Symbol.for("expect-global"),r0=Symbol.for("asymmetric-matchers-object");var n0=f.green,y9=f.red,aQ=f.inverse,sQ=f.bold,k1=f.dim;function tQ(Z,$="received",Q="expected",X={}){let{comment:J="",isDirectExpectCall:W=!1,isNot:G=!1,promise:Y="",secondArgument:z="",expectedColor:B=n0,receivedColor:U=y9,secondArgumentColor:K=n0}=X,P="",w="expect";if(!W&&$!=="")P+=k1(`${w}(`)+U($),w=")";if(Y!=="")P+=k1(`${w}.`)+Y,w="";if(G)P+=`${k1(`${w}.`)}not`,w="";if(Z.includes("."))w+=Z;else P+=k1(`${w}.`)+Z,w="";if(Q==="")w+="()";else{if(P+=k1(`${w}(`)+B(Q),z)P+=k1(", ")+K(z);w=")"}if(J!=="")w+=` // ${J}`;if(w!=="")P+=k1(w);return P}var eQ="·";function k7(Z){return Z.replace(/\s+$/gm,($)=>eQ.repeat($.length))}function ZX(Z){return y9(k7(a(Z)))}function $X(Z){return n0(k7(a(Z)))}function S7(){return{EXPECTED_COLOR:n0,RECEIVED_COLOR:y9,INVERTED_COLOR:aQ,BOLD_WEIGHT:sQ,DIM_COLOR:k1,diff:v1,matcherHint:tQ,printReceived:ZX,printExpected:$X,printDiffOrStringify:l0,printWithType:QX}}function QX(Z,$,Q){let X=g1($),J=X!=="null"&&X!=="undefined"?`${Z} has type: ${X} -`:"",W=`${Z} has value: ${Q($)}`;return J+W}function b9(){return globalThis[o0].customEqualityTesters}function E(Z,$,Q,X){return Q=Q||[],W0(Z,$,[],[],Q,X?_7:WX)}var aX=Function.prototype.toString;function R7(Z){return!!Z&&typeof Z==="object"&&"asymmetricMatch"in Z&&w1("Function",Z.asymmetricMatch)}function XX(Z,$,Q){let X=R7(Z),J=R7($);if(X&&J)return;if(X)return Z.asymmetricMatch($,Q);if(J)return $.asymmetricMatch(Z,Q)}function l1(Z){if(typeof Error.isError==="function")return Error.isError(Z);switch(Object.prototype.toString.call(Z)){case"[object Error]":case"[object Exception]":case"[object DOMException]":return!0;default:return Z instanceof Error}}function W0(Z,$,Q,X,J,W){let G=!0,Y=XX(Z,$,J);if(Y!==void 0)return Y;let z={equals:E};for(let N=0;NObject.getOwnPropertyDescriptor(Z,X).enumerable))}function WX(Z,$){return _7(Z,$)&&Z[$]!==void 0}function _7(Z,$){return Object.hasOwn(Z,$)}function w1(Z,$){return Object.prototype.toString.apply($)===`[object ${Z}]`}function M7(Z){return Z!==null&&typeof Z==="object"&&"nodeType"in Z&&typeof Z.nodeType==="number"&&"nodeName"in Z&&typeof Z.nodeName==="string"&&"isEqualNode"in Z&&typeof Z.isEqualNode==="function"}var j7="@@__IMMUTABLE_KEYED__@@",E7="@@__IMMUTABLE_SET__@@",GX="@@__IMMUTABLE_LIST__@@",a0="@@__IMMUTABLE_ORDERED__@@",BX="@@__IMMUTABLE_RECORD__@@";function YX(Z){return!!(Z&&Z[j7]&&!Z[a0])}function UX(Z){return!!(Z&&Z[E7]&&!Z[a0])}function s0(Z){return Z!=null&&typeof Z==="object"&&!Array.isArray(Z)}function zX(Z){return Boolean(Z&&s0(Z)&&Z[GX])}function KX(Z){return Boolean(Z&&s0(Z)&&Z[j7]&&Z[a0])}function VX(Z){return Boolean(Z&&s0(Z)&&Z[E7]&&Z[a0])}function wX(Z){return Boolean(Z&&s0(Z)&&Z[BX])}var x7=Symbol.iterator;function O7(Z){return!!(Z!=null&&Z[x7])}function W1(Z,$,Q=[],X=[],J=[]){if(typeof Z!=="object"||typeof $!=="object"||Array.isArray(Z)||Array.isArray($)||!O7(Z)||!O7($))return;if(Z.constructor!==$.constructor)return!1;let W=X.length;while(W--)if(X[W]===Z)return J[W]===$;X.push(Z),J.push($);let G=[...Q.filter((B)=>B!==W1),Y];function Y(B,U){return W1(B,U,[...Q],[...X],[...J])}if(Z.size!==void 0){if(Z.size!==$.size)return!1;else if(w1("Set",Z)||UX(Z)){let B=!0;for(let U of Z)if(!$.has(U)){let K=!1;for(let P of $)if(E(U,P,G)===!0)K=!0;if(K===!1){B=!1;break}}return X.pop(),J.pop(),B}else if(w1("Map",Z)||YX(Z)){let B=!0;for(let U of Z)if(!$.has(U[0])||!E(U[1],$.get(U[0]),G)){let K=!1;for(let P of $){let w=E(U[0],P[0],G),N=!1;if(w===!0)N=E(U[1],P[1],G);if(N===!0)K=!0}if(K===!1){B=!1;break}}return X.pop(),J.pop(),B}}let z=$[x7]();for(let B of Z){let U=z.next();if(U.done||!E(B,U.value,G))return!1}if(!z.next().done)return!1;if(!zX(Z)&&!KX(Z)&&!VX(Z)&&!wX(Z)){let B=Object.entries(Z),U=Object.entries($);if(!E(B,U,G))return!1}return X.pop(),J.pop(),!0}function u9(Z,$){if(!Z||typeof Z!=="object"||Z===Object.prototype)return!1;return Object.hasOwn(Z,$)||u9(Object.getPrototypeOf(Z),$)}function qX(Z){return Q0(Z)&&!l1(Z)&&!Array.isArray(Z)&&!(Z instanceof Date)&&!(Z instanceof Set)&&!(Z instanceof Map)}function G0(Z,$,Q=[]){let X=Q.filter((W)=>W!==G0),J=(W=new WeakMap)=>(G,Y)=>{if(!qX(Y))return;return Object.keys(Y).every((z)=>{if(Y[z]!=null&&typeof Y[z]==="object"){if(W.has(Y[z]))return E(G[z],Y[z],X);W.set(Y[z],!0)}let B=G!=null&&u9(G,z)&&E(G[z],Y[z],[...X,J(W)]);return W.delete(Y[z]),B})};return J()(Z,$)}function I7(Z,$){if(Z==null||$==null||Z.constructor===$.constructor)return;return!1}function C7(Z,$){let Q=Z,X=$;if(!(Z instanceof DataView&&$ instanceof DataView)){if(!(Z instanceof ArrayBuffer)||!($ instanceof ArrayBuffer))return;try{Q=new DataView(Z),X=new DataView($)}catch{return}}if(Q.byteLength!==X.byteLength)return!1;for(let J=0;JG!==A9);return E(Z,$,W,!0)&&E(X,J)}function HX(Z,$="#{this}",Q="#{exp}"){let X=`expected ${$} to be ${Q} // Object.is equality`;if(["toStrictEqual","toEqual"].includes(Z))return`${X} +`))return QQ(Q,Z,X);let[P]=r8(Q,Z,!0),w=P.some((M)=>M[0]===c),N=KQ($,J),H=N($)+wQ(u8(P,r,w)),F=N(J)+VQ(u8(P,i,w));return`${H} +${F}`}let G=K6(Q,{forceWritable:!0}),U=K6(Z,{forceWritable:!0}),{replacedExpected:z,replacedActual:Y}=y0(U,G),K=W?UQ(W):Q0;return E9(z,Y,X,K)}function y0(Z,Q,X=new WeakSet,W=new WeakSet){if(Z instanceof Error&&Q instanceof Error&&typeof Z.cause<"u"&&typeof Q.cause>"u")return delete Z.cause,{replacedActual:Z,replacedExpected:Q};if(!h0(Z,Q))return{replacedActual:Z,replacedExpected:Q};if(X.has(Z)||W.has(Q))return{replacedActual:Z,replacedExpected:Q};return X.add(Z),W.add(Q),z6(Q).forEach(($)=>{let J=Q[$],G=Z[$];if(b8(J)){if(J.asymmetricMatch(G))Q[$]=G;else if("sample"in J&&J.sample!==void 0&&h0(G,J.sample)){let U=y0(G,J.sample,X,W);Z[$]=U.replacedActual,Q[$]=U.replacedExpected}}else if(b8(G)){if(G.asymmetricMatch(J))Z[$]=J;else if("sample"in G&&G.sample!==void 0&&h0(G.sample,J)){let U=y0(G.sample,J,X,W);Z[$]=U.replacedActual,Q[$]=U.replacedExpected}}else if(h0(G,J)){let U=y0(G,J,X,W);Z[$]=U.replacedActual,Q[$]=U.replacedExpected}}),{replacedActual:Z,replacedExpected:Q}}function KQ(...Z){let Q=Z.reduce((X,W)=>W.length>X?W.length:X,0);return(X)=>`${X}: ${" ".repeat(Q-X.length)}`}var BQ="·";function e8(Z){return Z.replace(/\s+$/gm,(Q)=>BQ.repeat(Q.length))}function VQ(Z){return f.red(e8(o(Z)))}function wQ(Z){return f.green(e8(o(Z)))}function u8(Z,Q,X){return Z.reduce((W,$)=>W+($[0]===c?$[1]:$[0]===Q?X?f.inverse($[1]):$[1]:""),"")}function D9(Z){return typeof Z==="function"&&"_isMockFunction"in Z&&Z._isMockFunction===!0}var qQ=new Set,D6=new Set,Z5=new Set,HQ=new FinalizationRegistry((Z)=>{Z5.delete(Z)}),Q5=new WeakMap,X5=new WeakMap;function F6(Z={}){let{originalImplementation:Q,restore:X,mockImplementation:W,resetToMockImplementation:$,resetToMockName:J}=Z;if(X)qQ.add(X);let G=AQ(Q),U=kQ(),z=NQ({config:G,state:U,...Z}),Y=(W||Q)?.length??0;if(Object.defineProperty(z,"length",{writable:!0,enumerable:!1,value:Y,configurable:!0}),J)G.mockName=z.name||"vi.fn()";Q5.set(z,G),X5.set(U,z);let K=new WeakRef(z);Z5.add(K),HQ.register(z,K),z._isMockFunction=!0,z.getMockImplementation=()=>{return G.onceMockImplementations[0]||G.mockImplementation};let B=()=>{if(!Z.prototypeMembers?.length)G5(z,G.onceMockImplementations[0]||G.mockImplementation||Q)};if(Object.defineProperty(z,"mock",{configurable:!1,enumerable:!0,writable:!1,value:U}),z.mockImplementation=function(w){return G.mockImplementation=w,B(),z},z.mockImplementationOnce=function(w){return G.onceMockImplementations.push(w),B(),z},z.withImplementation=function(w,N){let{mockImplementation:H,onceMockImplementations:F}=G,M=()=>{G.mockImplementation=H,G.onceMockImplementations=F,B()};G.mockImplementation=w,G.onceMockImplementations=[],B();let O=N();if(typeof O==="object"&&typeof O?.then==="function")return O.then(()=>{return M(),z});else M();return z},z.mockReturnThis=function(){return z.mockImplementation(function(){return this})},z.mockReturnValue=function(w){return z.mockImplementation(function(){if(new.target)m9("mockReturnValue");return w})},z.mockReturnValueOnce=function(w){return z.mockImplementationOnce(function(){if(new.target)m9("mockReturnValueOnce");return w})},z.mockThrow=function(w){return z.mockImplementation(function(){throw w})},z.mockThrowOnce=function(w){return z.mockImplementationOnce(function(){throw w})},z.mockResolvedValue=function(w){return z.mockImplementation(function(){if(new.target)m9("mockResolvedValue");return Promise.resolve(w)})},z.mockResolvedValueOnce=function(w){return z.mockImplementationOnce(function(){if(new.target)m9("mockResolvedValueOnce");return Promise.resolve(w)})},z.mockRejectedValue=function(w){return z.mockImplementation(function(){if(new.target)m9("mockRejectedValue");return Promise.reject(w)})},z.mockRejectedValueOnce=function(w){return z.mockImplementationOnce(function(){if(new.target)m9("mockRejectedValueOnce");return Promise.reject(w)})},z.mockClear=function(){return U.calls=[],U.contexts=[],U.instances=[],U.invocationCallOrder=[],U.results=[],U.settledResults=[],D6.delete(z),z},z.mockReset=function(){return z.mockClear(),G.mockImplementation=$?W:void 0,G.mockName=J?z.name||"vi.fn()":"vi.fn()",G.onceMockImplementations=[],B(),z},z.mockRestore=function(){return z.mockReset(),X?.()},z.mockName=function(w){if(typeof w==="string")G.mockName=w;return z},z.getMockName=function(){return G.mockName||"vi.fn()"},Symbol.dispose)z[Symbol.dispose]=()=>z.mockRestore();if(W)z.mockImplementation(W);else B();return z}function W5(Z){if(Z!=null&&D9(Z))return Z;return F6({mockImplementation:Z,resetToMockImplementation:!0})}function $5(Z,Q,X){m0(Z!=null,"The vi.spyOn() function could not find an object to spy upon. The first argument must be defined."),m0(typeof Z==="object"||typeof Z==="function","Vitest cannot spy on a primitive value.");let[W,$]=J5(Z,Q)||[];m0($||Q in Z,`The property "${String(Q)}" is not defined on the ${typeof Z}.`);let J=X||"value",G=!1;if(J==="value"&&$&&$.value==null&&$.get)J="get",G=!0;let U;if($){if(U=$[J],U==null&&J==="value")U=Z[Q]}else if(J!=="value")U=()=>Z[Q];else U=Z[Q];let z=G&&U?U():U,Y=typeof z;if(m0(Y==="function"||J!=="value"&&U==null,`vi.spyOn() can only spy on a function. Received ${Y}.`),D9(z))return z;let K=(w)=>{let{value:N,...H}=$||{configurable:!0,writable:!0};if(J!=="value")delete H.writable;H[J]=w,Object.defineProperty(Z,Q,H)},P=F6({restore:()=>{if(W!==Z)Reflect.deleteProperty(Z,Q);else if($&&!U)Object.defineProperty(Z,Q,$);else K(U)},originalImplementation:z,resetToMockName:!0});try{K(G?()=>P:P)}catch(w){if(w instanceof TypeError&&Symbol.toStringTag&&Z[Symbol.toStringTag]==="Module"&&(w.message.includes("Cannot redefine property")||w.message.includes("Cannot replace module namespace")||w.message.includes("can't redefine non-configurable property")))throw TypeError(`Cannot spy on export "${String(Q)}". Module namespace is not configurable in ESM. See: https://vitest.dev/guide/mocking/modules#mocking-a-module`,{cause:w});throw w}return P}function J5(Z,Q){let X=Object.getOwnPropertyDescriptor(Z,Q);if(X)return[Z,X];let W=Object.getPrototypeOf(Z);while(W!==null){let $=Object.getOwnPropertyDescriptor(W,Q);if($)return[W,$];W=Object.getPrototypeOf(W)}}function m0(Z,Q){if(!Z)throw Error(Q)}var PQ=1;function NQ({state:Z,config:Q,name:X,prototypeState:W,prototypeConfig:$,keepMembersImplementation:J,mockImplementation:G,prototypeMembers:U=[]}){let z=Q.mockOriginal,Y=G,K=X||z?.name||"Mock",B=function(){},P=W&&X5.get(W),w={[K]:function(...F){if(D6.add(w[K]),P)D6.add(P);DQ(F,Z,W),FQ(PQ++,Z,W);let M={type:"incomplete",value:void 0},O={type:"incomplete",value:void 0};RQ(M,Z,W),MQ(O,Z,W);let A=new.target?void 0:this,[I,L]=OQ(A,Z,W),[T,C]=IQ(A,Z,W),S=Q.onceMockImplementations.shift()||Q.mockImplementation||$?.onceMockImplementations.shift()||$?.mockImplementation||z||B,b,a,u=!1;try{if(new.target){if(U.length===0)G5(N,S===B?void 0:S);b=Reflect.construct(S,F,new.target);for(let v of U){let s=b[v];if(s!==N.prototype[v])continue;let n=D9(s),g=n?s.mock:void 0,d=n?Q5.get(s):void 0;b[v]=F6({originalImplementation:J?d?.mockOriginal:void 0,prototypeState:g,prototypeConfig:d,keepMembersImplementation:J})}}else b=S.apply(this,F)}catch(v){if(a=v,u=!0,v instanceof TypeError&&v.message.includes("is not a constructor"))console.warn(`[vitest] The ${w[K].getMockName()} mock did not use 'function' or 'class' in its implementation, see https://vitest.dev/api/vi#vi-spyon for examples.`);throw v}finally{if(u)M.type="throw",M.value=a,O.type="rejected",O.value=a;else{if(M.type="return",M.value=b,new.target){if(Z.contexts[T-1]=b,Z.instances[I-1]=b,C!=null&&W)W.contexts[C-1]=b;if(L!=null&&W)W.instances[L-1]=b}if(b instanceof Promise)b.then((v)=>{O.type="fulfilled",O.value=v},(v)=>{O.type="rejected",O.value=v});else O.type="fulfilled",O.value=b}}return b}},N=w[K],H=z||Y;if(H)LQ(N,H);return N}function G5(Z,Q){let X=Z.prototype;if(X==null)return;let W=Q?.prototype??Object.prototype;if(X!==W&&Object.getPrototypeOf(X)!==W)Object.setPrototypeOf(X,W)}function DQ(Z,Q,X){Q.calls.push(Z),X?.calls.push(Z)}function FQ(Z,Q,X){Q.invocationCallOrder.push(Z),X?.invocationCallOrder.push(Z)}function RQ(Z,Q,X){Q.results.push(Z),X?.results.push(Z)}function MQ(Z,Q,X){Q.settledResults.push(Z),X?.settledResults.push(Z)}function OQ(Z,Q,X){let W=Q.instances.push(Z),$=X?.instances.push(Z);return[W,$]}function IQ(Z,Q,X){let W=Q.contexts.push(Z),$=X?.contexts.push(Z);return[W,$]}function LQ(Z,Q){let{properties:X,descriptors:W}=TQ(Q);for(let $ of X){let J=W[$];if(J5(Z,$))continue;Object.defineProperty(Z,$,J)}}var CQ=new Set(["length","name","prototype",Symbol.for("nodejs.util.promisify.custom")]);function TQ(Z){let Q=new Set,X={};while(Z&&Z!==Object.prototype&&Z!==Function.prototype){let W=[...Object.getOwnPropertyNames(Z),...Object.getOwnPropertySymbols(Z)];for(let $ of W){if(X[$]||CQ.has($))continue;Q.add($),X[$]=Object.getOwnPropertyDescriptor(Z,$)}Z=Object.getPrototypeOf(Z)}return{properties:Q,descriptors:X}}function AQ(Z){return{mockImplementation:void 0,mockOriginal:Z,mockName:"vi.fn()",onceMockImplementations:[]}}function kQ(){let Z={calls:[],contexts:[],instances:[],invocationCallOrder:[],settledResults:[],results:[],get lastCall(){return Z.calls.at(-1)}};return Z}function m9(Z){throw TypeError(`Cannot use \`${Z}\` when called with \`new\`. Use \`mockImplementation\` with a \`class\` keyword instead. See https://vitest.dev/api/mock#class-support for more information.`)}function SQ(Z){return Z&&(Z["@@__IMMUTABLE_ITERABLE__@@"]||Z["@@__IMMUTABLE_RECORD__@@"])}var jQ=Object.getPrototypeOf({});function Y5(Z){if(Z instanceof Error)return`: ${Z.message}`;if(typeof Z==="string")return`: ${Z}`;return""}function F9(Z,Q=new WeakMap){if(!Z||typeof Z==="string")return Z;if(Z instanceof Error&&"toJSON"in Z&&typeof Z.toJSON==="function"){let X=Z.toJSON();if(X&&X!==Z&&typeof X==="object"){if(typeof Z.message==="string")X0(()=>X.message??=U5(Z.message));if(typeof Z.stack==="string")X0(()=>X.stack??=Z.stack);if(typeof Z.name==="string")X0(()=>X.name??=Z.name);if(Z.cause!=null)X0(()=>X.cause??=F9(Z.cause,Q))}return F9(X,Q)}if(typeof Z==="function")return`Function<${Z.name||"anonymous"}>`;if(typeof Z==="symbol")return Z.toString();if(typeof Z!=="object")return Z;if(typeof Buffer<"u"&&Z instanceof Buffer)return``;if(typeof Uint8Array<"u"&&Z instanceof Uint8Array)return``;if(SQ(Z))return F9(Z.toJSON(),Q);if(Z instanceof Promise||Z.constructor&&Z.constructor.prototype==="AsyncFunction")return"Promise";if(typeof Element<"u"&&Z instanceof Element)return Z.tagName;if(typeof Z.toJSON==="function")return F9(Z.toJSON(),Q);if(Q.has(Z))return Q.get(Z);if(Array.isArray(Z)){let X=Array(Z.length);return Q.set(Z,X),Z.forEach((W,$)=>{try{X[$]=F9(W,Q)}catch(J){X[$]=Y5(J)}}),X}else{let X=Object.create(null);Q.set(Z,X);let W=Z;while(W&&W!==jQ)Object.getOwnPropertyNames(W).forEach(($)=>{if($ in X)return;try{X[$]=F9(Z[$],Q)}catch(J){delete X[$],X[$]=Y5(J)}}),W=Object.getPrototypeOf(W);if(Z instanceof Error)X0(()=>X.message=U5(Z.message));return X}}function X0(Z){try{return Z()}catch{}}function U5(Z){return Z.replace(/\(0\s?,\s?__vite_ssr_import_\d+__.(\w+)\)/g,"$1").replace(/__(vite_ssr_import|vi_import)_\d+__\./g,"").replace(/getByTestId('__vitest_\d+__')/g,"page")}function R6(Z,Q,X=new WeakSet){if(!Z||typeof Z!=="object")return{message:String(Z)};let W=Z;if(W.showDiff||W.showDiff===void 0&&W.expected!==void 0&&W.actual!==void 0){let $={...Q,...W.diffOptions};W.diff=g0(W.actual,W.expected,$,W),W.expected=z5(W.expected,$),W.actual=z5(W.actual,$)}try{if(!X.has(W)&&typeof W.cause==="object")X.add(W),W.cause=R6(W.cause,Q,X)}catch{}try{return F9(W)}catch($){return F9(Error(`Failed to fully serialize error: ${$?.message} +Inner error message: ${W?.message}`))}}function z5(Z,Q){if(typeof Z!=="string")return Q9(Z,t8(Q));return Z}var d0=Symbol.for("matchers-object"),l0=Symbol.for("$$jest-matchers-object"),D5=Symbol.for("expect-global"),p0=Symbol.for("asymmetric-matchers-object");var c0=f.green,E6=f.red,_Q=f.inverse,EQ=f.bold,T9=f.dim;function xQ(Z,Q="received",X="expected",W={}){let{comment:$="",isDirectExpectCall:J=!1,isNot:G=!1,promise:U="",secondArgument:z="",expectedColor:Y=c0,receivedColor:K=E6,secondArgumentColor:B=c0}=W,P="",w="expect";if(!J&&Q!=="")P+=T9(`${w}(`)+K(Q),w=")";if(U!=="")P+=T9(`${w}.`)+U,w="";if(G)P+=`${T9(`${w}.`)}not`,w="";if(Z.includes("."))w+=Z;else P+=T9(`${w}.`)+Z,w="";if(X==="")w+="()";else{if(P+=T9(`${w}(`)+Y(X),z)P+=T9(", ")+B(z);w=")"}if($!=="")w+=` // ${$}`;if(w!=="")P+=T9(w);return P}var vQ="·";function F5(Z){return Z.replace(/\s+$/gm,(Q)=>vQ.repeat(Q.length))}function hQ(Z){return E6(F5(o(Z)))}function yQ(Z){return c0(F5(o(Z)))}function R5(){return{EXPECTED_COLOR:c0,RECEIVED_COLOR:E6,INVERTED_COLOR:_Q,BOLD_WEIGHT:EQ,DIM_COLOR:T9,diff:E9,matcherHint:xQ,printReceived:hQ,printExpected:yQ,printDiffOrStringify:g0,printWithType:bQ}}function bQ(Z,Q,X){let W=u9(Q),$=W!=="null"&&W!=="undefined"?`${Z} has type: ${W} +`:"",J=`${Z} has value: ${X(Q)}`;return $+J}function x6(){return globalThis[l0].customEqualityTesters}function E(Z,Q,X,W){return X=X||[],W0(Z,Q,[],[],X,W?M5:gQ)}var kX=Function.prototype.toString;function K5(Z){return!!Z&&typeof Z==="object"&&"asymmetricMatch"in Z&&B9("Function",Z.asymmetricMatch)}function uQ(Z,Q,X){let W=K5(Z),$=K5(Q);if(W&&$)return;if(W)return Z.asymmetricMatch(Q,X);if($)return Q.asymmetricMatch(Z,X)}function d9(Z){if(typeof Error.isError==="function")return Error.isError(Z);switch(Object.prototype.toString.call(Z)){case"[object Error]":case"[object Exception]":case"[object DOMException]":return!0;default:return Z instanceof Error}}function W0(Z,Q,X,W,$,J){let G=!0,U=uQ(Z,Q,$);if(U!==void 0)return U;let z={equals:E};for(let N=0;N<$.length;N++){let H=$[N].call(z,Z,Q,$);if(H!==void 0)return H}if(typeof URL==="function"&&Z instanceof URL&&Q instanceof URL)return Z.href===Q.href;if(Object.is(Z,Q))return!0;if(Z===null||Q===null)return Z===Q;let Y=Object.prototype.toString.call(Z);if(Y!==Object.prototype.toString.call(Q))return!1;switch(Y){case"[object Boolean]":case"[object String]":case"[object Number]":if(typeof Z!==typeof Q)return!1;else if(typeof Z!=="object"&&typeof Q!=="object")return Object.is(Z,Q);else return Object.is(Z.valueOf(),Q.valueOf());case"[object Date]":{let N=+Z,H=+Q;return N===H||Number.isNaN(N)&&Number.isNaN(H)}case"[object RegExp]":return Z.source===Q.source&&Z.flags===Q.flags;case"[object Temporal.Instant]":case"[object Temporal.ZonedDateTime]":case"[object Temporal.PlainDateTime]":case"[object Temporal.PlainDate]":case"[object Temporal.PlainTime]":case"[object Temporal.PlainYearMonth]":case"[object Temporal.PlainMonthDay]":return Z.equals(Q);case"[object Temporal.Duration]":return Z.toString()===Q.toString()}if(typeof Z!=="object"||typeof Q!=="object")return!1;if(V5(Z)&&V5(Q))return Z.isEqualNode(Q);let K=X.length;while(K--)if(X[K]===Z)return W[K]===Q;else if(W[K]===Q)return!1;if(X.push(Z),W.push(Q),Y==="[object Array]"&&Z.length!==Q.length)return!1;if(d9(Z)&&d9(Q))try{return fQ(Z,Q,X,W,$,J)}finally{X.pop(),W.pop()}let B=B5(Z,J),P,w=B.length;if(B5(Q,J).length!==w)return!1;while(w--)if(P=B[w],G=J(Q,P)&&W0(Z[P],Q[P],X,W,$,J),!G)return!1;return X.pop(),W.pop(),G}function fQ(Z,Q,X,W,$,J){let G=Object.prototype.toString.call(Z)===Object.prototype.toString.call(Q)&&Z.name===Q.name&&Z.message===Q.message;if(typeof Q.cause<"u")G&&=W0(Z.cause,Q.cause,X,W,$,J);if(Z instanceof AggregateError&&Q instanceof AggregateError)G&&=W0(Z.errors,Q.errors,X,W,$,J);return G&&=W0({...Z},{...Q},X,W,$,J),G}function B5(Z,Q){let X=[];for(let W in Z)if(Q(Z,W))X.push(W);return X.concat(Object.getOwnPropertySymbols(Z).filter((W)=>Object.getOwnPropertyDescriptor(Z,W).enumerable))}function gQ(Z,Q){return M5(Z,Q)&&Z[Q]!==void 0}function M5(Z,Q){return Object.hasOwn(Z,Q)}function B9(Z,Q){return Object.prototype.toString.apply(Q)===`[object ${Z}]`}function V5(Z){return Z!==null&&typeof Z==="object"&&"nodeType"in Z&&typeof Z.nodeType==="number"&&"nodeName"in Z&&typeof Z.nodeName==="string"&&"isEqualNode"in Z&&typeof Z.isEqualNode==="function"}var O5="@@__IMMUTABLE_KEYED__@@",I5="@@__IMMUTABLE_SET__@@",mQ="@@__IMMUTABLE_LIST__@@",i0="@@__IMMUTABLE_ORDERED__@@",dQ="@@__IMMUTABLE_RECORD__@@";function cQ(Z){return!!(Z&&Z[O5]&&!Z[i0])}function lQ(Z){return!!(Z&&Z[I5]&&!Z[i0])}function n0(Z){return Z!=null&&typeof Z==="object"&&!Array.isArray(Z)}function pQ(Z){return Boolean(Z&&n0(Z)&&Z[mQ])}function iQ(Z){return Boolean(Z&&n0(Z)&&Z[O5]&&Z[i0])}function nQ(Z){return Boolean(Z&&n0(Z)&&Z[I5]&&Z[i0])}function oQ(Z){return Boolean(Z&&n0(Z)&&Z[dQ])}var L5=Symbol.iterator;function w5(Z){return!!(Z!=null&&Z[L5])}function W9(Z,Q,X=[],W=[],$=[]){if(typeof Z!=="object"||typeof Q!=="object"||Array.isArray(Z)||Array.isArray(Q)||!w5(Z)||!w5(Q))return;if(Z.constructor!==Q.constructor)return!1;let J=W.length;while(J--)if(W[J]===Z)return $[J]===Q;W.push(Z),$.push(Q);let G=[...X.filter((Y)=>Y!==W9),U];function U(Y,K){return W9(Y,K,[...X],[...W],[...$])}if(Z.size!==void 0){if(Z.size!==Q.size)return!1;else if(B9("Set",Z)||lQ(Z)){let Y=!0;for(let K of Z)if(!Q.has(K)){let B=!1;for(let P of Q)if(E(K,P,G)===!0)B=!0;if(B===!1){Y=!1;break}}return W.pop(),$.pop(),Y}else if(B9("Map",Z)||cQ(Z)){let Y=!0;for(let K of Z)if(!Q.has(K[0])||!E(K[1],Q.get(K[0]),G)){let B=!1;for(let P of Q){let w=E(K[0],P[0],G),N=!1;if(w===!0)N=E(K[1],P[1],G);if(N===!0)B=!0}if(B===!1){Y=!1;break}}return W.pop(),$.pop(),Y}}let z=Q[L5]();for(let Y of Z){let K=z.next();if(K.done||!E(Y,K.value,G))return!1}if(!z.next().done)return!1;if(!pQ(Z)&&!iQ(Z)&&!nQ(Z)&&!oQ(Z)){let Y=Object.entries(Z),K=Object.entries(Q);if(!E(Y,K,G))return!1}return W.pop(),$.pop(),!0}function v6(Z,Q){if(!Z||typeof Z!=="object"||Z===Object.prototype)return!1;return Object.hasOwn(Z,Q)||v6(Object.getPrototypeOf(Z),Q)}function rQ(Z){return Z0(Z)&&!d9(Z)&&!Array.isArray(Z)&&!(Z instanceof Date)&&!(Z instanceof Set)&&!(Z instanceof Map)}function $0(Z,Q,X=[]){let W=X.filter((J)=>J!==$0),$=(J=new WeakMap)=>(G,U)=>{if(!rQ(U))return;return Object.keys(U).every((z)=>{if(U[z]!=null&&typeof U[z]==="object"){if(J.has(U[z]))return E(G[z],U[z],W);J.set(U[z],!0)}let Y=G!=null&&v6(G,z)&&E(G[z],U[z],[...W,$(J)]);return J.delete(U[z]),Y})};return $()(Z,Q)}function q5(Z,Q){if(Z==null||Q==null||Z.constructor===Q.constructor)return;return!1}function H5(Z,Q){let X=Z,W=Q;if(!(Z instanceof DataView&&Q instanceof DataView)){if(!(Z instanceof ArrayBuffer)||!(Q instanceof ArrayBuffer))return;try{X=new DataView(Z),W=new DataView(Q)}catch{return}}if(X.byteLength!==W.byteLength)return!1;for(let $=0;$G!==I6);return E(Z,Q,J,!0)&&E(W,$)}function aQ(Z,Q="#{this}",X="#{exp}"){let W=`expected ${Q} to be ${X} // Object.is equality`;if(["toStrictEqual","toEqual"].includes(Z))return`${W} If it should pass with deep equality, replace "toBe" with "${Z}" -Expected: ${$} +Expected: ${Q} Received: serializes to the same string -`;return X}function PX(Z,$){return`${$} ${Z}${$===1?"":"s"}`}function L9(Z){return[...Object.keys(Z),...Object.getOwnPropertySymbols(Z).filter(($)=>Object.getOwnPropertyDescriptor(Z,$)?.enumerable)]}function NX(Z,$,Q){let X=0,J=(W=new WeakMap)=>(G,Y)=>{if(Array.isArray(G)){if(Array.isArray(Y)&&Y.length===G.length)return Y.map((z,B)=>J(W)(G[B],z))}else if(G instanceof Date)return G;else if(Q0(G)&&Q0(Y)){if(E(G,Y,[...Q,W1,G0]))return Y;let z={};if(W.set(G,z),typeof G.constructor==="function"&&typeof G.constructor.name==="string")Object.defineProperty(z,"constructor",{enumerable:!1,value:G.constructor});for(let B of L9(G))if(u9(Y,B))z[B]=W.has(G[B])?W.get(G[B]):J(W)(G[B],Y[B]);else if(!W.has(G[B])){if(X+=1,Q0(G[B]))X+=L9(G[B]).length;J(W)(G[B],Y[B])}if(L9(z).length>0)return z}return G};return{subset:J()(Z,$),stripped:X}}function DX(Z){return!!Z&&(typeof Z==="object"||typeof Z==="function")&&Z["~standard"]&&typeof Z["~standard"].validate==="function"}if(!Object.hasOwn(globalThis,i0)){let Z=new WeakMap,$=Object.create(null),Q=[],X=Object.create(null);Object.defineProperty(globalThis,i0,{configurable:!0,get:()=>Z}),Object.defineProperty(globalThis,o0,{configurable:!0,get:()=>({state:Z.get(globalThis[A7]),matchers:$,customEqualityTesters:Q})}),Object.defineProperty(globalThis,r0,{configurable:!0,get:()=>X})}function B0(Z){return globalThis[i0].get(Z)}function t0(Z,$){let Q=globalThis[i0],X=Q.get($),J=Object.defineProperties(X||{},Object.getOwnPropertyDescriptors(Z));if(!X)Q.set($,J)}class q1{sample;inverse;$$typeof=Symbol.for("jest.asymmetricMatcher");constructor(Z,$=!1){this.sample=Z,this.inverse=$}getMatcherContext(Z){return{...B0(Z||globalThis[A7]),equals:E,isNot:this.inverse,customTesters:b9(),utils:{...S7(),diff:v1,stringify:a,iterableEquality:W1,subsetEquality:G0}}}}q1.prototype[Symbol.for("chai/inspect")]=function(Z){let $=a(this,Z.depth,{min:!0});if($.length<=Z.truncate)return $;return`${this.toString()}{…}`};class k9 extends q1{constructor(Z,$=!1){if(!w1("String",Z))throw Error("Expected is not a string");super(Z,$)}asymmetricMatch(Z){let $=w1("String",Z)&&Z.includes(this.sample);return this.inverse?!$:$}toString(){return`String${this.inverse?"Not":""}Containing`}getExpectedType(){return"string"}}class v7 extends q1{asymmetricMatch(Z){return Z!=null}toString(){return"Anything"}toAsymmetricMatcher(){return"Anything"}}class S9 extends q1{constructor(Z,$=!1){super(Z,$)}getPrototype(Z){if(Object.getPrototypeOf)return Object.getPrototypeOf(Z);if(Z.constructor.prototype===Z)return null;return Z.constructor.prototype}hasProperty(Z,$){if(!Z)return!1;if(Object.hasOwn(Z,$))return!0;return this.hasProperty(this.getPrototype(Z),$)}getProperties(Z){return[...Object.keys(Z),...Object.getOwnPropertySymbols(Z).filter(($)=>Object.getOwnPropertyDescriptor(Z,$)?.enumerable)]}asymmetricMatch(Z,$){if(typeof this.sample!=="object")throw TypeError(`You must provide an object to ${this.toString()}, not '${typeof this.sample}'.`);let Q=!0,X=this.getProperties(this.sample);for(let J of X){if(!this.hasProperty(Z,J)){Q=!1;break}let W=this.sample[J],G=Z[J];if(!E(W,G,$)){Q=!1;break}}return this.inverse?!Q:Q}toString(){return`Object${this.inverse?"Not":""}Containing`}getExpectedType(){return"object"}}class _9 extends q1{constructor(Z,$=!1){super(Z,$)}asymmetricMatch(Z,$){if(!Array.isArray(this.sample))throw TypeError(`You must provide an array to ${this.toString()}, not '${typeof this.sample}'.`);let Q=this.sample.length===0||Array.isArray(Z)&&this.sample.every((X)=>Z.some((J)=>E(X,J,$)));return this.inverse?!Q:Q}toString(){return`Array${this.inverse?"Not":""}Containing`}getExpectedType(){return"array"}}class h7 extends q1{constructor(Z){if(typeof Z>"u")throw TypeError("any() expects to be passed a constructor function. Please pass one or use anything() to match any object.");super(Z)}fnNameFor(Z){if(Z.name)return Z.name;let Q=Function.prototype.toString.call(Z).match(/^(?:async)?\s*function\s*(?:\*\s*)?([\w$]+)\s*\(/);return Q?Q[1]:""}asymmetricMatch(Z){if(this.sample===String)return typeof Z=="string"||Z instanceof String;if(this.sample===Number)return typeof Z=="number"||Z instanceof Number;if(this.sample===Function)return typeof Z=="function"||typeof Z==="function";if(this.sample===Boolean)return typeof Z=="boolean"||Z instanceof Boolean;if(this.sample===BigInt)return typeof Z=="bigint"||Z instanceof BigInt;if(this.sample===Symbol)return typeof Z=="symbol"||Z instanceof Symbol;if(this.sample===Object)return typeof Z=="object";return Z instanceof this.sample}toString(){return"Any"}getExpectedType(){if(this.sample===String)return"string";if(this.sample===Number)return"number";if(this.sample===Function)return"function";if(this.sample===Object)return"object";if(this.sample===Boolean)return"boolean";return this.fnNameFor(this.sample)}toAsymmetricMatcher(){return`Any<${this.fnNameFor(this.sample)}>`}}class j9 extends q1{constructor(Z,$=!1){if(!w1("String",Z)&&!w1("RegExp",Z))throw Error("Expected is not a String or a RegExp");super(new RegExp(Z),$)}asymmetricMatch(Z){let $=w1("String",Z)&&this.sample.test(Z);return this.inverse?!$:$}toString(){return`String${this.inverse?"Not":""}Matching`}getExpectedType(){return"string"}}class E9 extends q1{precision;constructor(Z,$=2,Q=!1){if(!w1("Number",Z))throw Error("Expected is not a Number");if(!w1("Number",$))throw Error("Precision is not a Number");super(Z);this.inverse=Q,this.precision=$}asymmetricMatch(Z){if(!w1("Number",Z))return!1;let $=!1;if(Z===Number.POSITIVE_INFINITY&&this.sample===Number.POSITIVE_INFINITY)$=!0;else if(Z===Number.NEGATIVE_INFINITY&&this.sample===Number.NEGATIVE_INFINITY)$=!0;else $=Math.abs(this.sample-Z)<10**-this.precision/2;return this.inverse?!$:$}toString(){return`Number${this.inverse?"Not":""}CloseTo`}getExpectedType(){return"number"}toAsymmetricMatcher(){return[this.toString(),this.sample,`(${PX("digit",this.precision)})`].join(" ")}}class x9 extends q1{result;constructor(Z,$=!1){if(!DX(Z))throw TypeError("SchemaMatching expected to receive a Standard Schema.");super(Z,$)}asymmetricMatch(Z){let $=this.sample["~standard"].validate(Z);if($ instanceof Promise)throw TypeError("Async schema validation is not supported in asymmetric matchers.");this.result=$;let Q=!this.result.issues||this.result.issues.length===0;return this.inverse?!Q:Q}toString(){return`Schema${this.inverse?"Not":""}Matching`}getExpectedType(){return"object"}toAsymmetricMatcher(){let{utils:Z}=this.getMatcherContext();if((this.result?.issues||[]).length>0)return`${this.toString()} ${Z.stringify(this.result,void 0,{printBasicPrototype:!1})}`;return this.toString()}}var y7=(Z,$)=>{$.addMethod(Z.expect,"anything",()=>new v7),$.addMethod(Z.expect,"any",(Q)=>new h7(Q)),$.addMethod(Z.expect,"stringContaining",(Q)=>new k9(Q)),$.addMethod(Z.expect,"objectContaining",(Q)=>new S9(Q)),$.addMethod(Z.expect,"arrayContaining",(Q)=>new _9(Q)),$.addMethod(Z.expect,"stringMatching",(Q)=>new j9(Q)),$.addMethod(Z.expect,"closeTo",(Q,X)=>new E9(Q,X)),$.addMethod(Z.expect,"schemaMatching",(Q)=>new x9(Q)),Z.expect.not={stringContaining:(Q)=>new k9(Q,!0),objectContaining:(Q)=>new S9(Q,!0),arrayContaining:(Q)=>new _9(Q,!0),stringMatching:(Q)=>new j9(Q,!0),closeTo:(Q,X)=>new E9(Q,X,!0),schemaMatching:(Q)=>new x9(Q,!0)}};function L7(Z,$,Q){let X=Z.flag($,"soft")?".soft":"",J=Z.flag($,"negate")?"not.":"",W=`${Z.flag($,"_name")}(${Q?"expected":""})`,G=Z.flag($,"promise"),Y=G?`.${G}`:"";return`expect${X}(actual)${Y}.${J}${W}`}function T7(Z,$,Q,X,J){let W=Z;if(W&&$ instanceof Promise){if($=$.finally(()=>{if(!W.promises)return;let Y=W.promises.indexOf($);if(Y!==-1)W.promises.splice(Y,1)}),!W.promises)W.promises=[];if(J)$=$.then(H9,(Y)=>{v9(W,Y)});W.promises.push($);let G=!1;return W.onFinished??=[],W.onFinished.push(()=>{if(!G){let Y=Error(`Promise returned by \`${Q}\` was not awaited. This assertion is asynchronous and must be awaited; otherwise, it is not guaranteed to complete before the test finishes: +`;return W}function sQ(Z,Q){return`${Q} ${Z}${Q===1?"":"s"}`}function M6(Z){return[...Object.keys(Z),...Object.getOwnPropertySymbols(Z).filter((Q)=>Object.getOwnPropertyDescriptor(Z,Q)?.enumerable)]}function tQ(Z,Q,X){let W=0,$=(J=new WeakMap)=>(G,U)=>{if(Array.isArray(G)){if(Array.isArray(U)&&U.length===G.length)return U.map((z,Y)=>$(J)(G[Y],z))}else if(G instanceof Date)return G;else if(Z0(G)&&Z0(U)){if(E(G,U,[...X,W9,$0]))return U;let z={};if(J.set(G,z),typeof G.constructor==="function"&&typeof G.constructor.name==="string")Object.defineProperty(z,"constructor",{enumerable:!1,value:G.constructor});for(let Y of M6(G))if(v6(U,Y))z[Y]=J.has(G[Y])?J.get(G[Y]):$(J)(G[Y],U[Y]);else if(!J.has(G[Y])){if(W+=1,Z0(G[Y]))W+=M6(G[Y]).length;$(J)(G[Y],U[Y])}if(M6(z).length>0)return z}return G};return{subset:$()(Z,Q),stripped:W}}function eQ(Z){return!!Z&&(typeof Z==="object"||typeof Z==="function")&&Z["~standard"]&&typeof Z["~standard"].validate==="function"}if(!Object.hasOwn(globalThis,d0)){let Z=new WeakMap,Q=Object.create(null),X=[],W=Object.create(null);Object.defineProperty(globalThis,d0,{configurable:!0,get:()=>Z}),Object.defineProperty(globalThis,l0,{configurable:!0,get:()=>({state:Z.get(globalThis[D5]),matchers:Q,customEqualityTesters:X})}),Object.defineProperty(globalThis,p0,{configurable:!0,get:()=>W})}function J0(Z){return globalThis[d0].get(Z)}function o0(Z,Q){let X=globalThis[d0],W=X.get(Q),$=Object.defineProperties(W||{},Object.getOwnPropertyDescriptors(Z));if(!W)X.set(Q,$)}class V9{sample;inverse;$$typeof=Symbol.for("jest.asymmetricMatcher");constructor(Z,Q=!1){this.sample=Z,this.inverse=Q}getMatcherContext(Z){return{...J0(Z||globalThis[D5]),equals:E,isNot:this.inverse,customTesters:x6(),utils:{...R5(),diff:E9,stringify:o,iterableEquality:W9,subsetEquality:$0}}}}V9.prototype[Symbol.for("chai/inspect")]=function(Z){let Q=o(this,Z.depth,{min:!0});if(Q.length<=Z.truncate)return Q;return`${this.toString()}{…}`};class L6 extends V9{constructor(Z,Q=!1){if(!B9("String",Z))throw Error("Expected is not a string");super(Z,Q)}asymmetricMatch(Z){let Q=B9("String",Z)&&Z.includes(this.sample);return this.inverse?!Q:Q}toString(){return`String${this.inverse?"Not":""}Containing`}getExpectedType(){return"string"}}class C5 extends V9{asymmetricMatch(Z){return Z!=null}toString(){return"Anything"}toAsymmetricMatcher(){return"Anything"}}class C6 extends V9{constructor(Z,Q=!1){super(Z,Q)}getPrototype(Z){if(Object.getPrototypeOf)return Object.getPrototypeOf(Z);if(Z.constructor.prototype===Z)return null;return Z.constructor.prototype}hasProperty(Z,Q){if(!Z)return!1;if(Object.hasOwn(Z,Q))return!0;return this.hasProperty(this.getPrototype(Z),Q)}getProperties(Z){return[...Object.keys(Z),...Object.getOwnPropertySymbols(Z).filter((Q)=>Object.getOwnPropertyDescriptor(Z,Q)?.enumerable)]}asymmetricMatch(Z,Q){if(typeof this.sample!=="object")throw TypeError(`You must provide an object to ${this.toString()}, not '${typeof this.sample}'.`);let X=!0,W=this.getProperties(this.sample);for(let $ of W){if(!this.hasProperty(Z,$)){X=!1;break}let J=this.sample[$],G=Z[$];if(!E(J,G,Q)){X=!1;break}}return this.inverse?!X:X}toString(){return`Object${this.inverse?"Not":""}Containing`}getExpectedType(){return"object"}}class T6 extends V9{constructor(Z,Q=!1){super(Z,Q)}asymmetricMatch(Z,Q){if(!Array.isArray(this.sample))throw TypeError(`You must provide an array to ${this.toString()}, not '${typeof this.sample}'.`);let X=this.sample.length===0||Array.isArray(Z)&&this.sample.every((W)=>Z.some(($)=>E(W,$,Q)));return this.inverse?!X:X}toString(){return`Array${this.inverse?"Not":""}Containing`}getExpectedType(){return"array"}}class T5 extends V9{constructor(Z){if(typeof Z>"u")throw TypeError("any() expects to be passed a constructor function. Please pass one or use anything() to match any object.");super(Z)}fnNameFor(Z){if(Z.name)return Z.name;let X=Function.prototype.toString.call(Z).match(/^(?:async)?\s*function\s*(?:\*\s*)?([\w$]+)\s*\(/);return X?X[1]:""}asymmetricMatch(Z){if(this.sample===String)return typeof Z=="string"||Z instanceof String;if(this.sample===Number)return typeof Z=="number"||Z instanceof Number;if(this.sample===Function)return typeof Z=="function"||typeof Z==="function";if(this.sample===Boolean)return typeof Z=="boolean"||Z instanceof Boolean;if(this.sample===BigInt)return typeof Z=="bigint"||Z instanceof BigInt;if(this.sample===Symbol)return typeof Z=="symbol"||Z instanceof Symbol;if(this.sample===Object)return typeof Z=="object";return Z instanceof this.sample}toString(){return"Any"}getExpectedType(){if(this.sample===String)return"string";if(this.sample===Number)return"number";if(this.sample===Function)return"function";if(this.sample===Object)return"object";if(this.sample===Boolean)return"boolean";return this.fnNameFor(this.sample)}toAsymmetricMatcher(){return`Any<${this.fnNameFor(this.sample)}>`}}class A6 extends V9{constructor(Z,Q=!1){if(!B9("String",Z)&&!B9("RegExp",Z))throw Error("Expected is not a String or a RegExp");super(new RegExp(Z),Q)}asymmetricMatch(Z){let Q=B9("String",Z)&&this.sample.test(Z);return this.inverse?!Q:Q}toString(){return`String${this.inverse?"Not":""}Matching`}getExpectedType(){return"string"}}class k6 extends V9{precision;constructor(Z,Q=2,X=!1){if(!B9("Number",Z))throw Error("Expected is not a Number");if(!B9("Number",Q))throw Error("Precision is not a Number");super(Z);this.inverse=X,this.precision=Q}asymmetricMatch(Z){if(!B9("Number",Z))return!1;let Q=!1;if(Z===Number.POSITIVE_INFINITY&&this.sample===Number.POSITIVE_INFINITY)Q=!0;else if(Z===Number.NEGATIVE_INFINITY&&this.sample===Number.NEGATIVE_INFINITY)Q=!0;else Q=Math.abs(this.sample-Z)<10**-this.precision/2;return this.inverse?!Q:Q}toString(){return`Number${this.inverse?"Not":""}CloseTo`}getExpectedType(){return"number"}toAsymmetricMatcher(){return[this.toString(),this.sample,`(${sQ("digit",this.precision)})`].join(" ")}}class S6 extends V9{result;constructor(Z,Q=!1){if(!eQ(Z))throw TypeError("SchemaMatching expected to receive a Standard Schema.");super(Z,Q)}asymmetricMatch(Z){let Q=this.sample["~standard"].validate(Z);if(Q instanceof Promise)throw TypeError("Async schema validation is not supported in asymmetric matchers.");this.result=Q;let X=!this.result.issues||this.result.issues.length===0;return this.inverse?!X:X}toString(){return`Schema${this.inverse?"Not":""}Matching`}getExpectedType(){return"object"}toAsymmetricMatcher(){let{utils:Z}=this.getMatcherContext();if((this.result?.issues||[]).length>0)return`${this.toString()} ${Z.stringify(this.result,void 0,{printBasicPrototype:!1})}`;return this.toString()}}var A5=(Z,Q)=>{Q.addMethod(Z.expect,"anything",()=>new C5),Q.addMethod(Z.expect,"any",(X)=>new T5(X)),Q.addMethod(Z.expect,"stringContaining",(X)=>new L6(X)),Q.addMethod(Z.expect,"objectContaining",(X)=>new C6(X)),Q.addMethod(Z.expect,"arrayContaining",(X)=>new T6(X)),Q.addMethod(Z.expect,"stringMatching",(X)=>new A6(X)),Q.addMethod(Z.expect,"closeTo",(X,W)=>new k6(X,W)),Q.addMethod(Z.expect,"schemaMatching",(X)=>new S6(X)),Z.expect.not={stringContaining:(X)=>new L6(X,!0),objectContaining:(X)=>new C6(X,!0),arrayContaining:(X)=>new T6(X,!0),stringMatching:(X)=>new A6(X,!0),closeTo:(X,W)=>new k6(X,W,!0),schemaMatching:(X)=>new S6(X,!0)}};function P5(Z,Q,X){let W=Z.flag(Q,"soft")?".soft":"",$=Z.flag(Q,"negate")?"not.":"",J=`${Z.flag(Q,"_name")}(${X?"expected":""})`,G=Z.flag(Q,"promise"),U=G?`.${G}`:"";return`expect${W}(actual)${U}.${$}${J}`}function N5(Z,Q,X,W,$){let J=Z;if(J&&Q instanceof Promise){if(Q=Q.finally(()=>{if(!J.promises)return;let U=J.promises.indexOf(Q);if(U!==-1)J.promises.splice(U,1)}),!J.promises)J.promises=[];if($)Q=Q.then(B6,(U)=>{j6(J,U)});J.promises.push(Q);let G=!1;return J.onFinished??=[],J.onFinished.push(()=>{if(!G){let U=Error(`Promise returned by \`${X}\` was not awaited. This assertion is asynchronous and must be awaited; otherwise, it is not guaranteed to complete before the test finishes: -await ${Q} -`);if(X.stack)Y.stack=X.stack.replace(X.message,Y.message);throw Y}}),{then(Y,z){return G=!0,$.then(Y,z)},catch(Y){return G=!0,$.catch(Y)},finally(Y){return G=!0,$.finally(Y)},[Symbol.toStringTag]:"Promise"}}return $}function v9(Z,$){Z.result||={state:"fail"},Z.result.state="fail",Z.result.errors||=[],Z.result.errors.push(C9($))}function b7(Z,$,Q){return function(...X){if($!=="withTest")Z.flag(this,"_name",$);if(!Z.flag(this,"soft"))try{return Q.apply(this,X)}finally{}let J=Z.flag(this,"vitest-test");if(!J)throw Error("expect.soft() can only be used inside a test");try{let W=Q.apply(this,X);if(W&&typeof W==="object"&&typeof W.then==="function")return W.then(H9,(G)=>{v9(J,G)});return W}catch(W){v9(J,W)}}}var u7=(Z,$)=>{let{AssertionError:Q}=Z,X=b9();function J(B,U){let K=(P)=>{let w=b7($,P,U);$.addMethod(Z.Assertion.prototype,P,w),$.addMethod(globalThis[o0].matchers,P,w)};if(Array.isArray(B))B.forEach((P)=>K(P));else K(B)}["throw","throws","Throw"].forEach((B)=>{$.overwriteMethod(Z.Assertion.prototype,B,(U)=>{return function(...K){let P=$.flag(this,"promise"),w=$.flag(this,"object"),N=$.flag(this,"negate");if(P==="rejects")$.flag(this,"object",()=>{throw w});else if(P==="resolves"&&typeof w!=="function")if(!N){let H=$.flag(this,"message")||"expected promise to throw an error, but it didn't";throw new Q(H,{showDiff:!1},$.flag(this,"ssfi"))}else return;U.apply(this,K)}})}),J("withTest",function(B){return $.flag(this,"vitest-test",B),this}),J("toEqual",function(B){let U=$.flag(this,"object"),K=E(U,B,[...X,W1]);return this.assert(K,"expected #{this} to deeply equal #{exp}","expected #{this} to not deeply equal #{exp}",B,U)}),J("toStrictEqual",function(B){let U=$.flag(this,"object"),K=E(U,B,[...X,W1,I7,A9,C7],!0);return this.assert(K,"expected #{this} to strictly equal #{exp}","expected #{this} to not strictly equal #{exp}",B,U)}),J("toBe",function(B){let U=this._obj,K=Object.is(U,B),P="";if(!K){if(E(U,B,[...X,W1,I7,A9,C7],!0))P="toStrictEqual";else if(E(U,B,[...X,W1]))P="toEqual"}return this.assert(K,HX(P),"expected #{this} not to be #{exp} // Object.is equality",B,U)}),J("toMatchObject",function(B){let U=this._obj,K=E(U,B,[...X,W1,G0]),P=$.flag(this,"negate"),{subset:w,stripped:N}=NX(U,B,X);if(K&&P||!K&&!P){let H=$.getMessage(this,[K,"expected #{this} to match object #{exp}","expected #{this} to not match object #{exp}",B,w,!1]),R=N===0?H:`${H} -(${N} matching ${N===1?"property":"properties"} omitted from actual)`;throw new Q(R,{showDiff:!0,expected:B,actual:w})}}),J("toMatch",function(B){let U=this._obj;if(typeof U!=="string")throw TypeError(`.toMatch() expects to receive a string, but got ${typeof U}`);return this.assert(typeof B==="string"?U.includes(B):U.match(B),"expected #{this} to match #{exp}","expected #{this} not to match #{exp}",B,U)}),J("toContain",function(B){let U=this._obj;if(typeof Node<"u"&&U instanceof Node){if(!(B instanceof Node))throw TypeError(`toContain() expected a DOM node as the argument, but got ${typeof B}`);return this.assert(U.contains(B),"expected #{this} to contain element #{exp}","expected #{this} not to contain element #{exp}",B,U)}if(typeof DOMTokenList<"u"&&U instanceof DOMTokenList){N1(B,"class name",["string"]);let P=$.flag(this,"negate")?U.value.replace(B,"").trim():`${U.value} ${B}`;return this.assert(U.contains(B),`expected "${U.value}" to contain "${B}"`,`expected "${U.value}" not to contain "${B}"`,P,U.value)}if(typeof U==="string"&&typeof B==="string")return this.assert(U.includes(B),"expected #{this} to contain #{exp}","expected #{this} not to contain #{exp}",B,U);if(U!=null&&typeof U!=="string")$.flag(this,"object",Array.from(U));return this.contain(B)}),J("toContainEqual",function(B){let U=$.flag(this,"object"),K=Array.from(U).findIndex((P)=>{return E(P,B,X)});this.assert(K!==-1,"expected #{this} to deep equally contain #{exp}","expected #{this} to not deep equally contain #{exp}",B)}),J("toBeTruthy",function(){let B=$.flag(this,"object");this.assert(Boolean(B),"expected #{this} to be truthy","expected #{this} to not be truthy",!0,B)}),J("toBeFalsy",function(){let B=$.flag(this,"object");this.assert(!B,"expected #{this} to be falsy","expected #{this} to not be falsy",!1,B)}),J("toBeGreaterThan",function(B){let U=this._obj;return N1(U,"actual",["number","bigint"]),N1(B,"expected",["number","bigint"]),this.assert(U>B,`expected ${U} to be greater than ${B}`,`expected ${U} to be not greater than ${B}`,B,U,!1)}),J("toBeGreaterThanOrEqual",function(B){let U=this._obj;return N1(U,"actual",["number","bigint"]),N1(B,"expected",["number","bigint"]),this.assert(U>=B,`expected ${U} to be greater than or equal to ${B}`,`expected ${U} to be not greater than or equal to ${B}`,B,U,!1)}),J("toBeLessThan",function(B){let U=this._obj;return N1(U,"actual",["number","bigint"]),N1(B,"expected",["number","bigint"]),this.assert(UString(M).replace(/([.[\]])/g,"\\$1")).join(".");let U=this._obj;if(U==null)throw TypeError(`.toHaveProperty() expects to receive a valid object, but got ${U}`);let[K,P]=B,w=()=>{if(Object.hasOwn(U,K))return{value:U[K],exists:!0};return $.getPathInfo(U,K)},{value:N,exists:H}=w(),R=H&&(B.length===1||E(P,N,X)),O=B.length===1?"":` with value ${E6(P,{truncate:40})}`;return this.assert(R,`expected #{this} to have property "${K}"${O}`,`expected #{this} to not have property "${K}"${O}`,P,H?N:void 0)}),J("toBeCloseTo",function(B,U=2){let K=this._obj,P=!1,w=0,N=0;if(B===Number.POSITIVE_INFINITY&&K===Number.POSITIVE_INFINITY)P=!0;else if(B===Number.NEGATIVE_INFINITY&&K===Number.NEGATIVE_INFINITY)P=!0;else w=10**-U/2,N=Math.abs(K-B),P=N0,w=$.flag(this,"negate"),N=$.getMessage(this,[P,`expected "${U}" to be called at least once`,`expected "${U}" to not be called at all, but actually been called ${K} times`,!0,P]);if(P&&w)N=T9(B,N);if(P&&w||!P&&!w)throw new Q(N)});function Y(B,U){return B.length===U.length&&B.every((K,P)=>E(K,U[P],[...X,W1]))}J(["toHaveBeenCalledWith","toBeCalledWith"],function(...B){let U=G(this),K=U.getMockName(),P=U.mock.calls.some((N)=>Y(N,B)),w=$.flag(this,"negate");if(P&&w||!P&&!w){let N=$.getMessage(this,[P,`expected "${K}" to be called with arguments: #{exp}`,`expected "${K}" to not be called with arguments: #{exp}`,B]);throw new Q(T9(U,N,B))}}),J("toHaveBeenCalledExactlyOnceWith",function(...B){let U=G(this),K=U.getMockName(),P=U.mock.calls.length,N=U.mock.calls.some((R)=>Y(R,B))&&P===1,H=$.flag(this,"negate");if(N&&H||!N&&!H){let R=$.getMessage(this,[N,`expected "${K}" to be called once with arguments: #{exp}`,`expected "${K}" to not be called once with arguments: #{exp}`,B]);throw new Q(T9(U,R,B))}}),J("toHaveBeenNthCalledWith",function(B,...U){let K=G(this),P=K.getMockName(),w=K.mock.calls[B-1],N=K.mock.calls.length,H=B<=N;this.assert(w&&Y(w,U),`expected ${m1(B)} "${P}" call to have been called with #{exp}${H?"":`, but called only ${N} times`}`,`expected ${m1(B)} "${P}" call to not have been called with #{exp}`,U,w,H)}),J("toHaveBeenLastCalledWith",function(...B){let U=G(this),K=U.getMockName(),P=U.mock.calls.at(-1);this.assert(P&&Y(P,B),`expected last "${K}" call to have been called with #{exp}`,`expected last "${K}" call to not have been called with #{exp}`,B,P)});function z(B,U,K){let P=B.mock.invocationCallOrder,w=U.mock.invocationCallOrder;if(P.length===0)return!K;if(w.length===0)return!1;return P[0]"u"||B instanceof RegExp)return this.throws(B);let U=this._obj,K=$.flag(this,"promise"),P=$.flag(this,"negate"),w=null;if(K==="rejects")w=U;else if(K==="resolves"&&typeof U!=="function")if(!P){let H=$.flag(this,"message")||"expected promise to throw an error, but it didn't";throw new Q(H,{showDiff:!1},$.flag(this,"ssfi"))}else return;else{let H=!1;try{U()}catch(R){H=!0,w=R}if(!H&&!P){let R=$.flag(this,"message")||"expected function to throw an error, but it didn't";throw new Q(R,{showDiff:!1},$.flag(this,"ssfi"))}}if(typeof B==="function"){let H=B.name||B.prototype.constructor.name;return this.assert(w&&w instanceof B,`expected error to be instance of ${H}`,`expected error not to be instance of ${H}`,B,w)}if(l1(B)){let H=E(w,B,[...X,W1]);return this.assert(H,"expected a thrown error to be #{exp}","expected a thrown error not to be #{exp}",B,w)}if(typeof B==="object"&&"asymmetricMatch"in B&&typeof B.asymmetricMatch==="function"){let H=B;return this.assert(w&&H.asymmetricMatch(w),"expected error to match asymmetric matcher","expected error not to match asymmetric matcher",H,w)}let N=E(w,B,[...X,W1]);return this.assert(N,"expected a thrown value to equal #{exp}","expected a thrown value not to equal #{exp}",B,w)}),[{name:"toHaveResolved",condition:(B)=>B.mock.settledResults.length>0&&B.mock.settledResults.some(({type:U})=>U==="fulfilled"),action:"resolved"},{name:["toHaveReturned","toReturn"],condition:(B)=>B.mock.calls.length>0&&B.mock.results.some(({type:U})=>U!=="throw"),action:"called"}].forEach(({name:B,condition:U,action:K})=>{J(B,function(){let P=G(this),w=P.getMockName(),N=U(P);this.assert(N,`expected "${w}" to be successfully ${K} at least once`,`expected "${w}" to not be successfully ${K}`,N,!N,!1)})}),[{name:"toHaveResolvedTimes",condition:(B,U)=>B.mock.settledResults.reduce((K,{type:P})=>P==="fulfilled"?++K:K,0)===U,action:"resolved"},{name:["toHaveReturnedTimes","toReturnTimes"],condition:(B,U)=>B.mock.results.reduce((K,{type:P})=>P==="throw"?K:++K,0)===U,action:"called"}].forEach(({name:B,condition:U,action:K})=>{J(B,function(P){let w=G(this),N=w.getMockName(),H=U(w,P);this.assert(H,`expected "${N}" to be successfully ${K} ${P} times`,`expected "${N}" to not be successfully ${K} ${P} times`,`expected resolved times: ${P}`,`received resolved times: ${H}`,!1)})}),[{name:"toHaveResolvedWith",condition:(B,U)=>B.mock.settledResults.some(({type:K,value:P})=>K==="fulfilled"&&E(U,P)),action:"resolve"},{name:["toHaveReturnedWith","toReturnWith"],condition:(B,U)=>B.mock.results.some(({type:K,value:P})=>K==="return"&&E(U,P)),action:"return"}].forEach(({name:B,condition:U,action:K})=>{J(B,function(P){let w=G(this),N=U(w,P),H=$.flag(this,"negate");if(N&&H||!N&&!H){let R=w.getMockName(),O=$.getMessage(this,[N,`expected "${R}" to ${K} with: #{exp} at least once`,`expected "${R}" to not ${K} with: #{exp}`,P]),M=K==="return"?w.mock.results:w.mock.settledResults;throw new Q(RX(w,M,O,P))}})}),[{name:"toHaveLastResolvedWith",condition:(B,U)=>{let K=B.mock.settledResults.at(-1);return Boolean(K&&K.type==="fulfilled"&&E(K.value,U))},action:"resolve"},{name:"toHaveLastReturnedWith",condition:(B,U)=>{let K=B.mock.results.at(-1);return Boolean(K&&K.type==="return"&&E(K.value,U))},action:"return"}].forEach(({name:B,condition:U,action:K})=>{J(B,function(P){let w=G(this),H=(K==="return"?w.mock.results:w.mock.settledResults).at(-1),R=w.getMockName();this.assert(U(w,P),`expected last "${R}" call to ${K} #{exp}`,`expected last "${R}" call to not ${K} #{exp}`,P,H?.value)})}),[{name:"toHaveNthResolvedWith",condition:(B,U,K)=>{let P=B.mock.settledResults[U-1];return P&&P.type==="fulfilled"&&E(P.value,K)},action:"resolve"},{name:"toHaveNthReturnedWith",condition:(B,U,K)=>{let P=B.mock.results[U-1];return P&&P.type==="return"&&E(P.value,K)},action:"return"}].forEach(({name:B,condition:U,action:K})=>{J(B,function(P,w){let N=G(this),H=N.getMockName(),O=(K==="return"?N.mock.results:N.mock.settledResults)[P-1],M=`${m1(P)} call`;this.assert(U(N,P,w),`expected ${M} "${H}" call to ${K} #{exp}`,`expected ${M} "${H}" call to not ${K} #{exp}`,w,O?.value)})}),J("withContext",function(B){for(let U in B)$.flag(this,U,B[U]);return this}),$.addProperty(Z.Assertion.prototype,"resolves",function(){let U=Error("resolves");$.flag(this,"promise","resolves"),$.flag(this,"error",U);let K=$.flag(this,"vitest-test"),P=$.flag(this,"object");if($.flag(this,"poll"))throw SyntaxError("expect.poll() is not supported in combination with .resolves");if(typeof P?.then!=="function")throw TypeError(`You must provide a Promise to expect() when using .resolves, not '${typeof P}'.`);let w=new Proxy(this,{get:(N,H,R)=>{let O=Reflect.get(N,H,R);if(typeof O!=="function")return O instanceof Z.Assertion?w:O;return(...M)=>{$.flag(this,"_name",H);let T=Promise.resolve(P).then((I)=>{return $.flag(this,"object",I),O.call(this,...M)},(I)=>{let C=new Q(`promise rejected "${$.inspect(I)}" instead of resolving`,{showDiff:!1});throw C.cause=I,C}).catch((I)=>{if(l1(I)&&U.stack)I.stack=U.stack.replace(U.message,I.message);throw I});return T7(K,T,L7($,this,!!M.length),U,$.flag(this,"soft"))}}});return w}),$.addProperty(Z.Assertion.prototype,"rejects",function(){let U=Error("rejects");$.flag(this,"promise","rejects"),$.flag(this,"error",U);let K=$.flag(this,"vitest-test"),P=$.flag(this,"object"),w=typeof P==="function"?P():P;if($.flag(this,"poll"))throw SyntaxError("expect.poll() is not supported in combination with .rejects");if(typeof w?.then!=="function")throw TypeError(`You must provide a Promise to expect() when using .rejects, not '${typeof w}'.`);let N=new Proxy(this,{get:(H,R,O)=>{let M=Reflect.get(H,R,O);if(typeof M!=="function")return M instanceof Z.Assertion?N:M;return(...T)=>{$.flag(this,"_name",R);let I=Promise.resolve(w).then((C)=>{throw new Q(`promise resolved "${$.inspect(C)}" instead of rejecting`,{showDiff:!0,expected:Error("rejected promise"),actual:C})},(C)=>{return $.flag(this,"object",C),M.call(this,...T)}).catch((C)=>{if(l1(C)&&U.stack)C.stack=U.stack.replace(U.message,C.message);throw C});return T7(K,I,L7($,this,!!T.length),U,$.flag(this,"soft"))}}});return N})};function T9(Z,$,Q){if(Z.mock.calls.length)$+=f.gray(` +await ${X} +`);if(W.stack)U.stack=W.stack.replace(W.message,U.message);throw U}}),{then(U,z){return G=!0,Q.then(U,z)},catch(U){return G=!0,Q.catch(U)},finally(U){return G=!0,Q.finally(U)},[Symbol.toStringTag]:"Promise"}}return Q}function j6(Z,Q){Z.result||={state:"fail"},Z.result.state="fail",Z.result.errors||=[],Z.result.errors.push(R6(Q))}function k5(Z,Q,X){return function(...W){if(Q!=="withTest")Z.flag(this,"_name",Q);if(!Z.flag(this,"soft"))try{return X.apply(this,W)}finally{}let $=Z.flag(this,"vitest-test");if(!$)throw Error("expect.soft() can only be used inside a test");try{let J=X.apply(this,W);if(J&&typeof J==="object"&&typeof J.then==="function")return J.then(B6,(G)=>{j6($,G)});return J}catch(J){j6($,J)}}}var S5=(Z,Q)=>{let{AssertionError:X}=Z,W=x6();function $(Y,K){let B=(P)=>{let w=k5(Q,P,K);Q.addMethod(Z.Assertion.prototype,P,w),Q.addMethod(globalThis[l0].matchers,P,w)};if(Array.isArray(Y))Y.forEach((P)=>B(P));else B(Y)}["throw","throws","Throw"].forEach((Y)=>{Q.overwriteMethod(Z.Assertion.prototype,Y,(K)=>{return function(...B){let P=Q.flag(this,"promise"),w=Q.flag(this,"object"),N=Q.flag(this,"negate");if(P==="rejects")Q.flag(this,"object",()=>{throw w});else if(P==="resolves"&&typeof w!=="function")if(!N){let H=Q.flag(this,"message")||"expected promise to throw an error, but it didn't";throw new X(H,{showDiff:!1},Q.flag(this,"ssfi"))}else return;K.apply(this,B)}})}),$("withTest",function(Y){return Q.flag(this,"vitest-test",Y),this}),$("toEqual",function(Y){let K=Q.flag(this,"object"),B=E(K,Y,[...W,W9]);return this.assert(B,"expected #{this} to deeply equal #{exp}","expected #{this} to not deeply equal #{exp}",Y,K)}),$("toStrictEqual",function(Y){let K=Q.flag(this,"object"),B=E(K,Y,[...W,W9,q5,I6,H5],!0);return this.assert(B,"expected #{this} to strictly equal #{exp}","expected #{this} to not strictly equal #{exp}",Y,K)}),$("toBe",function(Y){let K=this._obj,B=Object.is(K,Y),P="";if(!B){if(E(K,Y,[...W,W9,q5,I6,H5],!0))P="toStrictEqual";else if(E(K,Y,[...W,W9]))P="toEqual"}return this.assert(B,aQ(P),"expected #{this} not to be #{exp} // Object.is equality",Y,K)}),$("toMatchObject",function(Y){let K=this._obj,B=E(K,Y,[...W,W9,$0]),P=Q.flag(this,"negate"),{subset:w,stripped:N}=tQ(K,Y,W);if(B&&P||!B&&!P){let H=Q.getMessage(this,[B,"expected #{this} to match object #{exp}","expected #{this} to not match object #{exp}",Y,w,!1]),F=N===0?H:`${H} +(${N} matching ${N===1?"property":"properties"} omitted from actual)`;throw new X(F,{showDiff:!0,expected:Y,actual:w})}}),$("toMatch",function(Y){let K=this._obj;if(typeof K!=="string")throw TypeError(`.toMatch() expects to receive a string, but got ${typeof K}`);return this.assert(typeof Y==="string"?K.includes(Y):K.match(Y),"expected #{this} to match #{exp}","expected #{this} not to match #{exp}",Y,K)}),$("toContain",function(Y){let K=this._obj;if(typeof Node<"u"&&K instanceof Node){if(!(Y instanceof Node))throw TypeError(`toContain() expected a DOM node as the argument, but got ${typeof Y}`);return this.assert(K.contains(Y),"expected #{this} to contain element #{exp}","expected #{this} not to contain element #{exp}",Y,K)}if(typeof DOMTokenList<"u"&&K instanceof DOMTokenList){H9(Y,"class name",["string"]);let P=Q.flag(this,"negate")?K.value.replace(Y,"").trim():`${K.value} ${Y}`;return this.assert(K.contains(Y),`expected "${K.value}" to contain "${Y}"`,`expected "${K.value}" not to contain "${Y}"`,P,K.value)}if(typeof K==="string"&&typeof Y==="string")return this.assert(K.includes(Y),"expected #{this} to contain #{exp}","expected #{this} not to contain #{exp}",Y,K);if(K!=null&&typeof K!=="string")Q.flag(this,"object",Array.from(K));return this.contain(Y)}),$("toContainEqual",function(Y){let K=Q.flag(this,"object"),B=Array.from(K).findIndex((P)=>{return E(P,Y,W)});this.assert(B!==-1,"expected #{this} to deep equally contain #{exp}","expected #{this} to not deep equally contain #{exp}",Y)}),$("toBeTruthy",function(){let Y=Q.flag(this,"object");this.assert(Boolean(Y),"expected #{this} to be truthy","expected #{this} to not be truthy",!0,Y)}),$("toBeFalsy",function(){let Y=Q.flag(this,"object");this.assert(!Y,"expected #{this} to be falsy","expected #{this} to not be falsy",!1,Y)}),$("toBeGreaterThan",function(Y){let K=this._obj;return H9(K,"actual",["number","bigint"]),H9(Y,"expected",["number","bigint"]),this.assert(K>Y,`expected ${K} to be greater than ${Y}`,`expected ${K} to be not greater than ${Y}`,Y,K,!1)}),$("toBeGreaterThanOrEqual",function(Y){let K=this._obj;return H9(K,"actual",["number","bigint"]),H9(Y,"expected",["number","bigint"]),this.assert(K>=Y,`expected ${K} to be greater than or equal to ${Y}`,`expected ${K} to be not greater than or equal to ${Y}`,Y,K,!1)}),$("toBeLessThan",function(Y){let K=this._obj;return H9(K,"actual",["number","bigint"]),H9(Y,"expected",["number","bigint"]),this.assert(KString(O).replace(/([.[\]])/g,"\\$1")).join(".");let K=this._obj;if(K==null)throw TypeError(`.toHaveProperty() expects to receive a valid object, but got ${K}`);let[B,P]=Y,w=()=>{if(Object.hasOwn(K,B))return{value:K[B],exists:!0};return Q.getPathInfo(K,B)},{value:N,exists:H}=w(),F=H&&(Y.length===1||E(P,N,W)),M=Y.length===1?"":` with value ${I8(P,{truncate:40})}`;return this.assert(F,`expected #{this} to have property "${B}"${M}`,`expected #{this} to not have property "${B}"${M}`,P,H?N:void 0)}),$("toBeCloseTo",function(Y,K=2){let B=this._obj,P=!1,w=0,N=0;if(Y===Number.POSITIVE_INFINITY&&B===Number.POSITIVE_INFINITY)P=!0;else if(Y===Number.NEGATIVE_INFINITY&&B===Number.NEGATIVE_INFINITY)P=!0;else w=10**-K/2,N=Math.abs(B-Y),P=N0,w=Q.flag(this,"negate"),N=Q.getMessage(this,[P,`expected "${K}" to be called at least once`,`expected "${K}" to not be called at all, but actually been called ${B} times`,!0,P]);if(P&&w)N=O6(Y,N);if(P&&w||!P&&!w)throw new X(N)});function U(Y,K){return Y.length===K.length&&Y.every((B,P)=>E(B,K[P],[...W,W9]))}$(["toHaveBeenCalledWith","toBeCalledWith"],function(...Y){let K=G(this),B=K.getMockName(),P=K.mock.calls.some((N)=>U(N,Y)),w=Q.flag(this,"negate");if(P&&w||!P&&!w){let N=Q.getMessage(this,[P,`expected "${B}" to be called with arguments: #{exp}`,`expected "${B}" to not be called with arguments: #{exp}`,Y]);throw new X(O6(K,N,Y))}}),$("toHaveBeenCalledExactlyOnceWith",function(...Y){let K=G(this),B=K.getMockName(),P=K.mock.calls.length,N=K.mock.calls.some((F)=>U(F,Y))&&P===1,H=Q.flag(this,"negate");if(N&&H||!N&&!H){let F=Q.getMessage(this,[N,`expected "${B}" to be called once with arguments: #{exp}`,`expected "${B}" to not be called once with arguments: #{exp}`,Y]);throw new X(O6(K,F,Y))}}),$("toHaveBeenNthCalledWith",function(Y,...K){let B=G(this),P=B.getMockName(),w=B.mock.calls[Y-1],N=B.mock.calls.length,H=Y<=N;this.assert(w&&U(w,K),`expected ${f9(Y)} "${P}" call to have been called with #{exp}${H?"":`, but called only ${N} times`}`,`expected ${f9(Y)} "${P}" call to not have been called with #{exp}`,K,w,H)}),$("toHaveBeenLastCalledWith",function(...Y){let K=G(this),B=K.getMockName(),P=K.mock.calls.at(-1);this.assert(P&&U(P,Y),`expected last "${B}" call to have been called with #{exp}`,`expected last "${B}" call to not have been called with #{exp}`,Y,P)});function z(Y,K,B){let P=Y.mock.invocationCallOrder,w=K.mock.invocationCallOrder;if(P.length===0)return!B;if(w.length===0)return!1;return P[0]"u"||Y instanceof RegExp)return this.throws(Y);let K=this._obj,B=Q.flag(this,"promise"),P=Q.flag(this,"negate"),w=null;if(B==="rejects")w=K;else if(B==="resolves"&&typeof K!=="function")if(!P){let H=Q.flag(this,"message")||"expected promise to throw an error, but it didn't";throw new X(H,{showDiff:!1},Q.flag(this,"ssfi"))}else return;else{let H=!1;try{K()}catch(F){H=!0,w=F}if(!H&&!P){let F=Q.flag(this,"message")||"expected function to throw an error, but it didn't";throw new X(F,{showDiff:!1},Q.flag(this,"ssfi"))}}if(typeof Y==="function"){let H=Y.name||Y.prototype.constructor.name;return this.assert(w&&w instanceof Y,`expected error to be instance of ${H}`,`expected error not to be instance of ${H}`,Y,w)}if(d9(Y)){let H=E(w,Y,[...W,W9]);return this.assert(H,"expected a thrown error to be #{exp}","expected a thrown error not to be #{exp}",Y,w)}if(typeof Y==="object"&&"asymmetricMatch"in Y&&typeof Y.asymmetricMatch==="function"){let H=Y;return this.assert(w&&H.asymmetricMatch(w),"expected error to match asymmetric matcher","expected error not to match asymmetric matcher",H,w)}let N=E(w,Y,[...W,W9]);return this.assert(N,"expected a thrown value to equal #{exp}","expected a thrown value not to equal #{exp}",Y,w)}),[{name:"toHaveResolved",condition:(Y)=>Y.mock.settledResults.length>0&&Y.mock.settledResults.some(({type:K})=>K==="fulfilled"),action:"resolved"},{name:["toHaveReturned","toReturn"],condition:(Y)=>Y.mock.calls.length>0&&Y.mock.results.some(({type:K})=>K!=="throw"),action:"called"}].forEach(({name:Y,condition:K,action:B})=>{$(Y,function(){let P=G(this),w=P.getMockName(),N=K(P);this.assert(N,`expected "${w}" to be successfully ${B} at least once`,`expected "${w}" to not be successfully ${B}`,N,!N,!1)})}),[{name:"toHaveResolvedTimes",condition:(Y,K)=>Y.mock.settledResults.reduce((B,{type:P})=>P==="fulfilled"?++B:B,0)===K,action:"resolved"},{name:["toHaveReturnedTimes","toReturnTimes"],condition:(Y,K)=>Y.mock.results.reduce((B,{type:P})=>P==="throw"?B:++B,0)===K,action:"called"}].forEach(({name:Y,condition:K,action:B})=>{$(Y,function(P){let w=G(this),N=w.getMockName(),H=K(w,P);this.assert(H,`expected "${N}" to be successfully ${B} ${P} times`,`expected "${N}" to not be successfully ${B} ${P} times`,`expected resolved times: ${P}`,`received resolved times: ${H}`,!1)})}),[{name:"toHaveResolvedWith",condition:(Y,K)=>Y.mock.settledResults.some(({type:B,value:P})=>B==="fulfilled"&&E(K,P)),action:"resolve"},{name:["toHaveReturnedWith","toReturnWith"],condition:(Y,K)=>Y.mock.results.some(({type:B,value:P})=>B==="return"&&E(K,P)),action:"return"}].forEach(({name:Y,condition:K,action:B})=>{$(Y,function(P){let w=G(this),N=K(w,P),H=Q.flag(this,"negate");if(N&&H||!N&&!H){let F=w.getMockName(),M=Q.getMessage(this,[N,`expected "${F}" to ${B} with: #{exp} at least once`,`expected "${F}" to not ${B} with: #{exp}`,P]),O=B==="return"?w.mock.results:w.mock.settledResults;throw new X(ZX(w,O,M,P))}})}),[{name:"toHaveLastResolvedWith",condition:(Y,K)=>{let B=Y.mock.settledResults.at(-1);return Boolean(B&&B.type==="fulfilled"&&E(B.value,K))},action:"resolve"},{name:"toHaveLastReturnedWith",condition:(Y,K)=>{let B=Y.mock.results.at(-1);return Boolean(B&&B.type==="return"&&E(B.value,K))},action:"return"}].forEach(({name:Y,condition:K,action:B})=>{$(Y,function(P){let w=G(this),H=(B==="return"?w.mock.results:w.mock.settledResults).at(-1),F=w.getMockName();this.assert(K(w,P),`expected last "${F}" call to ${B} #{exp}`,`expected last "${F}" call to not ${B} #{exp}`,P,H?.value)})}),[{name:"toHaveNthResolvedWith",condition:(Y,K,B)=>{let P=Y.mock.settledResults[K-1];return P&&P.type==="fulfilled"&&E(P.value,B)},action:"resolve"},{name:"toHaveNthReturnedWith",condition:(Y,K,B)=>{let P=Y.mock.results[K-1];return P&&P.type==="return"&&E(P.value,B)},action:"return"}].forEach(({name:Y,condition:K,action:B})=>{$(Y,function(P,w){let N=G(this),H=N.getMockName(),M=(B==="return"?N.mock.results:N.mock.settledResults)[P-1],O=`${f9(P)} call`;this.assert(K(N,P,w),`expected ${O} "${H}" call to ${B} #{exp}`,`expected ${O} "${H}" call to not ${B} #{exp}`,w,M?.value)})}),$("withContext",function(Y){for(let K in Y)Q.flag(this,K,Y[K]);return this}),Q.addProperty(Z.Assertion.prototype,"resolves",function(){let K=Error("resolves");Q.flag(this,"promise","resolves"),Q.flag(this,"error",K);let B=Q.flag(this,"vitest-test"),P=Q.flag(this,"object");if(Q.flag(this,"poll"))throw SyntaxError("expect.poll() is not supported in combination with .resolves");if(typeof P?.then!=="function")throw TypeError(`You must provide a Promise to expect() when using .resolves, not '${typeof P}'.`);let w=new Proxy(this,{get:(N,H,F)=>{let M=Reflect.get(N,H,F);if(typeof M!=="function")return M instanceof Z.Assertion?w:M;return(...O)=>{Q.flag(this,"_name",H);let A=Promise.resolve(P).then((I)=>{return Q.flag(this,"object",I),M.call(this,...O)},(I)=>{let L=new X(`promise rejected "${Q.inspect(I)}" instead of resolving`,{showDiff:!1});throw L.cause=I,L}).catch((I)=>{if(d9(I)&&K.stack)I.stack=K.stack.replace(K.message,I.message);throw I});return N5(B,A,P5(Q,this,!!O.length),K,Q.flag(this,"soft"))}}});return w}),Q.addProperty(Z.Assertion.prototype,"rejects",function(){let K=Error("rejects");Q.flag(this,"promise","rejects"),Q.flag(this,"error",K);let B=Q.flag(this,"vitest-test"),P=Q.flag(this,"object"),w=typeof P==="function"?P():P;if(Q.flag(this,"poll"))throw SyntaxError("expect.poll() is not supported in combination with .rejects");if(typeof w?.then!=="function")throw TypeError(`You must provide a Promise to expect() when using .rejects, not '${typeof w}'.`);let N=new Proxy(this,{get:(H,F,M)=>{let O=Reflect.get(H,F,M);if(typeof O!=="function")return O instanceof Z.Assertion?N:O;return(...A)=>{Q.flag(this,"_name",F);let I=Promise.resolve(w).then((L)=>{throw new X(`promise resolved "${Q.inspect(L)}" instead of rejecting`,{showDiff:!0,expected:Error("rejected promise"),actual:L})},(L)=>{return Q.flag(this,"object",L),O.call(this,...A)}).catch((L)=>{if(d9(L)&&K.stack)L.stack=K.stack.replace(K.message,L.message);throw L});return N5(B,I,P5(Q,this,!!A.length),K,Q.flag(this,"soft"))}}});return N})};function O6(Z,Q,X){if(Z.mock.calls.length)Q+=f.gray(` Received: -${Z.mock.calls.map((X,J)=>{let W=f.bold(` ${m1(J+1)} ${Z.getMockName()} call: +${Z.mock.calls.map((W,$)=>{let J=f.bold(` ${f9($+1)} ${Z.getMockName()} call: -`);if(Q)W+=v1(Q,X,{omitAnnotationLines:!0});else W+=a(X).split(` +`);if(X)J+=E9(X,W,{omitAnnotationLines:!0});else J+=o(W).split(` `).map((G)=>` ${G}`).join(` -`);return W+=` -`,W}).join(` -`)}`);return $+=f.gray(` +`);return J+=` +`,J}).join(` +`)}`);return Q+=f.gray(` Number of calls: ${f.bold(Z.mock.calls.length)} -`),$}function RX(Z,$,Q,X){if($.length)Q+=f.gray(` +`),Q}function ZX(Z,Q,X,W){if(Q.length)X+=f.gray(` Received: -${$.map((J,W)=>{let G=f.bold(` ${m1(W+1)} ${Z.getMockName()} call return: +${Q.map(($,J)=>{let G=f.bold(` ${f9(J+1)} ${Z.getMockName()} call return: -`);if(X)G+=v1(X,J.value,{omitAnnotationLines:!0});else G+=a(J).split(` -`).map((Y)=>` ${Y}`).join(` +`);if(W)G+=E9(W,$.value,{omitAnnotationLines:!0});else G+=o($).split(` +`).map((U)=>` ${U}`).join(` `);return G+=` `,G}).join(` -`)}`);return Q+=f.gray(` +`)}`);return X+=f.gray(` Number of calls: ${f.bold(Z.mock.calls.length)} -`),Q}function FX(Z,$){let Q=Z._obj,X=K1.flag(Z,"negate"),J=K1.flag(Z,"promise")||"",W=K1.flag(Z,"message"),G={...S7(),diff:v1,stringify:a,iterableEquality:W1,subsetEquality:G0},Y=K1.flag(Z,"vitest-test"),z=Y?.fullTestName??"";if(Y?.type!=="test")Y=void 0;let B={...B0($),currentTestName:z,customTesters:b9(),isNot:X,utils:G,promise:J,equals:E,suppressedErrors:[],soft:K1.flag(Z,"soft"),poll:K1.flag(Z,"poll"),assertion:Z};return Object.assign(B,{task:Y}),{state:B,isNot:X,obj:Q,customMessage:W}}class h9 extends Error{actual;expected;__vitest_error_context__;constructor(Z,$,Q,X){super(Z);this.actual=$,this.expected=Q,this.__vitest_error_context__=X}}function MX(Z,$,Q){return(X,J)=>{Object.entries(Q).forEach(([W,G])=>{function Y(...K){let{state:P,isNot:w,obj:N,customMessage:H}=FX(this,$),R=G.call(P,N,...K);if(R&&typeof R==="object"&&typeof R.then==="function")return R.then(({pass:L,message:S,actual:b,expected:t,meta:u})=>{if(L&&w||!L&&!w){let v=(H?`${H}: `:"")+S();throw new h9(v,b,t,{assertionName:W,meta:u})}});let{pass:O,message:M,actual:T,expected:I,meta:C}=R;if(O&&w||!O&&!w){let A=(H?`${H}: `:"")+M();throw new h9(A,T,I,{assertionName:W,meta:C})}}let z=b7(J,W,Y);if(J.addMethod(globalThis[o0].matchers,W,z),J.addMethod(Z.Assertion.prototype,W,z),G.__vitest_poll_takeover__){let K=Z.Assertion.prototype[W];Object.defineProperty(K,"__vitest_poll_takeover__",{value:!0})}class B extends q1{constructor(K=!1,...P){super(P,K)}asymmetricMatch(K){let{pass:P}=G.call(this.getMatcherContext($),K,...this.sample);return this.inverse?!P:P}toString(){return`${this.inverse?"not.":""}${W}`}getExpectedType(){return"any"}toAsymmetricMatcher(){return`${this.toString()}<${this.sample.map((K)=>a(K)).join(", ")}>`}}let U=(...K)=>new B(!1,...K);Object.defineProperty($,W,{configurable:!0,enumerable:!0,value:U,writable:!0}),Object.defineProperty($.not,W,{configurable:!0,enumerable:!0,value:(...K)=>new B(!0,...K),writable:!0}),Object.defineProperty(globalThis[r0],W,{configurable:!0,enumerable:!0,value:U,writable:!0})})}}var f7=(Z,$)=>{$.addMethod(Z.expect,"extend",(Q,X)=>{T1(MX(Z,Q,X))})};n9();T1(f7);T1(u7);T1(y7);function OX(){let Z=($,Q)=>{let{assertionCalls:X}=B0(Z);return t0({assertionCalls:X+1},Z),L1($,Q)};return Object.assign(Z,L1),Object.assign(Z,globalThis[r0]),Z.getState=()=>B0(Z),Z.setState=($)=>t0($,Z),t0({assertionCalls:0,isExpectingAssertions:!1,isExpectingAssertionsError:null,expectedAssertionsNumber:null,expectedAssertionsNumberErrorGen:null},Z),Z.assert=V,Z.extend=($)=>L1.extend(Z,$),Z.unreachable=($)=>V.fail("expected"+($?' "'+$+'" ':" ")+"not to be reached"),Z}globalThis.__bundles=globalThis.__bundles||{};globalThis.__bundles["vitest-expect"]={createExpect:OX,fn:V7,spyOn:w7,isMockFunction:F1};})(); +`),X}function QX(Z,Q){let X=Z._obj,W=z9.flag(Z,"negate"),$=z9.flag(Z,"promise")||"",J=z9.flag(Z,"message"),G={...R5(),diff:E9,stringify:o,iterableEquality:W9,subsetEquality:$0},U=z9.flag(Z,"vitest-test"),z=U?.fullTestName??"";if(U?.type!=="test")U=void 0;let Y={...J0(Q),currentTestName:z,customTesters:x6(),isNot:W,utils:G,promise:$,equals:E,suppressedErrors:[],soft:z9.flag(Z,"soft"),poll:z9.flag(Z,"poll"),assertion:Z};return Object.assign(Y,{task:U}),{state:Y,isNot:W,obj:X,customMessage:J}}class _6 extends Error{actual;expected;__vitest_error_context__;constructor(Z,Q,X,W){super(Z);this.actual=Q,this.expected=X,this.__vitest_error_context__=W}}function XX(Z,Q,X){return(W,$)=>{Object.entries(X).forEach(([J,G])=>{function U(...B){let{state:P,isNot:w,obj:N,customMessage:H}=QX(this,Q),F=G.call(P,N,...B);if(F&&typeof F==="object"&&typeof F.then==="function")return F.then(({pass:C,message:S,actual:b,expected:a,meta:u})=>{if(C&&w||!C&&!w){let v=(H?`${H}: `:"")+S();throw new _6(v,b,a,{assertionName:J,meta:u})}});let{pass:M,message:O,actual:A,expected:I,meta:L}=F;if(M&&w||!M&&!w){let T=(H?`${H}: `:"")+O();throw new _6(T,A,I,{assertionName:J,meta:L})}}let z=k5($,J,U);if($.addMethod(globalThis[l0].matchers,J,z),$.addMethod(Z.Assertion.prototype,J,z),G.__vitest_poll_takeover__){let B=Z.Assertion.prototype[J];Object.defineProperty(B,"__vitest_poll_takeover__",{value:!0})}class Y extends V9{constructor(B=!1,...P){super(P,B)}asymmetricMatch(B){let{pass:P}=G.call(this.getMatcherContext(Q),B,...this.sample);return this.inverse?!P:P}toString(){return`${this.inverse?"not.":""}${J}`}getExpectedType(){return"any"}toAsymmetricMatcher(){return`${this.toString()}<${this.sample.map((B)=>o(B)).join(", ")}>`}}let K=(...B)=>new Y(!1,...B);Object.defineProperty(Q,J,{configurable:!0,enumerable:!0,value:K,writable:!0}),Object.defineProperty(Q.not,J,{configurable:!0,enumerable:!0,value:(...B)=>new Y(!0,...B),writable:!0}),Object.defineProperty(globalThis[p0],J,{configurable:!0,enumerable:!0,value:K,writable:!0})})}}var j5=(Z,Q)=>{Q.addMethod(Z.expect,"extend",(X,W)=>{L9(XX(Z,X,W))})};u6();L9(j5);L9(S5);L9(A5);function WX(){let Z=(Q,X)=>{let{assertionCalls:W}=J0(Z);return o0({assertionCalls:W+1},Z),I9(Q,X)};return Object.assign(Z,I9),Object.assign(Z,globalThis[p0]),Z.getState=()=>J0(Z),Z.setState=(Q)=>o0(Q,Z),o0({assertionCalls:0,isExpectingAssertions:!1,isExpectingAssertionsError:null,expectedAssertionsNumber:null,expectedAssertionsNumberErrorGen:null},Z),Z.assert=V,Z.extend=(Q)=>I9.extend(Z,Q),Z.unreachable=(Q)=>V.fail("expected"+(Q?' "'+Q+'" ':" ")+"not to be reached"),Z}globalThis.__bundles=globalThis.__bundles||{};globalThis.__bundles["vitest-expect"]={createExpect:WX,fn:W5,spyOn:$5,isMockFunction:D9};})(); diff --git a/apps/sim/package.json b/apps/sim/package.json index 081a4e7507b..c5200c318c8 100644 --- a/apps/sim/package.json +++ b/apps/sim/package.json @@ -293,6 +293,8 @@ "@types/ssh2": "^1.15.5", "@vitejs/plugin-react": "^6.0.5", "@vitest/coverage-v8": "^5.0.1", + "@vitest/expect": "5.0.1", + "@vitest/spy": "5.0.1", "artillery": "2.0.34", "node-gyp": "12.4.0", "postcss": "^8", diff --git a/bun.lock b/bun.lock index 69ac6322289..23943348f64 100644 --- a/bun.lock +++ b/bun.lock @@ -417,6 +417,8 @@ "@types/ssh2": "^1.15.5", "@vitejs/plugin-react": "^6.0.5", "@vitest/coverage-v8": "^5.0.1", + "@vitest/expect": "5.0.1", + "@vitest/spy": "5.0.1", "artillery": "2.0.34", "node-gyp": "12.4.0", "postcss": "^8", @@ -2081,7 +2083,7 @@ "@stablelib/base64": ["@stablelib/base64@1.0.1", "", {}, "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ=="], - "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], @@ -2459,14 +2461,20 @@ "@vitest/coverage-v8": ["@vitest/coverage-v8@5.0.1", "", { "dependencies": { "@bcoe/v8-coverage": "^1.0.2", "@vitest/istanbul-lib-coverage": "^1.0.0", "@vitest/istanbul-lib-report": "^1.0.0", "ast-v8-to-istanbul": "^1.0.5", "magicast": "^0.5.4", "obug": "^2.1.4", "std-env": "^4.2.0", "tinyrainbow": "^3.1.1" }, "peerDependencies": { "@vitest/browser": "5.0.1", "vitest": "5.0.1" }, "optionalPeers": ["@vitest/browser"] }, "sha512-FRC8ACiudC3dI6MTplzRSYWHDRnIv2IPfbzs4FdoJNsMal/35sWV8hwIfV8ZcqzSPy+uXHeMVONt9CEqtOU17w=="], + "@vitest/expect": ["@vitest/expect@5.0.1", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "5.0.1", "@vitest/utils": "5.0.1", "chai": "^6.2.2", "tinyrainbow": "^3.1.1" } }, "sha512-U4YclNr7ds8kqZdtLcoVCYTaLRmaKeyjRjQHQ2We5iY0ZjegavIeBWJOr2F4pgUFuN0tO0bz/cVa4ERwKhf1Hg=="], + "@vitest/istanbul-lib-coverage": ["@vitest/istanbul-lib-coverage@1.0.1", "", {}, "sha512-k3DJZ8LhMBK9NS4SclF1ASD3OgXEWDorbIcPTRDK0/Zae6fRvu+fJRxtFdLfHsa9Y24beCdPnoNZ4LviTNstfA=="], "@vitest/istanbul-lib-report": ["@vitest/istanbul-lib-report@1.0.1", "", { "dependencies": { "@vitest/istanbul-lib-coverage": "1.0.1" } }, "sha512-1EOLRfsTMnyAr3+kEAsP4o9dhaDlGPpD7H5iLBBeq//YpNB1VIahkPhB+eRp9N2Dkfw8oySROjE3yf9XDeaIkQ=="], "@vitest/mocker": ["@vitest/mocker@5.0.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.31", "@vitest/spy": "5.0.1", "estree-walker": "^3.0.3", "magic-string": "^1.2.3" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-6K1DoBNAPGvuOcSsGA4D6x+5zEEff/KmOOP3uetT2TrGpVfI+HRHRnJJfKi5ib/g1vx8IYHQD8s0pbJz8WQI7Q=="], + "@vitest/pretty-format": ["@vitest/pretty-format@5.0.1", "", { "dependencies": { "tinyrainbow": "^3.1.1" } }, "sha512-6guWwj5d9bguuefTOvJoq387tfpkzSv554YdUEGzjJH2PnnmvzTLQ1UQSuAk5wBFVhf2CUmy/S/palOcb6dmpA=="], + "@vitest/spy": ["@vitest/spy@5.0.1", "", {}, "sha512-rbto/mF/SGERxEgYOek7Xm6B9b+y+mVoo+f4b2LymYO8zM1b7uB5nHuhVMTP2hxdzgxvGiZYGxGIaMvL5y180Q=="], + "@vitest/utils": ["@vitest/utils@5.0.1", "", { "dependencies": { "@vitest/pretty-format": "5.0.1", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.1" } }, "sha512-E9+yEA+jsfaoxZcUHFzEqUrQcoNh2EwrPT5efIqkUPUwD5Ua2Li9BRWaYeRwvzvdLgTSVrre8oKNeyrfg7KkdQ=="], + "@xmldom/is-dom-node": ["@xmldom/is-dom-node@1.0.1", "", {}, "sha512-CJDxIgE5I0FH+ttq/Fxy6nRpxP70+e2O048EPe85J2use3XKdatVM7dDVvFNjQudd9B49NPoZ+8PG49zj4Er8Q=="], "@xmldom/xmldom": ["@xmldom/xmldom@0.8.13", "", {}, "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw=="], @@ -2777,6 +2785,8 @@ "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], "cookie-parser": ["cookie-parser@1.4.7", "", { "dependencies": { "cookie": "0.7.2", "cookie-signature": "1.0.6" } }, "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw=="], @@ -4853,8 +4863,6 @@ "@a2a-js/sdk/jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="], - "@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "@antfu/install-pkg/package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], "@antfu/install-pkg/tinyexec": ["tinyexec@1.2.4", "", {}, "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg=="], @@ -5037,8 +5045,6 @@ "@better-auth/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.41.1", "", {}, "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA=="], - "@better-auth/core/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "@better-auth/core/jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="], "@better-auth/oauth-provider/jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="], @@ -5507,8 +5513,6 @@ "echarts/tslib": ["tslib@2.3.0", "", {}, "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="], - "effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "electron/@types/node": ["@types/node@24.13.3", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q=="], "electron-builder/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -5551,8 +5555,6 @@ "framer-motion/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "fumadocs-mdx/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "fumadocs-mdx/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], "fumadocs-mdx/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], @@ -5661,6 +5663,8 @@ "node-gyp/undici": ["undici@6.28.1", "", {}, "sha512-zWpdTVD54H48CIybL0rWQ3ukpb9d23wM7eH5RtfdmeP70cWHNjtfo7P4vZX+5CoDcO53J4Pu5uXp7lNfjc6DRA=="], + "nuqs/@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + "nypm/tinyexec": ["tinyexec@1.2.4", "", {}, "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg=="], "open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], From f0d2f0b1e83aca8340f9cf0c9987238c3a8218d3 Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Thu, 8 Oct 2026 12:43:39 -0700 Subject: [PATCH 11/14] build(tests): tell knip the sandbox bundle uses @vitest/expect and @vitest/spy Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EHGBgHrtePpi7KMrEfy41R --- knip.jsonc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/knip.jsonc b/knip.jsonc index e3cbb2641cd..cf7bdad401b 100644 --- a/knip.jsonc +++ b/knip.jsonc @@ -64,6 +64,8 @@ "node-gyp", // Imported inside the generated source string in sandbox/bundles/build.ts. "pptxgenjs", + "@vitest/expect", + "@vitest/spy", // Next aliases this marker to its bundled server-only module. "server-only", // Knip's email CLI plugin infers this transitive preview implementation. From b7ecf796cb00603c2f9f1a2341afc22b71efe931 Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Thu, 8 Oct 2026 15:40:45 -0700 Subject: [PATCH 12/14] feat(tests): name MCP and custom tool mocks by server and title MCP tool ids embed the server's database id, which changes when a server is re-added or a workspace is forked, so a stale mock silently stopped matching and the real server was called. Tests now name workspace tools the way the workspace does: mockTool({ mcp: 'Server', tool: 'name' }) resolved per run (failing on an unknown or ambiguous server), and mockTool({ customTool: 'Title' }) matched case- and space-insensitively. Raw mcp- and custom_ ids are rejected; built-in catalog ids are unchanged. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EHGBgHrtePpi7KMrEfy41R --- .../contracts/mothership-management-tools.ts | 2 +- apps/sim/lib/workflow-tests/protocol.ts | 4 ++ apps/sim/lib/workflow-tests/session.ts | 71 ++++++++++++++++--- apps/sim/sandbox-tasks/workflow-test.ts | 41 ++++++++--- 4 files changed, 101 insertions(+), 17 deletions(-) diff --git a/apps/sim/lib/api/contracts/mothership-management-tools.ts b/apps/sim/lib/api/contracts/mothership-management-tools.ts index 859c5830200..21adcee8ed7 100644 --- a/apps/sim/lib/api/contracts/mothership-management-tools.ts +++ b/apps/sim/lib/api/contracts/mothership-management-tools.ts @@ -24,7 +24,7 @@ export const managementToolContracts = [ route: 'sim', scope: 'all', description: - 'Create and run the selected workspace\u2019s workflow tests. create takes a name, a one-line title for the concern, and an optional description, and returns tests/.test.js; write the cases into that file with the file tools. A test file is plain vitest: import { describe, it, expect, vi } from "vitest" and { runWorkflow, mockBlock, mockTool, spyOnBlock } from "sim:test", one top-level describe, an it per case. runWorkflow(name, input) runs a workflow and returns { output } (pass { trigger: \"Trigger block name\" } as a third argument when it has several triggers); mockBlock(blockName) returns a vi.fn whose value replaces that block\u2019s output and records its inputs; mockTool(toolId) or mockTool(agentBlockName, toolId) answers an Agent\u2019s calls to that tool the same way while the model still runs; .mockSampleOutput({ ...overrides }) on either mock returns a placeholder output shaped like the real one, with your fields merged in; await expect(value).toMatchRubric(rubric) asks a model judge for pass or fail. Every write is checked and refused if the file does not load. run takes a version (draft while editing, deployed before shipping), waits, and returns each file\u2019s failures with line numbers and messages. list and get report status; update changes title or description.', + 'Create and run the selected workspace\u2019s workflow tests. create takes a name, a one-line title for the concern, and an optional description, and returns tests/.test.js; write the cases into that file with the file tools. A test file is plain vitest: import { describe, it, expect, vi } from "vitest" and { runWorkflow, mockBlock, mockTool, spyOnBlock } from "sim:test", one top-level describe, an it per case. runWorkflow(name, input) runs a workflow and returns { output } (pass { trigger: \"Trigger block name\" } as a third argument when it has several triggers); mockBlock(blockName) returns a vi.fn whose value replaces that block\u2019s output and records its inputs; mockTool(toolId) or mockTool(agentBlockName, toolId) answers an Agent\u2019s calls to that tool the same way while the model still runs, naming built-in tools by id (slack_message), MCP tools by server as mockTool({ mcp: "Server name", tool: "tool_name" }), and custom tools by title as mockTool({ customTool: "Title" }); .mockSampleOutput({ ...overrides }) on either mock returns a placeholder output shaped like the real one, with your fields merged in; await expect(value).toMatchRubric(rubric) asks a model judge for pass or fail. Every write is checked and refused if the file does not load. run takes a version (draft while editing, deployed before shipping), waits, and returns each file\u2019s failures with line numbers and messages. list and get report status; update changes title or description.', inputSchema: mothershipTestsInputSchema, }, { diff --git a/apps/sim/lib/workflow-tests/protocol.ts b/apps/sim/lib/workflow-tests/protocol.ts index aa7d0b65509..c8871149726 100644 --- a/apps/sim/lib/workflow-tests/protocol.ts +++ b/apps/sim/lib/workflow-tests/protocol.ts @@ -23,7 +23,11 @@ const testTargetSchema = z.discriminatedUnion('kind', [ workflow: nameSchema.nullable(), /** The calling Agent block; null answers the tool for every Agent. */ block: nameSchema.nullable(), + /** A built-in tool id, an MCP tool's name when `mcpServer` is set, or a custom tool's title. */ tool: nameSchema, + /** The MCP server, by name, so a test survives the server being re-added or forked. */ + mcpServer: nameSchema.nullable(), + customTool: z.boolean(), }), ]) export type TestTarget = z.infer diff --git a/apps/sim/lib/workflow-tests/session.ts b/apps/sim/lib/workflow-tests/session.ts index 0b0fe2b09c6..d3cf82f6fe9 100644 --- a/apps/sim/lib/workflow-tests/session.ts +++ b/apps/sim/lib/workflow-tests/session.ts @@ -5,6 +5,8 @@ import { generateId } from '@sim/utils/id' import { isRecordLike, toRecord } from '@sim/utils/object' import type { BlockState } from '@sim/workflow-types/workflow' import { OrchestrationError } from '@/lib/core/orchestration/types' +import { listWorkspaceMcpServers } from '@/lib/mcp/queries' +import { createMcpToolId } from '@/lib/mcp/utils' import { type JudgeVerdict, judgeRubric } from '@/lib/workflow-tests/judge' import { createMockChannel, @@ -26,7 +28,7 @@ import { import { getActiveWorkflowName, listActiveWorkflowsNamed } from '@/lib/workflows/queries' import { generateMockPayloadFromOutputsDefinition } from '@/lib/workflows/triggers/mock-payload' import { resolveTriggerRunOptions } from '@/lib/workflows/triggers/run-options' -import { normalizeName } from '@/executor/constants' +import { AGENT, normalizeName } from '@/executor/constants' import type { TestWorkflowBlock } from '@/executor/execution/types' import type { ExecutionResult } from '@/executor/types' import { hasExecutionResult, readAttemptedExecutionId } from '@/executor/utils/errors' @@ -130,10 +132,63 @@ function chooseTrigger( return undefined } -/** Tools test files may name: catalog tools, and MCP and custom tools by their prefixed ids. */ +const TOOL_OBJECT_FORMS = + 'an MCP tool as { mcp: "Server name", tool: "tool_name" } or a custom tool as { customTool: "Title" }' + +/** Tools test files may name by id: built-in catalog tools only. */ function assertKnownTool(toolId: string): void { - if (hasToolId(toolId) || toolId.startsWith('mcp-') || toolId.startsWith('custom_')) return - throw new OrchestrationError('validation', `mockTool("${toolId}"): there is no tool with that id`) + if (toolId.startsWith('mcp-') || toolId.startsWith(AGENT.CUSTOM_TOOL_PREFIX)) { + throw new OrchestrationError('validation', `mockTool("${toolId}"): name ${TOOL_OBJECT_FORMS}`) + } + if (!hasToolId(toolId)) { + throw new OrchestrationError( + 'validation', + `mockTool("${toolId}"): there is no tool with that id` + ) + } +} + +/** Rewrites each tool target to the id the executor calls, finding MCP servers by name. */ +async function resolveToolTargets( + workspaceId: string, + targets: TestTarget[] +): Promise { + const needsServers = targets.some((target) => target.kind === 'tool' && target.mcpServer !== null) + const servers = needsServers + ? (await listWorkspaceMcpServers({ workspaceId, sortBy: 'name', sortOrder: 'asc' })).data + : [] + return targets.map((target) => { + if (target.kind !== 'tool') return target + if (target.customTool) { + return { ...target, tool: `${AGENT.CUSTOM_TOOL_PREFIX}${target.tool}`, customTool: false } + } + if (target.mcpServer === null) { + assertKnownTool(target.tool) + return target + } + const wanted = normalizeName(target.mcpServer) + const matches = servers.filter((server) => normalizeName(server.name) === wanted) + if (matches.length !== 1) { + throw new OrchestrationError( + 'validation', + matches.length === 0 + ? `mockTool({ mcp: "${target.mcpServer}" }): this workspace has no MCP server with that name. Its servers: ${servers.map((server) => server.name).join(', ') || 'none'}` + : `mockTool({ mcp: "${target.mcpServer}" }): ${matches.length} MCP servers share that name; rename one` + ) + } + return { ...target, tool: createMcpToolId(matches[0].id, target.tool), mcpServer: null } + }) +} + +/** Custom tool ids carry the title, which tests may write in any case or spacing. */ +function sameTool(mockToolId: string, toolId: string): boolean { + if (mockToolId === toolId) return true + const prefix = AGENT.CUSTOM_TOOL_PREFIX + return ( + mockToolId.startsWith(prefix) && + toolId.startsWith(prefix) && + normalizeName(mockToolId.slice(prefix.length)) === normalizeName(toolId.slice(prefix.length)) + ) } /** Placeholder output shaped like a block's real one, for its configured operation. */ @@ -164,8 +219,8 @@ function childWorkflowId(id: unknown): string | undefined { /** The mock answering this Agent's call to a tool: one named for that Agent wins over a general one. */ function toolMockFor(mocks: ToolMock[], blockId: string, toolId: string): ToolMock | undefined { return ( - mocks.find((mock) => mock.toolId === toolId && mock.blockId === blockId) ?? - mocks.find((mock) => mock.toolId === toolId && mock.blockId === null) + mocks.find((mock) => sameTool(mock.toolId, toolId) && mock.blockId === blockId) ?? + mocks.find((mock) => sameTool(mock.toolId, toolId) && mock.blockId === null) ) } @@ -197,7 +252,6 @@ class RunMatcher implements MockMatcher { private readonly loadChildBlocks: (workflowId: string) => Promise>, private readonly onEnter: (workflowId: string) => void ) { - for (const target of targets) if (target.kind === 'tool') assertKnownTool(target.tool) this.targets = [...targets].sort( (a, b) => Number(a.workflow === null) - Number(b.workflow === null) ) @@ -483,8 +537,9 @@ export class WorkflowTestSession { const workflowId = await resolveWorkflowId(workspaceId, args.workflow) const blocks = await loadWorkflowBlocks(workflowId, workspaceId, version) const triggerBlockId = chooseTrigger(args.workflow, blocks, args.trigger) + const targets = await resolveToolTargets(workspaceId, args.targets) if (this.closed) throw new Error('This workflow test run has ended') - const matcher = new RunMatcher(args.targets, loadChildBlockNames, (id) => + const matcher = new RunMatcher(targets, loadChildBlockNames, (id) => this.enteredWorkflowIds.add(id) ) const channel = createMockChannel(matcher) diff --git a/apps/sim/sandbox-tasks/workflow-test.ts b/apps/sim/sandbox-tasks/workflow-test.ts index 9cad58837a3..908a98a165e 100644 --- a/apps/sim/sandbox-tasks/workflow-test.ts +++ b/apps/sim/sandbox-tasks/workflow-test.ts @@ -202,12 +202,15 @@ const HARNESS = ` const vi = { fn: vx.fn, spyOn: vx.spyOn, isMockFunction: vx.isMockFunction }; const registrations = new Map(); - const registrationKey = (kind, workflow, block, tool) => kind + '\\u0000' + (workflow ?? '') + '\\u0000' + (block ?? '') + '\\u0000' + (tool ?? ''); + const registrationKey = (kind, workflow, block, tool, mcpServer, customTool) => kind + '\\u0000' + (workflow ?? '') + '\\u0000' + (block ?? '') + '\\u0000' + (mcpServer ?? '') + '\\u0000' + (customTool ? 'custom' : '') + '\\u0000' + (tool ?? ''); + const describeTool = (r) => r.mcpServer !== null + ? '{ mcp: "' + r.mcpServer + '", tool: "' + r.tool + '" }' + : r.customTool ? '{ customTool: "' + r.tool + '" }' : '"' + r.tool + '"'; const describeRegistration = (r) => r.kind === 'tool' - ? 'mockTool("' + (r.block ? r.block + '", "' : '') + r.tool + '")' + ? 'mockTool(' + (r.block ? '"' + r.block + '", ' : '') + describeTool(r) + ')' : (r.kind === 'mock' ? 'mockBlock' : 'spyOnBlock') + '("' + (r.workflow ? r.workflow + '", "' : '') + r.block + '")'; function addRegistration(entry) { - const key = registrationKey(entry.kind, entry.workflow, entry.block, entry.tool); + const key = registrationKey(entry.kind, entry.workflow, entry.block, entry.tool, entry.mcpServer, entry.customTool); const existing = registrations.get(key); if (existing) return existing.fn; const fn = vx.fn().mockName(entry.label); @@ -230,16 +233,38 @@ const HARNESS = ` const what = kind === 'mock' ? 'mockBlock()' : 'spyOnBlock()'; requireName(what, block); if (workflow !== null) requireName(what + ' workflow', workflow); - return addRegistration({ kind, workflow, block, tool: null, label: workflow ? workflow + ' / ' + block : block }); + return addRegistration({ kind, workflow, block, tool: null, mcpServer: null, customTool: false, label: workflow ? workflow + ' / ' + block : block }); } const mockBlock = (first, second) => register('mock', first, second); const spyOnBlock = (first, second) => register('spy', first, second); const mockTool = (first, second) => { const block = second === undefined ? null : first; - const tool = second === undefined ? first : second; - requireName('mockTool()', tool); + const target = second === undefined ? first : second; if (block !== null) requireName('mockTool() Agent block', block); - return addRegistration({ kind: 'tool', workflow: null, block, tool, label: block ? block + ' / ' + tool : tool }); + const objectForms = 'an MCP tool as { mcp: "Server name", tool: "tool_name" } or a custom tool as { customTool: "Title" }'; + let tool = target; + let mcpServer = null; + let customTool = false; + if (target !== null && typeof target === 'object') { + const keys = Object.keys(target).sort().join(','); + if (keys === 'mcp,tool') { + requireName('mockTool() MCP server', target.mcp); + requireName('mockTool() MCP tool', target.tool); + mcpServer = target.mcp; + tool = target.tool; + } else if (keys === 'customTool') { + requireName('mockTool() custom tool', target.customTool); + customTool = true; + tool = target.customTool; + } else { + throw new Error('mockTool() takes a built-in tool id, ' + objectForms); + } + } else { + requireName('mockTool()', tool); + if (tool.startsWith('mcp-') || tool.startsWith('custom_')) throw new Error('mockTool("' + tool + '"): name ' + objectForms); + } + const name = mcpServer !== null ? mcpServer + ' / ' + tool : tool; + return addRegistration({ kind: 'tool', workflow: null, block, tool, mcpServer, customTool, label: block ? block + ' / ' + name : name }); }; /** Overrides on a generated sample: objects merge, anything else replaces, and an unknown field is a mistake. */ @@ -302,7 +327,7 @@ const HARNESS = ` } const trigger = options ? options.trigger : null; const targets = [...registrations.values()].map((r) => r.kind === 'tool' - ? { key: r.key, kind: 'tool', workflow: null, block: r.block, tool: r.tool } + ? { key: r.key, kind: 'tool', workflow: null, block: r.block, tool: r.tool, mcpServer: r.mcpServer, customTool: r.customTool } : { key: r.key, kind: r.kind, workflow: r.workflow, block: r.block }); const test = currentTest; const mistakes = []; From 15dd8112a2c1b461b430af09cbffd88ee51b02f0 Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Thu, 8 Oct 2026 15:58:01 -0700 Subject: [PATCH 13/14] fix(tests): reserve the run name, hold Run for unsaved edits, cancel judges on close - A test named "run" collided with the static run endpoint, so its detail page got a 405; the name is now reserved. - Run is disabled while the open editor holds edits the server has not saved (including a refused save), so a run never uses the previous source. - toMatchRubric model calls are aborted when the sandbox run ends, so a stopped test no longer keeps calling or billing the judge. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EHGBgHrtePpi7KMrEfy41R --- .../[workspaceId]/tests/components/test-detail.tsx | 14 +++++++++++--- apps/sim/lib/api/contracts/mothership-tests.ts | 6 +++--- apps/sim/lib/workflow-tests/judge.ts | 2 ++ apps/sim/lib/workflow-tests/paths.ts | 2 +- apps/sim/lib/workflow-tests/session.ts | 4 ++++ apps/sim/stores/workflow-tests/store.ts | 11 ++++++++++- 6 files changed, 31 insertions(+), 8 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/test-detail.tsx b/apps/sim/app/workspace/[workspaceId]/tests/components/test-detail.tsx index c7f0ab6edad..3ea390f13c5 100644 --- a/apps/sim/app/workspace/[workspaceId]/tests/components/test-detail.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/test-detail.tsx @@ -1,6 +1,6 @@ 'use client' -import { useState } from 'react' +import { useEffect, useState } from 'react' import { toast } from '@sim/emcn' import { Columns2, Eye, Pencil, PlayOutline, ShieldCheck } from '@sim/emcn/icons' import { getErrorMessage } from '@sim/utils/errors' @@ -19,6 +19,7 @@ import { useWorkflowTest, } from '@/hooks/queries/workflow-tests' import { useAddressedWorkspaceFileRecord } from '@/hooks/queries/workspace-files' +import { testRunSelectionKey, useTestRunSelectionStore } from '@/stores/workflow-tests/store' interface TestTarget { workspaceId: string @@ -74,12 +75,15 @@ interface TestRunActionInput extends TestTarget { export function useTestRunAction({ workspaceId, name, running }: TestRunActionInput) { const canEdit = useUserPermissionsContext().canEdit === true const runTests = useRunWorkflowTests(workspaceId) + const unsaved = useTestRunSelectionStore( + (state) => state.unsavedTests[testRunSelectionKey(workspaceId, name)] === true + ) const busy = running || runTests.isPending return { id: 'run', icon: PlayOutline, - text: busy ? 'Running…' : 'Run', - disabled: !canEdit || busy, + text: busy ? 'Running…' : unsaved ? 'Unsaved' : 'Run', + disabled: !canEdit || busy || unsaved, onSelect: () => runTests.mutate( { version: VERSION, names: [name] }, @@ -107,6 +111,9 @@ export function TestView({ workspaceId, name, previewMode }: TestViewProps) { const query = useWorkflowTest(workspaceId, name) const refreshTest = useRefreshWorkflowTest(workspaceId, name) const [saveError, setSaveError] = useState(null) + const setUnsaved = useTestRunSelectionStore((state) => state.setUnsaved) + const testKey = testRunSelectionKey(workspaceId, name) + useEffect(() => () => setUnsaved(testKey, false), [setUnsaved, testKey]) const detail = query.data const file = useAddressedWorkspaceFileRecord(workspaceId, detail?.test.fileId ?? '', { enabled: Boolean(detail), @@ -142,6 +149,7 @@ export function TestView({ workspaceId, name, previewMode }: TestViewProps) { preview={} sourceSide='end' enableFind + onDirtyChange={(dirty) => setUnsaved(testKey, dirty)} onSaveError={(saveFailure) => setSaveError(getErrorMessage(saveFailure, 'The test file was not saved')) } diff --git a/apps/sim/lib/api/contracts/mothership-tests.ts b/apps/sim/lib/api/contracts/mothership-tests.ts index 06eff8f89f6..074643057c9 100644 --- a/apps/sim/lib/api/contracts/mothership-tests.ts +++ b/apps/sim/lib/api/contracts/mothership-tests.ts @@ -1,15 +1,15 @@ import { z } from 'zod' const scope = z.object({ workspaceId: z.string().min(1).max(100).optional() }) -/** A test file name: `tests/.test.js`. */ -export const TEST_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,79}$/ +/** A test file name: `tests/.test.js`. `run` is taken by the run endpoint beside the names. */ +export const TEST_NAME_PATTERN = /^(?!run$)[a-z0-9][a-z0-9-]{0,79}$/ const nameSchema = z .string() .trim() .regex( TEST_NAME_PATTERN, - 'Use a lowercase name of letters, numbers, and dashes, like billing-routing' + 'Use a lowercase name of letters, numbers, and dashes, like billing-routing (not "run")' ) .describe('Test file name: tests/.test.js, lowercase letters, numbers, and dashes.') const versionSchema = z diff --git a/apps/sim/lib/workflow-tests/judge.ts b/apps/sim/lib/workflow-tests/judge.ts index 81306c576a7..a36426760f9 100644 --- a/apps/sim/lib/workflow-tests/judge.ts +++ b/apps/sim/lib/workflow-tests/judge.ts @@ -32,6 +32,7 @@ export interface JudgeRubricInput { actorUserId: string value: string rubric: string + abortSignal: AbortSignal } /** One pass/fail model call for `toMatchRubric`, billed to the workspace like a wand call. */ @@ -65,6 +66,7 @@ export async function judgeRubric(input: JudgeRubricInput): Promise.test.js with a lowercase name of letters, numbers, and dashes, like tests/billing-routing.test.js` + `"${testFilePath(name)}" is not a valid test file. Use tests/.test.js with a lowercase name of letters, numbers, and dashes other than "run", like tests/billing-routing.test.js` ) } } diff --git a/apps/sim/lib/workflow-tests/session.ts b/apps/sim/lib/workflow-tests/session.ts index d3cf82f6fe9..762e180a677 100644 --- a/apps/sim/lib/workflow-tests/session.ts +++ b/apps/sim/lib/workflow-tests/session.ts @@ -524,6 +524,8 @@ export class WorkflowTestSession { readonly enteredWorkflowIds = new Set() /** Set when the sandbox run ends; a start still awaiting its lookups then starts nothing. */ private closed = false + /** Stops `toMatchRubric` model calls still in flight when the sandbox run ends. */ + private readonly judgeAbort = new AbortController() constructor(readonly config: WorkflowTestSessionConfig) {} @@ -586,6 +588,7 @@ export class WorkflowTestSession { close(): void { this.closed = true + this.judgeAbort.abort(new Error('The test run ended')) this.cancel([...this.runs.keys()]) } @@ -595,6 +598,7 @@ export class WorkflowTestSession { actorUserId: requirePrincipalSubjectUserId(this.config.principal), value: args.value, rubric: args.rubric, + abortSignal: this.judgeAbort.signal, }) } diff --git a/apps/sim/stores/workflow-tests/store.ts b/apps/sim/stores/workflow-tests/store.ts index 4e5837f0e3e..81cd81d5235 100644 --- a/apps/sim/stores/workflow-tests/store.ts +++ b/apps/sim/stores/workflow-tests/store.ts @@ -8,7 +8,10 @@ import { devtools } from 'zustand/middleware' */ interface TestRunSelectionState { selectedRunIds: Record + /** Tests whose open editor holds edits the server has not saved; Run waits for them. */ + unsavedTests: Record selectRun: (testKey: string, runId: string | null) => void + setUnsaved: (testKey: string, unsaved: boolean) => void reset: () => void } @@ -16,12 +19,18 @@ export const useTestRunSelectionStore = create()( devtools( (set) => ({ selectedRunIds: {}, + unsavedTests: {}, selectRun: (testKey, runId) => set((state) => { const { [testKey]: _previous, ...rest } = state.selectedRunIds return { selectedRunIds: runId === null ? rest : { ...rest, [testKey]: runId } } }), - reset: () => set({ selectedRunIds: {} }), + setUnsaved: (testKey, unsaved) => + set((state) => { + const { [testKey]: _previous, ...rest } = state.unsavedTests + return { unsavedTests: unsaved ? { ...rest, [testKey]: true } : rest } + }), + reset: () => set({ selectedRunIds: {}, unsavedTests: {} }), }), { name: 'test-run-selection' } ) From 21cc7452320f8f0169358bb51da7bb8e7a5234c7 Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Thu, 8 Oct 2026 16:12:02 -0700 Subject: [PATCH 14/14] fix(tests): fail a run whose selected case names no test in the file A renamed or misspelled `only` path skipped every case, and the run was then saved as passing. The harness now rejects unknown names, so the run is recorded as an error with the names it could not find. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EHGBgHrtePpi7KMrEfy41R --- apps/sim/sandbox-tasks/workflow-test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/sim/sandbox-tasks/workflow-test.ts b/apps/sim/sandbox-tasks/workflow-test.ts index 908a98a165e..24f9915275d 100644 --- a/apps/sim/sandbox-tasks/workflow-test.ts +++ b/apps/sim/sandbox-tasks/workflow-test.ts @@ -488,6 +488,11 @@ const HARNESS = ` async run() { const plan = this.collect(); const { only } = await call('testPlan', {}); + if (only) { + const known = new Set(plan.tests.map((t) => t.path.join(' > '))); + const unknown = only.filter((name) => !known.has(name)); + if (unknown.length > 0) throw new Error('No test is named ' + unknown.map((name) => '"' + name + '"').join(', ') + ' in this file'); + } const results = []; await runSuiteTree(root, only, results); return {