diff --git a/.claude/rules/global.md b/.claude/rules/global.md index 4c98edc16cb..cfcb4c7cc29 100644 --- a/.claude/rules/global.md +++ b/.claude/rules/global.md @@ -10,7 +10,7 @@ Use TSDoc for documentation. No `====` separators. No non-TSDoc comments. Never update global styles. Keep all styling local to components. ## ID Generation -Never use `crypto.randomUUID()`, `nanoid`, or the `uuid` package directly. Use the utilities from `@/lib/core/utils/uuid`: +Never use `crypto.randomUUID()`, `nanoid`, or the `uuid` package directly. Use the utilities from `@sim/utils/id`: - `generateId()` — UUID v4, use by default - `generateShortId(size?)` — short URL-safe ID (default 21 chars), for compact identifiers @@ -24,14 +24,14 @@ import { v4 as uuidv4 } from 'uuid' const id = crypto.randomUUID() // ✓ Good -import { generateId, generateShortId } from '@/lib/core/utils/uuid' +import { generateId, generateShortId } from '@sim/utils/id' const uuid = generateId() const shortId = generateShortId() const tiny = generateShortId(8) ``` ## Common Utilities -Use shared helpers from `@/lib/core/utils/helpers` instead of writing inline implementations: +Use shared helpers from `@sim/utils` instead of writing inline implementations: - `sleep(ms)` — async delay. Never write `new Promise(resolve => setTimeout(resolve, ms))` - `toError(value)` — normalize unknown caught values to `Error`. Never write `e instanceof Error ? e : new Error(String(e))` @@ -44,7 +44,8 @@ const msg = error instanceof Error ? error.message : String(error) const err = error instanceof Error ? error : new Error(String(error)) // ✓ Good -import { sleep, toError } from '@/lib/core/utils/helpers' +import { sleep } from '@sim/utils/helpers' +import { toError } from '@sim/utils/errors' await sleep(1000) const msg = toError(error).message const err = toError(error) diff --git a/.claude/rules/sim-testing.md b/.claude/rules/sim-testing.md index 85a7554637b..68a7b8203aa 100644 --- a/.claude/rules/sim-testing.md +++ b/.claude/rules/sim-testing.md @@ -102,10 +102,6 @@ vi.mock('@/lib/workspaces/utils', () => ({ })) ``` -### NEVER use `mockAuth()`, `mockConsoleLogger()`, or `setupCommonApiMocks()` from `@sim/testing` - -These helpers internally use `vi.doMock()` which is slow. Use direct `vi.hoisted()` + `vi.mock()` instead. - ### Mock heavy transitive dependencies If a module under test imports `@/blocks` (200+ files), `@/tools/registry`, or other heavy modules, mock them: @@ -135,38 +131,61 @@ await new Promise(r => setTimeout(r, 1)) vi.useFakeTimers() ``` -## Mock Pattern Reference +## Centralized Mocks (prefer over local declarations) + +`@sim/testing` exports ready-to-use mock modules for common dependencies. Import and pass directly to `vi.mock()` — no `vi.hoisted()` boilerplate needed. Each paired `*MockFns` object exposes the underlying `vi.fn()`s for per-test overrides. + +| Module mocked | Import | Factory form | +|---|---|---| +| `@/app/api/auth/oauth/utils` | `authOAuthUtilsMock`, `authOAuthUtilsMockFns` | `vi.mock('@/app/api/auth/oauth/utils', () => authOAuthUtilsMock)` | +| `@/app/api/knowledge/utils` | `knowledgeApiUtilsMock`, `knowledgeApiUtilsMockFns` | `vi.mock('@/app/api/knowledge/utils', () => knowledgeApiUtilsMock)` | +| `@/app/api/workflows/utils` | `workflowsApiUtilsMock`, `workflowsApiUtilsMockFns` | `vi.mock('@/app/api/workflows/utils', () => workflowsApiUtilsMock)` | +| `@/lib/audit/log` | `auditMock`, `auditMockFns` | `vi.mock('@/lib/audit/log', () => auditMock)` | +| `@/lib/auth` | `authMock`, `authMockFns` | `vi.mock('@/lib/auth', () => authMock)` | +| `@/lib/auth/hybrid` | `hybridAuthMock`, `hybridAuthMockFns` | `vi.mock('@/lib/auth/hybrid', () => hybridAuthMock)` | +| `@/lib/copilot/request/http` | `copilotHttpMock`, `copilotHttpMockFns` | `vi.mock('@/lib/copilot/request/http', () => copilotHttpMock)` | +| `@/lib/core/config/env` | `envMock`, `createEnvMock(overrides)` | `vi.mock('@/lib/core/config/env', () => envMock)` | +| `@/lib/core/config/feature-flags` | `featureFlagsMock` | `vi.mock('@/lib/core/config/feature-flags', () => featureFlagsMock)` | +| `@/lib/core/config/redis` | `redisConfigMock`, `redisConfigMockFns` | `vi.mock('@/lib/core/config/redis', () => redisConfigMock)` | +| `@/lib/core/security/encryption` | `encryptionMock`, `encryptionMockFns` | `vi.mock('@/lib/core/security/encryption', () => encryptionMock)` | +| `@/lib/core/security/input-validation.server` | `inputValidationMock`, `inputValidationMockFns` | `vi.mock('@/lib/core/security/input-validation.server', () => inputValidationMock)` | +| `@/lib/core/utils/request` | `requestUtilsMock`, `requestUtilsMockFns` | `vi.mock('@/lib/core/utils/request', () => requestUtilsMock)` | +| `@/lib/core/utils/urls` | `urlsMock`, `urlsMockFns` | `vi.mock('@/lib/core/utils/urls', () => urlsMock)` | +| `@/lib/execution/preprocessing` | `executionPreprocessingMock`, `executionPreprocessingMockFns` | `vi.mock('@/lib/execution/preprocessing', () => executionPreprocessingMock)` | +| `@/lib/logs/execution/logging-session` | `loggingSessionMock`, `loggingSessionMockFns`, `LoggingSessionMock` | `vi.mock('@/lib/logs/execution/logging-session', () => loggingSessionMock)` | +| `@/lib/workflows/orchestration` | `workflowsOrchestrationMock`, `workflowsOrchestrationMockFns` | `vi.mock('@/lib/workflows/orchestration', () => workflowsOrchestrationMock)` | +| `@/lib/workflows/persistence/utils` | `workflowsPersistenceUtilsMock`, `workflowsPersistenceUtilsMockFns` | `vi.mock('@/lib/workflows/persistence/utils', () => workflowsPersistenceUtilsMock)` | +| `@/lib/workflows/utils` | `workflowsUtilsMock`, `workflowsUtilsMockFns` | `vi.mock('@/lib/workflows/utils', () => workflowsUtilsMock)` | +| `@/lib/workspaces/permissions/utils` | `permissionsMock`, `permissionsMockFns` | `vi.mock('@/lib/workspaces/permissions/utils', () => permissionsMock)` | +| `@sim/db/schema` | `schemaMock` | `vi.mock('@sim/db/schema', () => schemaMock)` | ### Auth mocking (API routes) ```typescript -const { mockGetSession } = vi.hoisted(() => ({ - mockGetSession: vi.fn(), -})) +import { authMock, authMockFns } from '@sim/testing' +import { beforeEach, describe, expect, it, vi } from 'vitest' -vi.mock('@/lib/auth', () => ({ - auth: { api: { getSession: vi.fn() } }, - getSession: mockGetSession, -})) +vi.mock('@/lib/auth', () => authMock) -// In tests: -mockGetSession.mockResolvedValue({ user: { id: 'user-1', email: 'test@example.com' } }) -mockGetSession.mockResolvedValue(null) // unauthenticated +import { GET } from '@/app/api/my-route/route' + +beforeEach(() => { + vi.clearAllMocks() + authMockFns.mockGetSession.mockResolvedValue({ user: { id: 'user-1' } }) +}) ``` +Only define a local `vi.mock('@/lib/auth', ...)` if the module under test consumes exports outside the centralized shape (e.g., `auth.api.verifyOneTimeToken`, `auth.api.resetPassword`). + ### Hybrid auth mocking ```typescript -const { mockCheckSessionOrInternalAuth } = vi.hoisted(() => ({ - mockCheckSessionOrInternalAuth: vi.fn(), -})) +import { hybridAuthMock, hybridAuthMockFns } from '@sim/testing' -vi.mock('@/lib/auth/hybrid', () => ({ - checkSessionOrInternalAuth: mockCheckSessionOrInternalAuth, -})) +vi.mock('@/lib/auth/hybrid', () => hybridAuthMock) // In tests: -mockCheckSessionOrInternalAuth.mockResolvedValue({ +hybridAuthMockFns.mockCheckSessionOrInternalAuth.mockResolvedValue({ success: true, userId: 'user-1', authType: 'session', }) ``` @@ -197,21 +216,23 @@ Always prefer over local test data. | Category | Utilities | |----------|-----------| -| **Mocks** | `loggerMock`, `databaseMock`, `drizzleOrmMock`, `setupGlobalFetchMock()` | +| **Module mocks** | See "Centralized Mocks" table above | +| **Logger helpers** | `loggerMock`, `createMockLogger()`, `getLoggerCalls()`, `clearLoggerMocks()` | +| **Database helpers** | `databaseMock`, `drizzleOrmMock`, `createMockDb()`, `createMockSql()`, `createMockSqlOperators()` | +| **Fetch helpers** | `setupGlobalFetchMock()`, `createMockFetch()`, `createMockResponse()`, `mockFetchError()` | | **Factories** | `createSession()`, `createWorkflowRecord()`, `createBlock()`, `createExecutionContext()` | | **Builders** | `WorkflowBuilder`, `ExecutionContextBuilder` | | **Assertions** | `expectWorkflowAccessGranted()`, `expectBlockExecuted()` | -| **Requests** | `createMockRequest()`, `createEnvMock()` | +| **Requests** | `createMockRequest()`, `createMockFormDataRequest()` | ## Rules Summary 1. `@vitest-environment node` unless DOM is required -2. `vi.hoisted()` + `vi.mock()` + static imports — never `vi.resetModules()` + `vi.doMock()` + dynamic imports -3. `vi.mock()` calls before importing mocked modules -4. `@sim/testing` utilities over local mocks +2. Prefer centralized mocks from `@sim/testing` (see table above) over local `vi.hoisted()` + `vi.mock()` boilerplate +3. `vi.hoisted()` + `vi.mock()` + static imports — never `vi.resetModules()` + `vi.doMock()` + dynamic imports +4. `vi.mock()` calls before importing mocked modules 5. `beforeEach(() => vi.clearAllMocks())` to reset state — no redundant `afterEach` 6. No `vi.importActual()` — mock everything explicitly -7. No `mockAuth()`, `mockConsoleLogger()`, `setupCommonApiMocks()` — use direct mocks -8. Mock heavy deps (`@/blocks`, `@/tools/registry`, `@/triggers`) in tests that don't need them -9. Use absolute imports in test files -10. Avoid real timers — use 1ms delays or `vi.useFakeTimers()` +7. Mock heavy deps (`@/blocks`, `@/tools/registry`, `@/triggers`) in tests that don't need them +8. Use absolute imports in test files +9. Avoid real timers — use 1ms delays or `vi.useFakeTimers()` diff --git a/.cursor/rules/global.mdc b/.cursor/rules/global.mdc index 991244503f3..78f0cb106b3 100644 --- a/.cursor/rules/global.mdc +++ b/.cursor/rules/global.mdc @@ -17,7 +17,7 @@ Use TSDoc for documentation. No `====` separators. No non-TSDoc comments. Never update global styles. Keep all styling local to components. ## ID Generation -Never use `crypto.randomUUID()`, `nanoid`, or the `uuid` package directly. Use the utilities from `@/lib/core/utils/uuid`: +Never use `crypto.randomUUID()`, `nanoid`, or the `uuid` package directly. Use the utilities from `@sim/utils/id`: - `generateId()` — UUID v4, use by default - `generateShortId(size?)` — short URL-safe ID (default 21 chars), for compact identifiers @@ -31,14 +31,14 @@ import { v4 as uuidv4 } from 'uuid' const id = crypto.randomUUID() // ✓ Good -import { generateId, generateShortId } from '@/lib/core/utils/uuid' +import { generateId, generateShortId } from '@sim/utils/id' const uuid = generateId() const shortId = generateShortId() const tiny = generateShortId(8) ``` ## Common Utilities -Use shared helpers from `@/lib/core/utils/helpers` instead of writing inline implementations: +Use shared helpers from `@sim/utils` instead of writing inline implementations: - `sleep(ms)` — async delay. Never write `new Promise(resolve => setTimeout(resolve, ms))` - `toError(value)` — normalize unknown caught values to `Error`. Never write `e instanceof Error ? e : new Error(String(e))` @@ -51,7 +51,8 @@ const msg = error instanceof Error ? error.message : String(error) const err = error instanceof Error ? error : new Error(String(error)) // ✓ Good -import { sleep, toError } from '@/lib/core/utils/helpers' +import { sleep } from '@sim/utils/helpers' +import { toError } from '@sim/utils/errors' await sleep(1000) const msg = toError(error).message const err = toError(error) diff --git a/.cursor/rules/sim-testing.mdc b/.cursor/rules/sim-testing.mdc index ec140388e84..41b66b3693c 100644 --- a/.cursor/rules/sim-testing.mdc +++ b/.cursor/rules/sim-testing.mdc @@ -3,6 +3,7 @@ description: Testing patterns with Vitest and @sim/testing globs: ["apps/sim/**/*.test.ts", "apps/sim/**/*.test.tsx"] --- + # Testing Patterns Use Vitest. Test files: `feature.ts` → `feature.test.ts` @@ -101,10 +102,6 @@ vi.mock('@/lib/workspaces/utils', () => ({ })) ``` -### NEVER use `mockAuth()`, `mockConsoleLogger()`, or `setupCommonApiMocks()` from `@sim/testing` - -These helpers internally use `vi.doMock()` which is slow. Use direct `vi.hoisted()` + `vi.mock()` instead. - ### Mock heavy transitive dependencies If a module under test imports `@/blocks` (200+ files), `@/tools/registry`, or other heavy modules, mock them: @@ -134,38 +131,61 @@ await new Promise(r => setTimeout(r, 1)) vi.useFakeTimers() ``` -## Mock Pattern Reference +## Centralized Mocks (prefer over local declarations) + +`@sim/testing` exports ready-to-use mock modules for common dependencies. Import and pass directly to `vi.mock()` — no `vi.hoisted()` boilerplate needed. Each paired `*MockFns` object exposes the underlying `vi.fn()`s for per-test overrides. + +| Module mocked | Import | Factory form | +|---|---|---| +| `@/app/api/auth/oauth/utils` | `authOAuthUtilsMock`, `authOAuthUtilsMockFns` | `vi.mock('@/app/api/auth/oauth/utils', () => authOAuthUtilsMock)` | +| `@/app/api/knowledge/utils` | `knowledgeApiUtilsMock`, `knowledgeApiUtilsMockFns` | `vi.mock('@/app/api/knowledge/utils', () => knowledgeApiUtilsMock)` | +| `@/app/api/workflows/utils` | `workflowsApiUtilsMock`, `workflowsApiUtilsMockFns` | `vi.mock('@/app/api/workflows/utils', () => workflowsApiUtilsMock)` | +| `@/lib/audit/log` | `auditMock`, `auditMockFns` | `vi.mock('@/lib/audit/log', () => auditMock)` | +| `@/lib/auth` | `authMock`, `authMockFns` | `vi.mock('@/lib/auth', () => authMock)` | +| `@/lib/auth/hybrid` | `hybridAuthMock`, `hybridAuthMockFns` | `vi.mock('@/lib/auth/hybrid', () => hybridAuthMock)` | +| `@/lib/copilot/request/http` | `copilotHttpMock`, `copilotHttpMockFns` | `vi.mock('@/lib/copilot/request/http', () => copilotHttpMock)` | +| `@/lib/core/config/env` | `envMock`, `createEnvMock(overrides)` | `vi.mock('@/lib/core/config/env', () => envMock)` | +| `@/lib/core/config/feature-flags` | `featureFlagsMock` | `vi.mock('@/lib/core/config/feature-flags', () => featureFlagsMock)` | +| `@/lib/core/config/redis` | `redisConfigMock`, `redisConfigMockFns` | `vi.mock('@/lib/core/config/redis', () => redisConfigMock)` | +| `@/lib/core/security/encryption` | `encryptionMock`, `encryptionMockFns` | `vi.mock('@/lib/core/security/encryption', () => encryptionMock)` | +| `@/lib/core/security/input-validation.server` | `inputValidationMock`, `inputValidationMockFns` | `vi.mock('@/lib/core/security/input-validation.server', () => inputValidationMock)` | +| `@/lib/core/utils/request` | `requestUtilsMock`, `requestUtilsMockFns` | `vi.mock('@/lib/core/utils/request', () => requestUtilsMock)` | +| `@/lib/core/utils/urls` | `urlsMock`, `urlsMockFns` | `vi.mock('@/lib/core/utils/urls', () => urlsMock)` | +| `@/lib/execution/preprocessing` | `executionPreprocessingMock`, `executionPreprocessingMockFns` | `vi.mock('@/lib/execution/preprocessing', () => executionPreprocessingMock)` | +| `@/lib/logs/execution/logging-session` | `loggingSessionMock`, `loggingSessionMockFns`, `LoggingSessionMock` | `vi.mock('@/lib/logs/execution/logging-session', () => loggingSessionMock)` | +| `@/lib/workflows/orchestration` | `workflowsOrchestrationMock`, `workflowsOrchestrationMockFns` | `vi.mock('@/lib/workflows/orchestration', () => workflowsOrchestrationMock)` | +| `@/lib/workflows/persistence/utils` | `workflowsPersistenceUtilsMock`, `workflowsPersistenceUtilsMockFns` | `vi.mock('@/lib/workflows/persistence/utils', () => workflowsPersistenceUtilsMock)` | +| `@/lib/workflows/utils` | `workflowsUtilsMock`, `workflowsUtilsMockFns` | `vi.mock('@/lib/workflows/utils', () => workflowsUtilsMock)` | +| `@/lib/workspaces/permissions/utils` | `permissionsMock`, `permissionsMockFns` | `vi.mock('@/lib/workspaces/permissions/utils', () => permissionsMock)` | +| `@sim/db/schema` | `schemaMock` | `vi.mock('@sim/db/schema', () => schemaMock)` | ### Auth mocking (API routes) ```typescript -const { mockGetSession } = vi.hoisted(() => ({ - mockGetSession: vi.fn(), -})) +import { authMock, authMockFns } from '@sim/testing' +import { beforeEach, describe, expect, it, vi } from 'vitest' -vi.mock('@/lib/auth', () => ({ - auth: { api: { getSession: vi.fn() } }, - getSession: mockGetSession, -})) +vi.mock('@/lib/auth', () => authMock) -// In tests: -mockGetSession.mockResolvedValue({ user: { id: 'user-1', email: 'test@example.com' } }) -mockGetSession.mockResolvedValue(null) // unauthenticated +import { GET } from '@/app/api/my-route/route' + +beforeEach(() => { + vi.clearAllMocks() + authMockFns.mockGetSession.mockResolvedValue({ user: { id: 'user-1' } }) +}) ``` +Only define a local `vi.mock('@/lib/auth', ...)` if the module under test consumes exports outside the centralized shape (e.g., `auth.api.verifyOneTimeToken`, `auth.api.resetPassword`). + ### Hybrid auth mocking ```typescript -const { mockCheckSessionOrInternalAuth } = vi.hoisted(() => ({ - mockCheckSessionOrInternalAuth: vi.fn(), -})) +import { hybridAuthMock, hybridAuthMockFns } from '@sim/testing' -vi.mock('@/lib/auth/hybrid', () => ({ - checkSessionOrInternalAuth: mockCheckSessionOrInternalAuth, -})) +vi.mock('@/lib/auth/hybrid', () => hybridAuthMock) // In tests: -mockCheckSessionOrInternalAuth.mockResolvedValue({ +hybridAuthMockFns.mockCheckSessionOrInternalAuth.mockResolvedValue({ success: true, userId: 'user-1', authType: 'session', }) ``` @@ -196,21 +216,23 @@ Always prefer over local test data. | Category | Utilities | |----------|-----------| -| **Mocks** | `loggerMock`, `databaseMock`, `drizzleOrmMock`, `setupGlobalFetchMock()` | +| **Module mocks** | See "Centralized Mocks" table above | +| **Logger helpers** | `loggerMock`, `createMockLogger()`, `getLoggerCalls()`, `clearLoggerMocks()` | +| **Database helpers** | `databaseMock`, `drizzleOrmMock`, `createMockDb()`, `createMockSql()`, `createMockSqlOperators()` | +| **Fetch helpers** | `setupGlobalFetchMock()`, `createMockFetch()`, `createMockResponse()`, `mockFetchError()` | | **Factories** | `createSession()`, `createWorkflowRecord()`, `createBlock()`, `createExecutionContext()` | | **Builders** | `WorkflowBuilder`, `ExecutionContextBuilder` | | **Assertions** | `expectWorkflowAccessGranted()`, `expectBlockExecuted()` | -| **Requests** | `createMockRequest()`, `createEnvMock()` | +| **Requests** | `createMockRequest()`, `createMockFormDataRequest()` | ## Rules Summary 1. `@vitest-environment node` unless DOM is required -2. `vi.hoisted()` + `vi.mock()` + static imports — never `vi.resetModules()` + `vi.doMock()` + dynamic imports -3. `vi.mock()` calls before importing mocked modules -4. `@sim/testing` utilities over local mocks +2. Prefer centralized mocks from `@sim/testing` (see table above) over local `vi.hoisted()` + `vi.mock()` boilerplate +3. `vi.hoisted()` + `vi.mock()` + static imports — never `vi.resetModules()` + `vi.doMock()` + dynamic imports +4. `vi.mock()` calls before importing mocked modules 5. `beforeEach(() => vi.clearAllMocks())` to reset state — no redundant `afterEach` 6. No `vi.importActual()` — mock everything explicitly -7. No `mockAuth()`, `mockConsoleLogger()`, `setupCommonApiMocks()` — use direct mocks -8. Mock heavy deps (`@/blocks`, `@/tools/registry`, `@/triggers`) in tests that don't need them -9. Use absolute imports in test files -10. Avoid real timers — use 1ms delays or `vi.useFakeTimers()` +7. Mock heavy deps (`@/blocks`, `@/tools/registry`, `@/triggers`) in tests that don't need them +8. Use absolute imports in test files +9. Avoid real timers — use 1ms delays or `vi.useFakeTimers()` diff --git a/AGENTS.md b/AGENTS.md index bc54c6f912c..5e21c7e009c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,7 +7,7 @@ You are a professional software engineer. All code must follow best practices: a - **Logging**: Import `createLogger` from `@sim/logger`. Use `logger.info`, `logger.warn`, `logger.error` instead of `console.log` - **Comments**: Use TSDoc for documentation. No `====` separators. No non-TSDoc comments - **Styling**: Never update global styles. Keep all styling local to components -- **ID Generation**: Never use `crypto.randomUUID()`, `nanoid`, or `uuid` package. Use `generateId()` (UUID v4) or `generateShortId()` (compact) from `@/lib/core/utils/uuid` +- **ID Generation**: Never use `crypto.randomUUID()`, `nanoid`, or `uuid` package. Use `generateId()` (UUID v4) or `generateShortId()` (compact) from `@sim/utils/id` - **Package Manager**: Use `bun` and `bunx`, not `npm` and `npx` ## Architecture diff --git a/CLAUDE.md b/CLAUDE.md index 965ae7fd7b3..1ca9bf41a25 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,8 +7,8 @@ You are a professional software engineer. All code must follow best practices: a - **Logging**: Import `createLogger` from `@sim/logger`. Use `logger.info`, `logger.warn`, `logger.error` instead of `console.log` - **Comments**: Use TSDoc for documentation. No `====` separators. No non-TSDoc comments - **Styling**: Never update global styles. Keep all styling local to components -- **ID Generation**: Never use `crypto.randomUUID()`, `nanoid`, or `uuid` package. Use `generateId()` (UUID v4) or `generateShortId()` (compact) from `@/lib/core/utils/uuid` -- **Common Utilities**: Use shared helpers from `@/lib/core/utils/helpers` instead of inline implementations. `sleep(ms)` for delays, `toError(e)` to normalize caught values. +- **ID Generation**: Never use `crypto.randomUUID()`, `nanoid`, or `uuid` package. Use `generateId()` (UUID v4) or `generateShortId()` (compact) from `@sim/utils/id` +- **Common Utilities**: Use shared helpers from `@sim/utils` instead of inline implementations. `sleep(ms)` from `@sim/utils/helpers` for delays, `toError(e)` from `@sim/utils/errors` to normalize caught values. - **Package Manager**: Use `bun` and `bunx`, not `npm` and `npx` ## Architecture diff --git a/README.md b/README.md index de6befd2a8f..57c0192825e 100644 --- a/README.md +++ b/README.md @@ -142,13 +142,15 @@ See the [environment variables reference](https://docs.sim.ai/self-hosting/envir - **Database**: PostgreSQL with [Drizzle ORM](https://orm.drizzle.team) - **Authentication**: [Better Auth](https://better-auth.com) - **UI**: [Shadcn](https://ui.shadcn.com/), [Tailwind CSS](https://tailwindcss.com) -- **State Management**: [Zustand](https://zustand-demo.pmnd.rs/) +- **Streaming Markdown**: [Streamdown](https://github.com/vercel/streamdown) +- **State Management**: [Zustand](https://zustand-demo.pmnd.rs/), [TanStack Query](https://tanstack.com/query) - **Flow Editor**: [ReactFlow](https://reactflow.dev/) - **Docs**: [Fumadocs](https://fumadocs.vercel.app/) - **Monorepo**: [Turborepo](https://turborepo.org/) - **Realtime**: [Socket.io](https://socket.io/) - **Background Jobs**: [Trigger.dev](https://trigger.dev/) - **Remote Code Execution**: [E2B](https://www.e2b.dev/) +- **Isolated Code Execution**: [isolated-vm](https://github.com/laverdet/isolated-vm) ## Contributing diff --git a/apps/sim/app/(landing)/integrations/not-found.tsx b/apps/sim/app/(landing)/integrations/not-found.tsx new file mode 100644 index 00000000000..b4ca1ec9d64 --- /dev/null +++ b/apps/sim/app/(landing)/integrations/not-found.tsx @@ -0,0 +1,28 @@ +import type { Metadata } from 'next' +import Link from 'next/link' +import { AUTH_PRIMARY_CTA_BASE } from '@/app/(auth)/components/auth-button-classes' + +export const metadata: Metadata = { + title: 'Page Not Found', + robots: { index: false, follow: true }, +} + +export default function IntegrationsNotFound() { + return ( +
+ The page you're looking for doesn't exist or has been moved. +
++ The page you're looking for doesn't exist or has been moved. +
+- Are you sure you want to delete{' '} - {deletingColumn}?{' '} + {deletingColumns && deletingColumns.length > 1 ? ( + <> + Are you sure you want to delete{' '} + + {deletingColumns.length} columns + + ?{' '} + > + ) : ( + <> + Are you sure you want to delete{' '} + + {deletingColumns?.[0]} + + ?{' '} + > + )} - This will remove all data in this column. + This will remove all data in{' '} + {deletingColumns && deletingColumns.length > 1 ? 'these columns' : 'this column'}. {' '} - This action cannot be undone. + You can undo this action.