diff --git a/.codelit-platform.json b/.codelit-platform.json index ccebde2..a0025c8 100644 --- a/.codelit-platform.json +++ b/.codelit-platform.json @@ -4,7 +4,7 @@ "name": "SendLit", "slug": "sendlit" }, - "cliVersion": "0.4.2", + "cliVersion": "0.6.0", "capabilities": [ "auth", "mcp", @@ -12,7 +12,7 @@ "billing" ], "managedFiles": { - ".github/workflows/platform-conformance.yml": "939bf5686a4433b93153ca2b919ba77251c7bf70b17b269cde02606be41535f4" + ".github/workflows/platform-conformance.yml": "2f7ededb9ad4a18228873bf23e5607aaa3822fc236762af2eda1a18a82d35d8f" }, "productOwnedGlobs": [ "apps/**", diff --git a/.github/workflows/platform-conformance.yml b/.github/workflows/platform-conformance.yml index de747b2..a1b25a6 100644 --- a/.github/workflows/platform-conformance.yml +++ b/.github/workflows/platform-conformance.yml @@ -23,7 +23,7 @@ jobs: if: ${{ !hashFiles('bun.lock') }} - run: bun install --frozen-lockfile # Pinned to the CLI release that wrote this file. `sync` moves it. - - run: bunx @codelitdev/platform-cli@0.4.2 doctor + - run: bunx @codelitdev/platform-cli@0.6.0 doctor - run: bun run --filter "*/api" check:drift # Fails when the API package has no `test:conformance` script. - run: bun run --filter "*/api" test:conformance diff --git a/apps/api/.env.example b/apps/api/.env.example index 81cab49..c4d3511 100644 --- a/apps/api/.env.example +++ b/apps/api/.env.example @@ -155,6 +155,11 @@ MEDIALIT_SERVER=https://api.medialit.cloud GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= +# Cloudflare Turnstile on the sign-in page (https://dash.cloudflare.com/?to=/:account/turnstile). +# Set both to turn it on; it checks the request for a sign-in code. +TURNSTILE_SITE_KEY= +TURNSTILE_SECRET_KEY= + # Hosted abuse controls (cloud only; see src/abuse). Review the queue with # `bun scripts/abuse.ts queue`. # ABUSE_CONTROLS=off # switch all of them off diff --git a/apps/api/package.json b/apps/api/package.json index ac4b0f3..ea28df5 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -30,11 +30,11 @@ "@anthropic-ai/sdk": "^0.135.0", "@better-auth/cimd": "1.7.7", "@better-auth/oauth-provider": "1.7.7", - "@codelitdev/billing": "0.4.2", - "@codelitdev/mcp-server-kit": "0.4.2", - "@codelitdev/oauth-server-kit": "0.4.2", - "@codelitdev/observability": "0.4.2", - "@codelitdev/platform": "0.4.2", + "@codelitdev/billing": "0.6.0", + "@codelitdev/mcp-server-kit": "0.6.0", + "@codelitdev/oauth-server-kit": "0.6.0", + "@codelitdev/observability": "0.6.0", + "@codelitdev/platform": "0.6.0", "@modelcontextprotocol/node": "^2.0.0", "@modelcontextprotocol/server": "^2.0.0", "@sendlit/api-contract": "workspace:*", @@ -64,7 +64,7 @@ }, "devDependencies": { "@better-auth/core": "1.7.7", - "@codelitdev/platform-conformance": "0.4.2", + "@codelitdev/platform-conformance": "0.6.0", "@electric-sql/pglite": "0.5.8", "@modelcontextprotocol/client": "^2.0.0", "@sendlit/test-utils": "workspace:*", diff --git a/apps/api/src/auth/better-auth.ts b/apps/api/src/auth/better-auth.ts index 55475dc..3fe3f82 100644 --- a/apps/api/src/auth/better-auth.ts +++ b/apps/api/src/auth/better-auth.ts @@ -2,10 +2,14 @@ import { cimd } from "@better-auth/cimd"; import { oauthProvider } from "@better-auth/oauth-provider"; import { oauthProviderResourceClient } from "@better-auth/oauth-provider/resource-client"; import { createOAuthProviderOptions } from "@codelitdev/oauth-server-kit/better-auth"; -import type { HostedLoginMethod } from "@codelitdev/oauth-server-kit/express"; +import type { + HostedLoginCaptcha, + HostedLoginMethod, +} from "@codelitdev/oauth-server-kit/express"; import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { APIError, createAuthMiddleware } from "better-auth/api"; +import { captcha } from "better-auth/plugins"; import { emailOTP } from "better-auth/plugins/email-otp"; import { jwt } from "better-auth/plugins/jwt"; import { createTransport } from "nodemailer"; @@ -113,6 +117,17 @@ export const mcpProtectedResourceMetadataUrl = `${authBaseUrl}/.well-known/oauth const googleClientId = process.env.GOOGLE_CLIENT_ID; const googleClientSecret = process.env.GOOGLE_CLIENT_SECRET; +const turnstileSiteKey = process.env.TURNSTILE_SITE_KEY; +const turnstileSecretKey = process.env.TURNSTILE_SECRET_KEY; +/** Cloudflare Turnstile on the hosted sign-in form, on only when both keys + * are set: the page sends the widget's token and the `captcha` plugin below + * verifies it, so one without the other would refuse every email sign-in. + * Only asking for a code is checked; signing in still needs that code. */ +export const hostedLoginCaptcha: HostedLoginCaptcha | undefined = + turnstileSiteKey && turnstileSecretKey + ? { provider: "cloudflare-turnstile", siteKey: turnstileSiteKey } + : undefined; + /** Codes of type `email-verification` only confirm deleting an account * (`POST /account/deletion/code`); every other code signs in. */ function otpEmailCopy(type: string) { @@ -294,6 +309,15 @@ export const auth = betterAuth({ max: 3, }, }), + ...(hostedLoginCaptcha && turnstileSecretKey + ? [ + captcha({ + provider: "cloudflare-turnstile", + secretKey: turnstileSecretKey, + endpoints: ["/email-otp/send-verification-otp"], + }), + ] + : []), jwt(), oauthProvider({ ...createOAuthProviderOptions({ diff --git a/apps/api/src/auth/oauth-pages.test.ts b/apps/api/src/auth/oauth-pages.test.ts index f0076f5..19e56e0 100644 --- a/apps/api/src/auth/oauth-pages.test.ts +++ b/apps/api/src/auth/oauth-pages.test.ts @@ -12,6 +12,7 @@ mock.module("./better-auth", () => ({ webClientUrl: "http://localhost:3000", authBasePath: "/api/auth", hostedLoginMethods: [{ type: "email-otp" }], + hostedLoginCaptcha: { provider: "cloudflare-turnstile", siteKey: "0x4AAAAAAAtest" }, auth: { api: { getSession } }, oauthTeamSelectionAdapter: { listTeamsForUser, @@ -86,6 +87,17 @@ describe("SendLit OAuth pages", () => { expect(response.headers.get("x-frame-options")).toBe("DENY"); }); + it.each(["/login", "/oauth/login"])( + "renders the Turnstile widget on %s when it is configured", + async (path) => { + const response = await request(path); + expect(response.body).toContain( + 'class="cf-turnstile" data-sitekey="0x4AAAAAAAtest"', + ); + expect(response.body).toContain("var captcha=true"); + }, + ); + it("routes an unauthenticated team selection through OAuth login", async () => { const response = await request( "/oauth/select-team?client_id=abc&scope=contacts:read", diff --git a/apps/api/src/auth/oauth-pages.ts b/apps/api/src/auth/oauth-pages.ts index ffa090c..7658f44 100644 --- a/apps/api/src/auth/oauth-pages.ts +++ b/apps/api/src/auth/oauth-pages.ts @@ -3,6 +3,7 @@ import { Router } from "express"; import { auth, authBasePath, + hostedLoginCaptcha, hostedLoginMethods, oauthTeamSelectionAdapter, webClientUrl, @@ -21,6 +22,7 @@ router.use( faviconUrl: new URL("/icon.svg", webClientUrl).toString(), primaryColor: "oklch(0.47 0.14 150)", loginMethods: hostedLoginMethods, + captcha: hostedLoginCaptcha, legacyHostOnlySessionCookieNames: process.env.AUTH_COOKIE_DOMAIN ? [ "__Secure-better-auth.session_token", diff --git a/apps/docs/package.json b/apps/docs/package.json index ef83e80..a474156 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -11,7 +11,7 @@ "postinstall": "fumadocs-mdx" }, "dependencies": { - "@codelitdev/design-system": "0.4.2", + "@codelitdev/design-system": "0.6.0", "@sendlit/api-contract": "workspace:*", "@sendlit/email-blocks": "workspace:*", "@sendlit/email-editor": "workspace:*", diff --git a/apps/web/package.json b/apps/web/package.json index 82e2fd0..3467187 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -12,8 +12,8 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@codelitdev/design-system": "0.4.2", - "@codelitdev/observability": "0.4.2", + "@codelitdev/design-system": "0.6.0", + "@codelitdev/observability": "0.6.0", "@radix-ui/react-alert-dialog": "^1.1.18", "@radix-ui/react-dialog": "^1.1.18", "@radix-ui/react-label": "^2.1.11", diff --git a/bun.lock b/bun.lock index 9ebd51c..ab60331 100644 --- a/bun.lock +++ b/bun.lock @@ -19,11 +19,11 @@ "@anthropic-ai/sdk": "^0.135.0", "@better-auth/cimd": "1.7.7", "@better-auth/oauth-provider": "1.7.7", - "@codelitdev/billing": "0.4.2", - "@codelitdev/mcp-server-kit": "0.4.2", - "@codelitdev/oauth-server-kit": "0.4.2", - "@codelitdev/observability": "0.4.2", - "@codelitdev/platform": "0.4.2", + "@codelitdev/billing": "0.6.0", + "@codelitdev/mcp-server-kit": "0.6.0", + "@codelitdev/oauth-server-kit": "0.6.0", + "@codelitdev/observability": "0.6.0", + "@codelitdev/platform": "0.6.0", "@modelcontextprotocol/node": "^2.0.0", "@modelcontextprotocol/server": "^2.0.0", "@sendlit/api-contract": "workspace:*", @@ -53,7 +53,7 @@ }, "devDependencies": { "@better-auth/core": "1.7.7", - "@codelitdev/platform-conformance": "0.4.2", + "@codelitdev/platform-conformance": "0.6.0", "@electric-sql/pglite": "0.5.8", "@modelcontextprotocol/client": "^2.0.0", "@sendlit/test-utils": "workspace:*", @@ -73,7 +73,7 @@ "name": "@sendlit/docs", "version": "0.1.0", "dependencies": { - "@codelitdev/design-system": "0.4.2", + "@codelitdev/design-system": "0.6.0", "@sendlit/api-contract": "workspace:*", "@sendlit/email-blocks": "workspace:*", "@sendlit/email-editor": "workspace:*", @@ -82,7 +82,7 @@ "fumadocs-openapi": "10.4.1", "fumadocs-ui": "16.7.1", "lucide-react": "^0.577.0", - "next": "16.3.6", + "next": "16.4.1", "react": "19.2.8", "react-dom": "19.2.8", "shiki": "^4.3.1", @@ -102,8 +102,8 @@ "name": "@sendlit/web", "version": "0.1.0", "dependencies": { - "@codelitdev/design-system": "0.4.2", - "@codelitdev/observability": "0.4.2", + "@codelitdev/design-system": "0.6.0", + "@codelitdev/observability": "0.6.0", "@radix-ui/react-alert-dialog": "^1.1.18", "@radix-ui/react-dialog": "^1.1.18", "@radix-ui/react-label": "^2.1.11", @@ -117,7 +117,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.577.0", - "next": "16.3.6", + "next": "16.4.1", "next-themes": "^0.4.6", "papaparse": "^5.7.0", "posthog-js": "^1.425.1", @@ -160,7 +160,7 @@ }, "packages/email-blocks": { "name": "@sendlit/email-blocks", - "version": "0.2.0", + "version": "0.3.0", "dependencies": { "@radix-ui/react-alert-dialog": "^1.1.18", "@radix-ui/react-avatar": "^1.2.1", @@ -235,7 +235,7 @@ }, "packages/sendlit": { "name": "sendlit", - "version": "0.2.0", + "version": "0.3.0", "dependencies": { "@ts-rest/core": "3.52.1", "zod": "3.25.76", @@ -416,19 +416,19 @@ "@changesets/write": ["@changesets/write@0.4.0", "", { "dependencies": { "@changesets/types": "^6.1.0", "fs-extra": "^7.0.1", "human-id": "^4.1.1", "prettier": "^2.7.1" } }, "sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q=="], - "@codelitdev/billing": ["@codelitdev/billing@0.4.2", "", { "dependencies": { "drizzle-orm": "^0.45.2", "nanoid": "^5.1.6", "zod": "^3.25.76" }, "peerDependencies": { "dodopayments": "^2.48.0" }, "optionalPeers": ["dodopayments"], "bin": { "codelit-billing": "bin/codelit-billing.js" } }, "sha512-tjFaDsnTMh0hj3yYhhsBNOai7pzkv5xo0lslLh/TAqIXw3YzC3kUjQX7xryPkMGhDEnvZm5igtGVob6UEdofCw=="], + "@codelitdev/billing": ["@codelitdev/billing@0.6.0", "", { "dependencies": { "drizzle-orm": "^0.45.2", "nanoid": "^5.1.6", "zod": "^3.25.76" }, "peerDependencies": { "dodopayments": "^2.48.0" }, "optionalPeers": ["dodopayments"], "bin": { "codelit-billing": "bin/codelit-billing.js" } }, "sha512-Fsr1H6ubCYu/tN1rpyAGCEcH8Jb0h5V1zm36H9SqfdnkS+QSOjR9F2BSe1Ij/jeH+5gTGr13lX61lL45/pHwAQ=="], - "@codelitdev/design-system": ["@codelitdev/design-system@0.4.2", "", { "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-P8XpUW6/MDoyZ8cvVExGjhz7LtTEVFKMrBcjXcjwbPYGmDKeNj3PQkVP3V5sfYno5NQY/XFVcWR1f/vVsIIb7w=="], + "@codelitdev/design-system": ["@codelitdev/design-system@0.6.0", "", { "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-7A+Qmckj8FUii1Ly3s9zrZpnvcY5GLZdo5mZABn6VTfu+EgQWMAbuy41+st6h/Y5J55A2W5FU77YY28eHXI+eA=="], - "@codelitdev/mcp-server-kit": ["@codelitdev/mcp-server-kit@0.4.2", "", { "dependencies": { "@modelcontextprotocol/sdk": "^1.30.0", "zod": "^3.25.76" }, "peerDependencies": { "@codelitdev/platform": ">=0.4.2" } }, "sha512-1Wdd4ELa4zARXfa3Mpam7ZizHpfPC1XBjBHc5ny0Xi7uiojHLpU0pT7R94D0cCqeEnZvJntiBMzABgAyPS2esg=="], + "@codelitdev/mcp-server-kit": ["@codelitdev/mcp-server-kit@0.6.0", "", { "dependencies": { "@modelcontextprotocol/server": "^2.4.0", "zod": "^3.25.76", "zod-to-json-schema": "^3.25.2" }, "peerDependencies": { "@codelitdev/platform": ">=0.6.0" } }, "sha512-AOjVsVkmEvIGkjrsEIAfijB4PIuwlXW2p+YdA2KVelE/SGh8DsJuWtwqNzfdp6419T30+IVXSlodcdIqQY8eMA=="], - "@codelitdev/oauth-server-kit": ["@codelitdev/oauth-server-kit@0.4.2", "", { "peerDependencies": { "@better-auth/oauth-provider": "^1.7.2", "better-auth": "^1.7.2", "express": "^4.21.2" } }, "sha512-3KD3J2etQqDrqkzyvew/aCqlYaA1ivXW/34aMLLlEMwUlf0a4FrzCfmalSqv2IIGeogz9kqZ/mnqrvqduBak1w=="], + "@codelitdev/oauth-server-kit": ["@codelitdev/oauth-server-kit@0.6.0", "", { "peerDependencies": { "@better-auth/oauth-provider": "^1.7.2", "better-auth": "^1.7.2", "express": "^4.21.2" } }, "sha512-AjgSkdzWhrgykntLxosKADZeI7JauBz82TCrxRnVs7XmwFJp0PPl4ncSl1KSYFflLzZ33wQvmlsEHc+0tjymNw=="], - "@codelitdev/observability": ["@codelitdev/observability@0.4.2", "", { "dependencies": { "pino": "^9.6.0" }, "peerDependencies": { "express": "^4.21.2", "posthog-js": "^1.200.0", "posthog-node": "^4.0.0" }, "optionalPeers": ["express", "posthog-js", "posthog-node"] }, "sha512-cPcXOkrp3G1uAC4Y8rt0GkmJ2vKw8ydL6j4sxL3FeSZBn083tiLVbL4FtHxICZqZMWA3OODom6xKA/L0EIYNxw=="], + "@codelitdev/observability": ["@codelitdev/observability@0.6.0", "", { "dependencies": { "pino": "^9.6.0" }, "peerDependencies": { "express": "^4.21.2", "posthog-js": "^1.200.0", "posthog-node": "^4.0.0" }, "optionalPeers": ["express", "posthog-js", "posthog-node"] }, "sha512-RIRcxQm/B/bZ5BvAeShM8wyKUUo7i5zb3ljmfnMiK2V8wXKeHUkjHGC3jFPOorFrLqQbu0m73du8ddy1oPNcnQ=="], - "@codelitdev/platform": ["@codelitdev/platform@0.4.2", "", { "peerDependencies": { "@codelitdev/billing": ">=0.4.2", "drizzle-orm": "^0.45.2" }, "optionalPeers": ["@codelitdev/billing", "drizzle-orm"] }, "sha512-eZd2UJ+aQ/AOCwRBhE5btI9gXEMOnuvash8GgWU4zSbO1Pu0Hk4cr4FhbL/VpqwASvgvzvlDnYm0es+LRo38HQ=="], + "@codelitdev/platform": ["@codelitdev/platform@0.6.0", "", { "peerDependencies": { "@codelitdev/billing": ">=0.6.0", "drizzle-orm": "^0.45.2" }, "optionalPeers": ["@codelitdev/billing", "drizzle-orm"] }, "sha512-WqaL6b8c+DXVvQWUzt6pBsJjAdQqFgy3BzFgQ9wwQ+/7vZF9TwCL/dtFQ4IoNSohmvREmT2C5hLzqOAu+mLSGQ=="], - "@codelitdev/platform-conformance": ["@codelitdev/platform-conformance@0.4.2", "", { "peerDependencies": { "@codelitdev/platform": ">=0.4.2" } }, "sha512-dvVvOrhP5cFe86FaI1Qf1sGAdS0NIeZasNrN9OBoTAIAeo+zM4WseRsXdhs4PGjYf5aD11ycZNGbgzrzg4KXdg=="], + "@codelitdev/platform-conformance": ["@codelitdev/platform-conformance@0.6.0", "", { "peerDependencies": { "@codelitdev/platform": ">=0.6.0" } }, "sha512-KR6jaINk5YUjZOR52GwfVQuoaShmK+l/CBBuhevfiw2ubNG3BRBZrrbt4GVHtnR4WGAjHEY5gscqUmAIzUokdA=="], "@csstools/color-helpers": ["@csstools/color-helpers@5.1.0", "", {}, "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA=="], @@ -2060,7 +2060,7 @@ "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], - "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], + "raw-body": ["raw-body@2.5.2", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA=="], "react": ["react@19.2.8", "", {}, "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw=="], @@ -2468,6 +2468,8 @@ "@changesets/write/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], + "@codelitdev/mcp-server-kit/@modelcontextprotocol/server": ["@modelcontextprotocol/server@2.4.0", "", { "dependencies": { "@modelcontextprotocol/core": "2.4.0", "zod": "^4.2.0" } }, "sha512-rgR9yxSaAJ+bcsi/XroT/S2AfPiIWloXAnrdfYSCms9ivVju0ishu/wW0J21Ho6+ZOR1r7RyeRbjiRuF87ix5Q=="], + "@dotenvx/dotenvx/commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="], "@dotenvx/dotenvx/open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="], @@ -2496,6 +2498,8 @@ "@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.7.1", "", { "dependencies": { "debug": "^4.4.3", "ip-address": "^10.2.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-2FXbqkygil3mZiXzEtM0IjE/UBmBNot0J1GEVrJT4JbrvwOSoEtqtO+wHbiMQELWe1eNvHoYRg2dkjOid+6JkA=="], + "@modelcontextprotocol/sdk/raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], + "@modelcontextprotocol/sdk/zod": ["zod@4.6.7", "", {}, "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg=="], "@modelcontextprotocol/server/zod": ["zod@4.6.7", "", {}, "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg=="], @@ -2548,8 +2552,6 @@ "body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], - "body-parser/raw-body": ["raw-body@2.5.2", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA=="], - "conf/ajv-formats": ["ajv-formats@2.1.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA=="], "conf/json-schema-typed": ["json-schema-typed@7.0.3", "", {}, "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A=="], @@ -2614,7 +2616,7 @@ "proper-lockfile/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], - "raw-body/http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + "raw-body/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], "read-yaml-file/js-yaml": ["js-yaml@3.15.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w=="], @@ -2662,6 +2664,10 @@ "wsl-utils/is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="], + "@codelitdev/mcp-server-kit/@modelcontextprotocol/server/@modelcontextprotocol/core": ["@modelcontextprotocol/core@2.4.0", "", { "dependencies": { "zod": "^4.2.0" } }, "sha512-z8QBKqdfXLPzkLQuG4xp8hrVEMm1PZ1IgxMN2sMbJiFiAKOWXnAzogXn2W4aqIBlP1ea6U5yiqVquvUNM1Cj6Q=="], + + "@codelitdev/mcp-server-kit/@modelcontextprotocol/server/zod": ["zod@4.6.7", "", {}, "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg=="], + "@dotenvx/dotenvx/open/define-lazy-prop": ["define-lazy-prop@2.0.0", "", {}, "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="], "@dotenvx/dotenvx/which/isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], @@ -2744,6 +2750,8 @@ "@modelcontextprotocol/sdk/express-rate-limit/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "@modelcontextprotocol/sdk/raw-body/http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + "@sendlit/email-blocks/tailwindcss/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], "@sendlit/email-editor/tailwindcss/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], @@ -2822,8 +2830,6 @@ "pkg-up/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="], - "raw-body/http-errors/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], - "read-yaml-file/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], "shadcn/execa/get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], @@ -2952,6 +2958,8 @@ "@modelcontextprotocol/sdk/express/type-is/media-typer": ["media-typer@1.1.1", "", {}, "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ=="], + "@modelcontextprotocol/sdk/raw-body/http-errors/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + "@sendlit/email-blocks/tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "@sendlit/email-blocks/tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],