From 0552549db5e89d9db0c57c7e91dc0bde8fec4708 Mon Sep 17 00:00:00 2001 From: Andreas Thomas Date: Wed, 6 Nov 2024 10:18:25 +0100 Subject: [PATCH] refactor: clickhouse migration #2 (#2135) * feat: clickhouse keyverification tables * feat(clickhouse): add new file latest_verifications.ts for querying latest verifications from Clickhouse feat(tinybird): remove unused import 'time' from node:console feat(tinybird): remove unused functions and pipes related to verifications and active keys feat(tinybird): add comment indicating completion of changes by @andreas * [autofix.ci] apply automated fixes * feat: add ratelimits * refactor: clickhouse table names * refactor: replace more pipes * refactor: replace more pipes * refactor: replace more pipes * wip: everything broken * [autofix.ci] apply automated fixes * wip * feat: billing verifications * wip * fix: merge conflicts * feat: ratelimits on clickhouse * feat: ratelimit logs * wip * feat: it builds again * feat: downmigrations * feat: downmigrations * fix: type export * Update internal/clickhouse/src/ratelimits.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix: table engine --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- Taskfile.yml | 2 +- ...05_create_mv_key_verifications_per_day.sql | 19 - apps/api/package.json | 2 +- apps/api/src/pkg/analytics.ts | 36 +- apps/api/src/pkg/keys/service.ts | 2 +- apps/api/src/pkg/middleware/metrics.ts | 8 + apps/api/src/pkg/ratelimit/client.ts | 16 +- apps/api/src/pkg/ratelimit/interface.ts | 2 +- apps/api/src/pkg/ratelimit/noop.ts | 4 +- apps/api/src/routes/v1_ratelimit_limit.ts | 23 +- .../[apiId]/keys/[keyAuthId]/[keyId]/page.tsx | 153 +- .../[keyId]/verification-table.tsx | 40 +- .../dashboard/app/(app)/apis/[apiId]/page.tsx | 146 +- apps/dashboard/app/(app)/banner.tsx | 18 +- .../(app)/identities/[identityId]/page.tsx | 18 +- apps/dashboard/app/(app)/logs/page.tsx | 4 +- .../ratelimits/[namespaceId]/logs/page.tsx | 25 +- .../[namespaceId]/overrides/table.tsx | 35 +- .../(app)/ratelimits/[namespaceId]/page.tsx | 163 +- apps/dashboard/app/(app)/ratelimits/card.tsx | 18 +- .../app/(app)/ratelimits/sparkline.tsx | 6 +- .../app/(app)/settings/billing/page.tsx | 29 +- .../[keyId]/history/access-table.tsx | 27 +- .../root-keys/[keyId]/history/page.tsx | 10 +- .../settings/root-keys/[keyId]/layout.tsx | 14 +- .../(app)/settings/root-keys/[keyId]/page.tsx | 10 +- apps/dashboard/app/(app)/success/page.tsx | 8 +- apps/dashboard/components/array-input.tsx | 4 +- apps/dashboard/lib/analytics.tsx | 87 - apps/dashboard/lib/charts/sparkline.tsx | 6 +- apps/dashboard/lib/clickhouse.ts | 4 + apps/dashboard/lib/clickhouse/index.ts | 52 - apps/dashboard/lib/tinybird.ts | 603 +------ apps/dashboard/middleware.ts | 7 - apps/dashboard/package.json | 2 +- apps/play/app/page-bk.tsx | 2 +- apps/www/.env.example | 3 +- apps/www/components/stats.tsx | 13 +- apps/www/lib/env.ts | 1 - apps/www/lib/tinybird.ts | 11 - deployment/grafana/grafana.yaml | 12 + .../package.json | 2 +- .../clickhouse/schema/000_README.md | 12 +- .../schema/001_create_databases.sql | 17 + ...002_create_metrics_raw_api_requests_v1.sql | 12 +- ...verifications_raw_key_verifications_v1.sql | 5 +- ...ications_key_verifications_per_hour_v1.sql | 18 + ...fications_key_verifications_per_day_v1.sql | 11 +- ...cations_key_verifications_per_month_v1.sql | 17 + ...tions_key_verifications_per_hour_mv_v1.sql | 25 + ...ations_key_verifications_per_day_mv_v1.sql | 25 + ...ions_key_verifications_per_month_mv_v1.sql | 25 + ...create_ratelimits_raw_ratelimits_table.sql | 19 + .../011_create_telemetry_raw_sdks_v1.sql | 7 +- ...ng_billable_verifications_per_month_v1.sql | 15 + ...billable_verifications_per_month_mv_v1.sql | 21 + ...te_ratelimits_ratelimits_per_minute_v1.sql | 19 + ...eate_ratelimits_ratelimits_per_hour_v1.sql | 18 + ...reate_ratelimits_ratelimits_per_day_v1.sql | 19 + ...ate_ratelimits_ratelimits_per_month_v1.sql | 19 + ...ratelimits_ratelimits_per_minute_mv_v1.sql | 23 + ...e_ratelimits_ratelimits_per_hour_mv_v1.sql | 22 + ...te_ratelimits_ratelimits_per_day_mv_v1.sql | 23 + ..._ratelimits_ratelimits_per_month_mv_v1.sql | 22 + ...usiness_active_workspaces_per_month_v1.sql | 13 + ...ness_active_workspaces_per_month_mv_v1.sql | 21 + .../024_create_ratelimits_last_used_mv_v1.sql | 19 + internal/clickhouse/src/active_keys.ts | 114 ++ internal/clickhouse/src/billing.ts | 38 + .../src => clickhouse/src/client}/client.ts | 22 +- .../src => clickhouse/src/client}/index.ts | 0 .../src/client}/interface.ts | 4 +- .../src => clickhouse/src/client}/noop.ts | 4 +- internal/clickhouse/src/index.ts | 80 + internal/clickhouse/src/last_used.ts | 36 + .../clickhouse/src/latest_verifications.ts | 33 + internal/clickhouse/src/logs.ts | 49 + internal/clickhouse/src/ratelimits.ts | 212 +++ internal/clickhouse/src/success.ts | 26 + internal/clickhouse/src/util.ts | 3 + internal/clickhouse/src/verifications.ts | 109 ++ internal/db/src/schema/workspaces.ts | 4 +- internal/db/src/types.ts | 2 + pnpm-lock.yaml | 1591 ++++++----------- tools/migrate/auditlog-import.ts | 130 +- tools/migrate/package.json | 5 +- tools/migrate/tinybird-export.ts | 14 +- 87 files changed, 2148 insertions(+), 2422 deletions(-) delete mode 100644 apps/agent/pkg/clickhouse/schema/005_create_mv_key_verifications_per_day.sql delete mode 100644 apps/dashboard/lib/analytics.tsx create mode 100644 apps/dashboard/lib/clickhouse.ts delete mode 100644 apps/dashboard/lib/clickhouse/index.ts delete mode 100644 apps/www/lib/tinybird.ts rename internal/{clickhouse-zod => clickhouse}/package.json (90%) rename {apps/agent/pkg => internal}/clickhouse/schema/000_README.md (87%) create mode 100644 internal/clickhouse/schema/001_create_databases.sql rename apps/agent/pkg/clickhouse/schema/001_create_raw_api_requests_table.sql => internal/clickhouse/schema/002_create_metrics_raw_api_requests_v1.sql (76%) rename apps/agent/pkg/clickhouse/schema/002_create_raw_key_verifications_table.sql => internal/clickhouse/schema/003_create_verifications_raw_key_verifications_v1.sql (85%) create mode 100644 internal/clickhouse/schema/004_create_verifications_key_verifications_per_hour_v1.sql rename apps/agent/pkg/clickhouse/schema/004_create_key_verifications_per_day.sql => internal/clickhouse/schema/005_create_verifications_key_verifications_per_day_v1.sql (57%) create mode 100644 internal/clickhouse/schema/006_create_verifications_key_verifications_per_month_v1.sql create mode 100644 internal/clickhouse/schema/007_create_verifications_key_verifications_per_hour_mv_v1.sql create mode 100644 internal/clickhouse/schema/008_create_verifications_key_verifications_per_day_mv_v1.sql create mode 100644 internal/clickhouse/schema/009_create_verifications_key_verifications_per_month_mv_v1.sql create mode 100644 internal/clickhouse/schema/010_create_ratelimits_raw_ratelimits_table.sql rename apps/agent/pkg/clickhouse/schema/003_create_raw_telemetry_sdks_table.sql => internal/clickhouse/schema/011_create_telemetry_raw_sdks_v1.sql (80%) create mode 100644 internal/clickhouse/schema/012_create_billing_billable_verifications_per_month_v1.sql create mode 100644 internal/clickhouse/schema/013_create_billing_billable_verifications_per_month_mv_v1.sql create mode 100644 internal/clickhouse/schema/014_create_ratelimits_ratelimits_per_minute_v1.sql create mode 100644 internal/clickhouse/schema/015_create_ratelimits_ratelimits_per_hour_v1.sql create mode 100644 internal/clickhouse/schema/016_create_ratelimits_ratelimits_per_day_v1.sql create mode 100644 internal/clickhouse/schema/017_create_ratelimits_ratelimits_per_month_v1.sql create mode 100644 internal/clickhouse/schema/018_create_ratelimits_ratelimits_per_minute_mv_v1.sql create mode 100644 internal/clickhouse/schema/019_create_ratelimits_ratelimits_per_hour_mv_v1.sql create mode 100644 internal/clickhouse/schema/020_create_ratelimits_ratelimits_per_day_mv_v1.sql create mode 100644 internal/clickhouse/schema/021_create_ratelimits_ratelimits_per_month_mv_v1.sql create mode 100644 internal/clickhouse/schema/022_create_business_active_workspaces_per_month_v1.sql create mode 100644 internal/clickhouse/schema/023_create_business_active_workspaces_per_month_mv_v1.sql create mode 100644 internal/clickhouse/schema/024_create_ratelimits_last_used_mv_v1.sql create mode 100644 internal/clickhouse/src/active_keys.ts create mode 100644 internal/clickhouse/src/billing.ts rename internal/{clickhouse-zod/src => clickhouse/src/client}/client.ts (77%) rename internal/{clickhouse-zod/src => clickhouse/src/client}/index.ts (100%) rename internal/{clickhouse-zod/src => clickhouse/src/client}/interface.ts (92%) rename internal/{clickhouse-zod/src => clickhouse/src/client}/noop.ts (92%) create mode 100644 internal/clickhouse/src/index.ts create mode 100644 internal/clickhouse/src/last_used.ts create mode 100644 internal/clickhouse/src/latest_verifications.ts create mode 100644 internal/clickhouse/src/logs.ts create mode 100644 internal/clickhouse/src/ratelimits.ts create mode 100644 internal/clickhouse/src/success.ts create mode 100644 internal/clickhouse/src/util.ts create mode 100644 internal/clickhouse/src/verifications.ts diff --git a/Taskfile.yml b/Taskfile.yml index 4e44a5160b..862a8ccd1a 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -23,7 +23,7 @@ tasks: env: GOOSE_DRIVER: clickhouse GOOSE_DBSTRING: "tcp://default:password@127.0.0.1:9000" - GOOSE_MIGRATION_DIR: ./apps/agent/pkg/clickhouse/schema + GOOSE_MIGRATION_DIR: ./internal/clickhouse/schema cmds: - goose up diff --git a/apps/agent/pkg/clickhouse/schema/005_create_mv_key_verifications_per_day.sql b/apps/agent/pkg/clickhouse/schema/005_create_mv_key_verifications_per_day.sql deleted file mode 100644 index efeddc0c6f..0000000000 --- a/apps/agent/pkg/clickhouse/schema/005_create_mv_key_verifications_per_day.sql +++ /dev/null @@ -1,19 +0,0 @@ --- +goose up -CREATE MATERIALIZED VIEW default.mv_key_verifications_per_day_v1 TO default.key_verifications_per_day_v1 AS -SELECT - workspace_id, - key_space_id, - identity_id, - key_id, - outcome, - countState() as count, - toStartOfDay(fromUnixTimestamp64Milli(time)) AS time -FROM default.raw_key_verifications_v1 -GROUP BY - workspace_id, - key_space_id, - identity_id, - key_id, - outcome, - time -; diff --git a/apps/api/package.json b/apps/api/package.json index 6945d90ee3..2a0a98e142 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -27,7 +27,7 @@ "@hono/zod-validator": "^0.2.1", "@planetscale/database": "^1.16.0", "@unkey/cache": "workspace:^", - "@unkey/clickhouse-zod": "workspace:^", + "@unkey/clickhouse": "workspace:^", "@unkey/db": "workspace:^", "@unkey/encryption": "workspace:^", "@unkey/error": "workspace:^", diff --git a/apps/api/src/pkg/analytics.ts b/apps/api/src/pkg/analytics.ts index 2cf78f3ea7..41782581dd 100644 --- a/apps/api/src/pkg/analytics.ts +++ b/apps/api/src/pkg/analytics.ts @@ -1,5 +1,5 @@ import { NoopTinybird, Tinybird } from "@chronark/zod-bird"; -import * as ch from "@unkey/clickhouse-zod"; +import { ClickHouse } from "@unkey/clickhouse"; import { newId } from "@unkey/id"; import { auditLogSchemaV1, unkeyAuditLogEvents } from "@unkey/schema/src/auditlog"; import { ratelimitSchemaV1 } from "@unkey/schema/src/ratelimit-tinybird"; @@ -18,7 +18,7 @@ const dateToUnixMilli = z.string().transform((t) => new Date(t.split(" ").at(0) export class Analytics { public readonly readClient: Tinybird | NoopTinybird; public readonly writeClient: Tinybird | NoopTinybird; - private clickhouse: ch.Clickhouse; + private clickhouse: ClickHouse; constructor(opts: { tinybirdToken?: string; @@ -38,12 +38,12 @@ export class Analytics { ? new Tinybird({ token: opts.tinybirdProxy.token, baseUrl: opts.tinybirdProxy.url }) : this.readClient; - this.clickhouse = opts.clickhouse ? new ch.Client({ url: opts.clickhouse.url }) : new ch.Noop(); + this.clickhouse = new ClickHouse({ url: opts.clickhouse?.url }); } public get insertSdkTelemetry() { - return this.clickhouse.insert({ - table: "default.raw_telemetry_sdks_v1", + return this.clickhouse.client.insert({ + table: "telemetry.raw_sdks_v1", schema: z.object({ request_id: z.string(), time: z.number().int(), @@ -107,6 +107,20 @@ export class Analytics { })), }); } + public get insertRatelimit() { + return this.clickhouse.client.insert({ + table: "ratelimits.raw_ratelimits_v1", + schema: z.object({ + request_id: z.string(), + time: z.number().int(), + workspace_id: z.string(), + namespace_id: z.string(), + identifier: z.string(), + passed: z.boolean(), + }), + }); + } + //tinybird public get ingestRatelimit() { return this.writeClient.buildIngestEndpoint({ @@ -116,8 +130,8 @@ export class Analytics { } public get insertKeyVerification() { - return this.clickhouse.insert({ - table: "default.raw_key_verifications_v1", + return this.clickhouse.client.insert({ + table: "verifications.raw_key_verifications_v1", schema: z.object({ request_id: z.string(), time: z.number().int(), @@ -140,8 +154,8 @@ export class Analytics { } public get insertApiRequest() { - return this.clickhouse.insert({ - table: "default.raw_api_requests_v1", + return this.clickhouse.client.insert({ + table: "metrics.raw_api_requests_v1", schema: z.object({ request_id: z.string(), time: z.number().int(), @@ -158,6 +172,10 @@ export class Analytics { service_latency: z.number().int(), user_agent: z.string(), ip_address: z.string(), + continent: z.string().nullable().default(""), + city: z.string().nullable().default(""), + country: z.string().nullable().default(""), + colo: z.string().nullable().default(""), }), }); } diff --git a/apps/api/src/pkg/keys/service.ts b/apps/api/src/pkg/keys/service.ts index 8b2dab21fd..f1b349b73d 100644 --- a/apps/api/src/pkg/keys/service.ts +++ b/apps/api/src/pkg/keys/service.ts @@ -678,7 +678,7 @@ export class KeyService { } return [ - res.val.pass, + res.val.passed, { remaining: res.val.remaining, limit: ratelimits.default?.limit, diff --git a/apps/api/src/pkg/middleware/metrics.ts b/apps/api/src/pkg/middleware/metrics.ts index 87be7f43c4..cd1ae99bac 100644 --- a/apps/api/src/pkg/middleware/metrics.ts +++ b/apps/api/src/pkg/middleware/metrics.ts @@ -125,6 +125,14 @@ export function metrics(): MiddlewareHandler { service_latency: Date.now() - c.get("requestStartedAt"), ip_address: c.req.header("True-Client-IP") ?? c.req.header("CF-Connecting-IP") ?? "", user_agent: c.req.header("User-Agent") ?? "", + // @ts-ignore - this is a bug in the types + continent: c.req.raw?.cf?.continent, + // @ts-ignore - this is a bug in the types + country: c.req.raw?.cf?.country, + // @ts-ignore - this is a bug in the types + colo: c.req.raw?.cf?.colo, + // @ts-ignore - this is a bug in the types + city: c.req.raw?.cf?.city, }), ); } diff --git a/apps/api/src/pkg/ratelimit/client.ts b/apps/api/src/pkg/ratelimit/client.ts index e059ab29cd..0e405d8e56 100644 --- a/apps/api/src/pkg/ratelimit/client.ts +++ b/apps/api/src/pkg/ratelimit/client.ts @@ -87,7 +87,7 @@ export class AgentRatelimiter implements RateLimiter { source: "cloudflare", }); return Ok({ - pass: res.success, + passed: res.success, reset: -1, current: -1, remaining: -1, @@ -109,7 +109,7 @@ export class AgentRatelimiter implements RateLimiter { identifier: req.identifier, mode: req.async ? "async" : "sync", error: !!res.err, - success: res?.val?.pass, + success: res?.val?.passed, source: "agent", }); return res; @@ -127,7 +127,7 @@ export class AgentRatelimiter implements RateLimiter { if (r.err) { return r; } - if (!r.val.pass) { + if (!r.val.passed) { return r; } } @@ -137,7 +137,7 @@ export class AgentRatelimiter implements RateLimiter { return Ok({ current: -1, - pass: true, + passed: true, reset: -1, remaining: -1, triggered: null, @@ -162,7 +162,7 @@ export class AgentRatelimiter implements RateLimiter { const cached = this.cache.get(id) ?? { current: 0, reset: 0 }; if (cached.current >= req.limit) { return Ok({ - pass: false, + passed: false, current: cached.current, reset, remaining: 0, @@ -224,7 +224,7 @@ export class AgentRatelimiter implements RateLimiter { if (cached.current + cost > req.limit) { return Ok({ current: cached.current, - pass: false, + passed: false, reset, remaining: req.limit - cached.current, triggered: req.name, @@ -234,7 +234,7 @@ export class AgentRatelimiter implements RateLimiter { this.setCacheMax(id, cached.current, reset); return Ok({ - pass: true, + passed: true, current: cached.current, reset, remaining: req.limit - cached.current, @@ -288,7 +288,7 @@ export class AgentRatelimiter implements RateLimiter { return Ok({ current: Number(res.limit - res.remaining), reset: Number(res.reset), - pass: res.success, + passed: res.success, remaining: Number(res.remaining), triggered: res.success ? null : req.name, }); diff --git a/apps/api/src/pkg/ratelimit/interface.ts b/apps/api/src/pkg/ratelimit/interface.ts index 97ca367154..e02dd72038 100644 --- a/apps/api/src/pkg/ratelimit/interface.ts +++ b/apps/api/src/pkg/ratelimit/interface.ts @@ -31,7 +31,7 @@ export const ratelimitResponseSchema = z.object({ current: z.number(), remaining: z.number(), reset: z.number(), - pass: z.boolean(), + passed: z.boolean(), /** * The name of the limit that triggered a rejection */ diff --git a/apps/api/src/pkg/ratelimit/noop.ts b/apps/api/src/pkg/ratelimit/noop.ts index 48c3904fc9..cbd27e456c 100644 --- a/apps/api/src/pkg/ratelimit/noop.ts +++ b/apps/api/src/pkg/ratelimit/noop.ts @@ -7,12 +7,12 @@ export class NoopRateLimiter implements RateLimiter { _c: Context, _req: RatelimitRequest, ): Promise> { - return Ok({ current: 0, pass: true, reset: 0, remaining: 0, triggered: null }); + return Ok({ current: 0, passed: true, reset: 0, remaining: 0, triggered: null }); } public async multiLimit( _c: Context, _req: Array, ): Promise> { - return Ok({ current: 0, pass: true, reset: 0, remaining: 0, triggered: null }); + return Ok({ current: 0, passed: true, reset: 0, remaining: 0, triggered: null }); } } diff --git a/apps/api/src/routes/v1_ratelimit_limit.ts b/apps/api/src/routes/v1_ratelimit_limit.ts index 4aa10d11ed..81e3c46bb9 100644 --- a/apps/api/src/routes/v1_ratelimit_limit.ts +++ b/apps/api/src/routes/v1_ratelimit_limit.ts @@ -342,6 +342,17 @@ export const registerV1RatelimitLimit = (app: App) => }); } const remaining = Math.max(0, limit - ratelimitResponse.current); + + c.executionCtx.waitUntil( + analytics.insertRatelimit({ + workspace_id: rootKey.authorizedWorkspaceId, + namespace_id: namespace.id, + request_id: c.get("requestId"), + identifier: req.identifier, + time: Date.now(), + passed: ratelimitResponse.passed, + }), + ); c.executionCtx.waitUntil( analytics .ingestRatelimit({ @@ -353,7 +364,7 @@ export const registerV1RatelimitLimit = (app: App) => time: Date.now(), serviceLatency: -1, - success: ratelimitResponse.pass, + success: ratelimitResponse.passed, remaining, config: { limit, @@ -392,12 +403,12 @@ export const registerV1RatelimitLimit = (app: App) => id: rootKey.key.id, }, description: "ratelimit", - event: ratelimitResponse.pass ? "ratelimit.success" : "ratelimit.denied", + event: ratelimitResponse.passed ? "ratelimit.success" : "ratelimit.denied", meta: { requestId: c.get("requestId"), namespacId: namespace.id, identifier: req.identifier, - success: ratelimitResponse.pass, + success: ratelimitResponse.passed, }, time: Date.now(), resources: req.resources ?? [], @@ -417,12 +428,12 @@ export const registerV1RatelimitLimit = (app: App) => id: rootKey.key.id, }, description: "ratelimit", - event: ratelimitResponse.pass ? "ratelimit.success" : "ratelimit.denied", + event: ratelimitResponse.passed ? "ratelimit.success" : "ratelimit.denied", meta: { requestId: c.get("requestId"), namespacId: namespace.id, identifier: req.identifier, - success: ratelimitResponse.pass, + success: ratelimitResponse.passed, }, time: Date.now(), resources: req.resources, @@ -438,6 +449,6 @@ export const registerV1RatelimitLimit = (app: App) => limit, remaining, reset: ratelimitResponse.reset, - success: ratelimitResponse.pass, + success: ratelimitResponse.passed, }); }); diff --git a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/page.tsx b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/page.tsx index d17c24347f..982c1a6d76 100644 --- a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/page.tsx +++ b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/page.tsx @@ -12,21 +12,15 @@ import { Button, buttonVariants } from "@/components/ui/button"; import { Card, CardContent, CardHeader } from "@/components/ui/card"; import { Separator } from "@/components/ui/separator"; import { getTenantId } from "@/lib/auth"; +import { clickhouse } from "@/lib/clickhouse"; import { and, db, eq, isNull, schema } from "@/lib/db"; import { formatNumber } from "@/lib/fmt"; -import { - getLastUsed, - getLatestVerifications, - getVerificationsDaily, - getVerificationsHourly, -} from "@/lib/tinybird"; import { cn } from "@/lib/utils"; import { BarChart, Minus } from "lucide-react"; import ms from "ms"; import { notFound } from "next/navigation"; import PermissionTree from "./permission-list"; import { VerificationTable } from "./verification-table"; - export default async function APIKeyDetailPage(props: { params: { apiId: string; @@ -89,24 +83,21 @@ export default async function APIKeyDetailPage(props: { const { getVerificationsPerInterval, start, end, granularity } = prepareInterval(interval); const query = { workspaceId: api.workspaceId, - apiId: api.id, + keySpaceId: key.keyAuthId, keyId: key.id, start, end, }; - const [verifications, totalUsage, latestVerifications, lastUsed] = await Promise.all([ + const [verifications, latestVerifications, lastUsed] = await Promise.all([ getVerificationsPerInterval(query), - getVerificationsPerInterval({ - workspaceId: api.workspaceId, - apiId: api.id, - keyId: key.id, - }).then((res) => res.data.at(0) ?? { success: 0, rateLimited: 0, usageExceeded: 0 }), // no interval -> a - getLatestVerifications({ + clickhouse.verifications.logs({ workspaceId: key.workspaceId, - apiId: api.id, + keySpaceId: key.keyAuthId, keyId: key.id, }), - getLastUsed({ keyId: key.id }).then((res) => res.data.at(0)?.lastUsed ?? 0), + clickhouse.verifications + .latest({ workspaceId: key.workspaceId, keySpaceId: key.keyAuthId, keyId: key.id }) + .then((res) => res.at(0)?.time ?? 0), ]); const successOverTime: { x: string; y: number }[] = []; @@ -117,15 +108,31 @@ export default async function APIKeyDetailPage(props: { const expiredOverTime: { x: string; y: number }[] = []; const forbiddenOverTime: { x: string; y: number }[] = []; - for (const d of verifications.data.sort((a, b) => a.time - b.time)) { + for (const d of verifications.sort((a, b) => a.time - b.time)) { const x = new Date(d.time).toISOString(); - successOverTime.push({ x, y: d.success }); - ratelimitedOverTime.push({ x, y: d.rateLimited }); - usageExceededOverTime.push({ x, y: d.usageExceeded }); - disabledOverTime.push({ x, y: d.disabled }); - insufficientPermissionsOverTime.push({ x, y: d.insufficientPermissions }); - expiredOverTime.push({ x, y: d.expired }); - forbiddenOverTime.push({ x, y: d.forbidden }); + switch (d.outcome) { + case "VALID": + successOverTime.push({ x, y: d.count }); + break; + case "RATE_LIMITED": + ratelimitedOverTime.push({ x, y: d.count }); + break; + case "USAGE_EXCEEDED": + usageExceededOverTime.push({ x, y: d.count }); + break; + case "DISABLED": + disabledOverTime.push({ x, y: d.count }); + break; + case "INSUFFICIENT_PERMISSIONS": + insufficientPermissionsOverTime.push({ x, y: d.count }); + break; + case "EXPIRED": + expiredOverTime.push({ x, y: d.count }); + break; + case "FORBIDDEN": + forbiddenOverTime.push({ x, y: d.count }); + break; + } } const verificationsData = [ @@ -157,6 +164,39 @@ export default async function APIKeyDetailPage(props: { } } + const stats = { + valid: 0, + ratelimited: 0, + usageExceeded: 0, + disabled: 0, + insufficientPermissions: 0, + expired: 0, + forbidden: 0, + }; + verifications.forEach((v) => { + switch (v.outcome) { + case "VALID": + stats.valid += v.count; + break; + case "RATE_LIMITED": + stats.ratelimited += v.count; + break; + case "USAGE_EXCEEDED": + stats.usageExceeded += v.count; + break; + case "DISABLED": + stats.disabled += v.count; + break; + case "INSUFFICIENT_PERMISSIONS": + stats.insufficientPermissions += v.count; + break; + case "EXPIRED": + stats.expired += v.count; + break; + case "FORBIDDEN": + stats.forbidden += v.count; + } + }); const roleTee = key.workspace.roles.map((role) => { const nested: NestedPermissions = {}; for (const permission of key.workspace.permissions) { @@ -209,7 +249,7 @@ export default async function APIKeyDetailPage(props: {
- + } @@ -222,9 +262,6 @@ export default async function APIKeyDetailPage(props: { label="Last Used" value={lastUsed ? `${ms(Date.now() - lastUsed)} ago` : } /> - - - @@ -241,48 +278,16 @@ export default async function APIKeyDetailPage(props: {
- sum + day.success, 0), - )} - /> - sum + day.rateLimited, 0), - )} - /> - sum + day.usageExceeded, 0), - )} - /> - sum + day.disabled, 0), - )} - /> + + + + sum + day.insufficientPermissions, 0), - )} - /> - sum + day.expired, 0), - )} - /> - sum + day.forbidden, 0), - )} + value={formatNumber(stats.insufficientPermissions)} /> + +
@@ -311,13 +316,13 @@ export default async function APIKeyDetailPage(props: { )} - {latestVerifications.data.length > 0 ? ( + {latestVerifications.length > 0 ? ( <>

Latest Verifications

- + ) : null} @@ -360,7 +365,7 @@ function prepareInterval(interval: Interval) { end, intervalMs, granularity: 1000 * 60 * 60, - getVerificationsPerInterval: getVerificationsHourly, + getVerificationsPerInterval: clickhouse.verifications.perHour, }; } case "7d": { @@ -372,7 +377,7 @@ function prepareInterval(interval: Interval) { end, intervalMs, granularity: 1000 * 60 * 60 * 24, - getVerificationsPerInterval: getVerificationsDaily, + getVerificationsPerInterval: clickhouse.verifications.perDay, }; } case "30d": { @@ -384,7 +389,7 @@ function prepareInterval(interval: Interval) { end, intervalMs, granularity: 1000 * 60 * 60 * 24, - getVerificationsPerInterval: getVerificationsDaily, + getVerificationsPerInterval: clickhouse.verifications.perDay, }; } case "90d": { @@ -396,7 +401,7 @@ function prepareInterval(interval: Interval) { end, intervalMs, granularity: 1000 * 60 * 60 * 24, - getVerificationsPerInterval: getVerificationsDaily, + getVerificationsPerInterval: clickhouse.verifications.perDay, }; } } diff --git a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/verification-table.tsx b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/verification-table.tsx index d5bb1d19ee..f94e998328 100644 --- a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/verification-table.tsx +++ b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/verification-table.tsx @@ -1,6 +1,5 @@ "use client"; -import { Button } from "@/components/ui/button"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Table, @@ -10,44 +9,27 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; -import type { getLatestVerifications } from "@/lib/tinybird"; +import type { clickhouse } from "@/lib/clickhouse"; import { cn } from "@/lib/utils"; import { format } from "date-fns"; -import { Eye, EyeOff } from "lucide-react"; -import { useState } from "react"; const CELL_CLASS = "py-[2px] text-xs leading-[0.5rem]"; const YELLOW_STATES = ["RATE_LIMITED", "EXPIRED", "USAGE_EXCEEDED"]; const RED_STATES = ["DISABLED", "FORBIDDEN", "INSUFFICIENT_PERMISSIONS"]; -type LatestVerifications = Awaited>["data"]; +type LatestVerifications = Awaited>; type Props = { verifications: LatestVerifications; }; export const VerificationTable = ({ verifications }: Props) => { - const [showIp, setShowIp] = useState(false); - return ( Time - User Agent - - IP Address{" "} - - Region Result @@ -58,17 +40,17 @@ export const VerificationTable = ({ verifications }: Props) => { * Instead of rounding every row individually, we want to round consecutive colored rows together. * For example: * ╭──────╮ - * │ row1 │ + * │ row1 │ * ╰──────╯ * ╭──────╮ - * │ row2 │ + * │ row2 │ * ╰──────╯ * * Becomes this * * ╭──────╮ - * │ row1 │ - * │ row2 │ + * │ row1 │ + * │ row2 │ * ╰──────╯ */ const isStartOfColoredBlock = @@ -80,7 +62,7 @@ export const VerificationTable = ({ verifications }: Props) => { return ( { > {format(verification.time, "MMM dd HH:mm:ss.SS")} - - {verification.userAgent} - - - {showIp - ? verification.ipAddress - : verification.ipAddress.replace(/[a-z0-9]/g, "*")} - {verification.region} `count(*)` }) - .from(schema.keys) - .where(and(eq(schema.keys.keyAuthId, api.keyAuthId!), isNull(schema.keys.deletedAt))) - .execute() - .then((res) => res.at(0)?.count ?? 0), - getVerificationsPerInterval(query), - getActiveKeysPerInterval(query), - getActiveKeys(query), - getActiveKeys({ - workspaceId: api.workspaceId, - apiId: api.id, - start: billingCycleStart, - end: billingCycleEnd, - }).then((res) => res.data.at(0)), - getVerificationsPerInterval({ - workspaceId: api.workspaceId, - apiId: api.id, - start: billingCycleStart, - end: billingCycleEnd, - }), - ]); + const [keys, verifications, activeKeys, activeKeysTotal, verificationsInBillingCycle] = + await Promise.all([ + db + .select({ count: sql`count(*)` }) + .from(schema.keys) + .where(and(eq(schema.keys.keyAuthId, api.keyAuthId!), isNull(schema.keys.deletedAt))) + .execute() + .then((res) => res.at(0)?.count ?? 0), + getVerificationsPerInterval(query), + getActiveKeysPerInterval(query), + clickhouse.activeKeys + .perMonth({ + workspaceId: api.workspaceId, + keySpaceId: api.keyAuthId!, + start: billingCycleStart, + end: billingCycleEnd, + }) + .then((res) => res.at(0)), + getVerificationsPerInterval({ + workspaceId: api.workspaceId, + keySpaceId: api.keyAuthId!, + start: billingCycleStart, + end: billingCycleEnd, + }), + ]); const successOverTime: { x: string; y: number }[] = []; const ratelimitedOverTime: { x: string; y: number }[] = []; @@ -93,15 +81,32 @@ export default async function ApiPage(props: { const expiredOverTime: { x: string; y: number }[] = []; const forbiddenOverTime: { x: string; y: number }[] = []; - for (const d of verifications.data.sort((a, b) => a.time - b.time)) { + for (const d of verifications.sort((a, b) => a.time - b.time)) { const x = new Date(d.time).toISOString(); - successOverTime.push({ x, y: d.success }); - ratelimitedOverTime.push({ x, y: d.rateLimited }); - usageExceededOverTime.push({ x, y: d.usageExceeded }); - disabledOverTime.push({ x, y: d.disabled }); - insufficientPermissionsOverTime.push({ x, y: d.insufficientPermissions }); - expiredOverTime.push({ x, y: d.expired }); - forbiddenOverTime.push({ x, y: d.forbidden }); + + switch (d.outcome) { + case "VALID": + successOverTime.push({ x, y: d.count }); + break; + case "RATE_LIMITED": + ratelimitedOverTime.push({ x, y: d.count }); + break; + case "USAGE_EXCEEDED": + usageExceededOverTime.push({ x, y: d.count }); + break; + case "DISABLED": + disabledOverTime.push({ x, y: d.count }); + break; + case "INSUFFICIENT_PERMISSIONS": + insufficientPermissionsOverTime.push({ x, y: d.count }); + break; + case "EXPIRED": + expiredOverTime.push({ x, y: d.count }); + break; + case "FORBIDDEN": + forbiddenOverTime.push({ x, y: d.count }); + break; + } } const verificationsData = [ @@ -117,7 +122,7 @@ export default async function ApiPage(props: { ...forbiddenOverTime.map((d) => ({ ...d, category: "Forbidden" })), ]; - const activeKeysOverTime = activeKeys.data.map(({ time, keys }) => ({ + const activeKeysOverTime = activeKeys.map(({ time, keys }) => ({ x: new Date(time).toISOString(), y: keys, })); @@ -132,14 +137,14 @@ export default async function ApiPage(props: { month: "long", })}`} value={formatNumber( - verificationsInBillingCycle.data.reduce((sum, day) => sum + day.success, 0), + verificationsInBillingCycle.reduce((sum, day) => sum + day.count, 0), )} /> @@ -159,39 +164,33 @@ export default async function ApiPage(props: {
sum + day.success, 0))} + value={formatNumber(successOverTime.reduce((sum, day) => sum + day.y, 0))} /> sum + day.rateLimited, 0), - )} + value={formatNumber(ratelimitedOverTime.reduce((sum, day) => sum + day.y, 0))} /> sum + day.usageExceeded, 0), - )} + value={formatNumber(usageExceededOverTime.reduce((sum, day) => sum + day.y, 0))} /> sum + day.disabled, 0))} + value={formatNumber(disabledOverTime.reduce((sum, day) => sum + day.y, 0))} /> sum + day.insufficientPermissions, 0), + insufficientPermissionsOverTime.reduce((sum, day) => sum + day.y, 0), )} /> sum + day.expired, 0))} + value={formatNumber(expiredOverTime.reduce((sum, day) => sum + day.y, 0))} /> sum + day.forbidden, 0), - )} + value={formatNumber(forbiddenOverTime.reduce((sum, day) => sum + day.y, 0))} />
@@ -233,10 +232,7 @@ export default async function ApiPage(props: {
- +
@@ -280,8 +276,8 @@ function prepareInterval(interval: Interval) { end, intervalMs, granularity: 1000 * 60 * 60, - getVerificationsPerInterval: getVerificationsHourly, - getActiveKeysPerInterval: getActiveKeysHourly, + getVerificationsPerInterval: clickhouse.verifications.perHour, + getActiveKeysPerInterval: clickhouse.activeKeys.perHour, }; } case "7d": { @@ -293,8 +289,8 @@ function prepareInterval(interval: Interval) { end, intervalMs, granularity: 1000 * 60 * 60 * 24, - getVerificationsPerInterval: getVerificationsDaily, - getActiveKeysPerInterval: getActiveKeysDaily, + getVerificationsPerInterval: clickhouse.verifications.perDay, + getActiveKeysPerInterval: clickhouse.activeKeys.perDay, }; } case "30d": { @@ -306,8 +302,8 @@ function prepareInterval(interval: Interval) { end, intervalMs, granularity: 1000 * 60 * 60 * 24, - getVerificationsPerInterval: getVerificationsDaily, - getActiveKeysPerInterval: getActiveKeysDaily, + getVerificationsPerInterval: clickhouse.verifications.perDay, + getActiveKeysPerInterval: clickhouse.activeKeys.perDay, }; } case "90d": { @@ -319,8 +315,8 @@ function prepareInterval(interval: Interval) { end, intervalMs, granularity: 1000 * 60 * 60 * 24, - getVerificationsPerInterval: getVerificationsDaily, - getActiveKeysPerInterval: getActiveKeysDaily, + getVerificationsPerInterval: clickhouse.verifications.perDay, + getActiveKeysPerInterval: clickhouse.activeKeys.perDay, }; } } diff --git a/apps/dashboard/app/(app)/banner.tsx b/apps/dashboard/app/(app)/banner.tsx index 82648693c4..6a6cbfa2a9 100644 --- a/apps/dashboard/app/(app)/banner.tsx +++ b/apps/dashboard/app/(app)/banner.tsx @@ -1,6 +1,6 @@ import { Banner } from "@/components/banner"; +import { clickhouse } from "@/lib/clickhouse"; import type { Workspace } from "@/lib/db"; -import { verifications } from "@/lib/tinybird"; import { QUOTA } from "@unkey/billing"; import ms from "ms"; import Link from "next/link"; @@ -23,20 +23,18 @@ export const UsageBanner: React.FC<{ workspace: Workspace | undefined }> = async const fmt = new Intl.NumberFormat("en-US").format; if (workspace.plan === "free") { - const [usedVerifications] = await Promise.all([ - verifications({ - workspaceId: workspace.id, - year, - month, - }).then((res) => res.data.at(0)?.success ?? 0), - ]); + const billableVerifications = await clickhouse.billing.billableVerifications({ + workspaceId: workspace.id, + year, + month, + }); - if (usedVerifications >= QUOTA.free.maxVerifications) { + if (billableVerifications >= QUOTA.free.maxVerifications) { return (

You have exceeded your plan's monthly usage limit for verifications:{" "} - {fmt(usedVerifications)} /{" "} + {fmt(billableVerifications)} /{" "} {fmt(QUOTA.free.maxVerifications)}.{" "} Upgrade your plan diff --git a/apps/dashboard/app/(app)/identities/[identityId]/page.tsx b/apps/dashboard/app/(app)/identities/[identityId]/page.tsx index d3ef062f05..ecd5a90ed9 100644 --- a/apps/dashboard/app/(app)/identities/[identityId]/page.tsx +++ b/apps/dashboard/app/(app)/identities/[identityId]/page.tsx @@ -14,8 +14,8 @@ import { TableRow, } from "@/components/ui/table"; import { getTenantId } from "@/lib/auth"; +import { clickhouse } from "@/lib/clickhouse"; import { db } from "@/lib/db"; -import { getLastUsed } from "@/lib/tinybird"; import { ChevronRight, Minus } from "lucide-react"; import ms from "ms"; import Link from "next/link"; @@ -142,7 +142,7 @@ export default async function Page(props: Props) { )} - +

{verifications.length === 0 ? ( @@ -34,20 +28,6 @@ export const AccessTable: React.FC = ({ verifications }) => { Time - Resource - User Agent - - IP Address{" "} - - Region Valid @@ -62,11 +42,6 @@ export const AccessTable: React.FC = ({ verifications }) => { {new Date(verification.time).toTimeString().split("(").at(0)} - {verification.requestedResource} - {verification.userAgent} - - {showIp ? verification.ipAddress : verification.ipAddress.replace(/[a-z0-9]/g, "*")} - {verification.region} {verification.outcome === "VALID" ? : {verification.outcome}} diff --git a/apps/dashboard/app/(app)/settings/root-keys/[keyId]/history/page.tsx b/apps/dashboard/app/(app)/settings/root-keys/[keyId]/history/page.tsx index dd8b57b62f..53c6b6cbe8 100644 --- a/apps/dashboard/app/(app)/settings/root-keys/[keyId]/history/page.tsx +++ b/apps/dashboard/app/(app)/settings/root-keys/[keyId]/history/page.tsx @@ -1,7 +1,7 @@ import { getTenantId } from "@/lib/auth"; +import { clickhouse } from "@/lib/clickhouse"; import { db } from "@/lib/db"; import { env } from "@/lib/env"; -import { getLatestVerifications } from "@/lib/tinybird"; import { notFound } from "next/navigation"; import { AccessTable } from "./access-table"; @@ -18,7 +18,7 @@ export default async function HistoryPage(props: { if (!workspace) { return notFound(); } - const { UNKEY_WORKSPACE_ID, UNKEY_API_ID } = env(); + const { UNKEY_WORKSPACE_ID } = env(); const key = await db.query.keys.findFirst({ where: (table, { and, eq, isNull }) => @@ -39,11 +39,11 @@ export default async function HistoryPage(props: { if (!key?.keyAuth?.api) { return notFound(); } - const history = await getLatestVerifications({ + const history = await clickhouse.verifications.logs({ workspaceId: UNKEY_WORKSPACE_ID, - apiId: UNKEY_API_ID, + keySpaceId: key.keyAuthId, keyId: key.id, }); - return ; + return ; } diff --git a/apps/dashboard/app/(app)/settings/root-keys/[keyId]/layout.tsx b/apps/dashboard/app/(app)/settings/root-keys/[keyId]/layout.tsx index f47a085ccf..b950ff4b55 100644 --- a/apps/dashboard/app/(app)/settings/root-keys/[keyId]/layout.tsx +++ b/apps/dashboard/app/(app)/settings/root-keys/[keyId]/layout.tsx @@ -1,7 +1,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { getTenantId } from "@/lib/auth"; +import { clickhouse } from "@/lib/clickhouse"; import { db } from "@/lib/db"; -import { getLastUsed } from "@/lib/tinybird"; import { ArrowLeft } from "lucide-react"; import ms from "ms"; import Link from "next/link"; @@ -55,7 +55,7 @@ export default async function Layout({ children, params: { keyId } }: Props) { /> x}> - + @@ -77,8 +77,14 @@ const Metric: React.FC<{ ); }; -const LastUsed: React.FC<{ keyId: string }> = async ({ keyId }) => { - const lastUsed = await getLastUsed({ keyId }).then((res) => res.data.at(0)?.lastUsed ?? 0); +const LastUsed: React.FC<{ workspaceId: string; keySpaceId: string; keyId: string }> = async ({ + workspaceId, + keySpaceId, + keyId, +}) => { + const lastUsed = await clickhouse.verifications + .latest({ workspaceId, keySpaceId, keyId }) + .then((res) => res.at(0)?.time ?? 0); return ( diff --git a/apps/dashboard/app/(app)/settings/root-keys/[keyId]/page.tsx b/apps/dashboard/app/(app)/settings/root-keys/[keyId]/page.tsx index bcc22ea3f9..24b3426d37 100644 --- a/apps/dashboard/app/(app)/settings/root-keys/[keyId]/page.tsx +++ b/apps/dashboard/app/(app)/settings/root-keys/[keyId]/page.tsx @@ -2,9 +2,9 @@ import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { DialogTrigger } from "@/components/ui/dialog"; import { getTenantId } from "@/lib/auth"; +import { clickhouse } from "@/lib/clickhouse"; import { type Permission, db, eq, schema } from "@/lib/db"; import { env } from "@/lib/env"; -import { getLatestVerifications } from "@/lib/tinybird"; import { notFound } from "next/navigation"; import { AccessTable } from "./history/access-table"; import { DialogAddPermissionsForAPI } from "./permissions/add-permission-for-api"; @@ -70,7 +70,7 @@ export default async function RootKeyPage(props: { {} as { [apiId: string]: Permission[] }, ); - const { UNKEY_WORKSPACE_ID, UNKEY_API_ID } = env(); + const { UNKEY_WORKSPACE_ID } = env(); const keyForHistory = await db.query.keys.findFirst({ where: (table, { and, eq, isNull }) => @@ -91,9 +91,9 @@ export default async function RootKeyPage(props: { if (!keyForHistory?.keyAuth?.api) { return notFound(); } - const history = await getLatestVerifications({ + const history = await clickhouse.verifications.latest({ workspaceId: UNKEY_WORKSPACE_ID, - apiId: UNKEY_API_ID, + keySpaceId: key.keyAuthId, keyId: key.id, }); @@ -152,7 +152,7 @@ export default async function RootKeyPage(props: { diff --git a/apps/dashboard/app/(app)/success/page.tsx b/apps/dashboard/app/(app)/success/page.tsx index f8c049b4ea..fdbac2827e 100644 --- a/apps/dashboard/app/(app)/success/page.tsx +++ b/apps/dashboard/app/(app)/success/page.tsx @@ -5,9 +5,9 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com import { Progress } from "@/components/ui/progress"; import { Separator } from "@/components/ui/separator"; import { getTenantId } from "@/lib/auth"; +import { clickhouse } from "@/lib/clickhouse"; import { and, count, db, gte, isNotNull, schema, sql } from "@/lib/db"; import { stripeEnv } from "@/lib/env"; -import { getMonthlyActiveWorkspaces } from "@/lib/tinybird"; import { notFound } from "next/navigation"; import { Suspense } from "react"; import Stripe from "stripe"; @@ -64,8 +64,8 @@ export default async function SuccessPage() { }); } - const activeWorkspaces = await getMonthlyActiveWorkspaces({}); - const chartData = activeWorkspaces.data.map(({ time, workspaces }) => ({ + const activeWorkspaces = await clickhouse.business.activeWorkspaces(); + const chartData = activeWorkspaces.map(({ time, workspaces }) => ({ x: new Date(time).toLocaleDateString(), y: workspaces, })); @@ -118,7 +118,7 @@ export default async function SuccessPage() { {Object.entries(tables).map(([title, table]) => ( - }> + } key={title}> void; }; -export const ArrayInput: React.FC = ({ title, placeholder, selected = [], setSelected }) => { +export const ArrayInput: React.FC = ({ title, placeholder, selected, setSelected }) => { const inputRef = React.useRef(null); const [inputValue, setInputValue] = React.useState(""); @@ -41,7 +41,7 @@ export const ArrayInput: React.FC = ({ title, placeholder, selected = [], } } }, - [selected], + [selected, setSelected], ); return ( diff --git a/apps/dashboard/lib/analytics.tsx b/apps/dashboard/lib/analytics.tsx deleted file mode 100644 index 344368bd4b..0000000000 --- a/apps/dashboard/lib/analytics.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { env } from "@/lib/env"; -import { NoopTinybird, Tinybird } from "@chronark/zod-bird"; -import type { NextRequest } from "next/server"; -import { z } from "zod"; - -const token = env().TINYBIRD_TOKEN; -const tb = token ? new Tinybird({ token }) : new NoopTinybird(); - -export const ingestPageView = tb.buildIngestEndpoint({ - datasource: "pageviews__v1", - event: z.object({ - sessionId: z.string(), - path: z.string(), - time: z.number().int(), - userId: z.string().optional(), - tenantId: z.string().optional(), - region: z.string().optional(), - country: z.string().optional(), - city: z.string().optional(), - userAgent: z.string().optional(), - referrer: z.string().optional(), - }), -}); - -/** - * Collects page view analytics - * This function can not fail and will not throw. Instead errors are logged to the console. - */ -export async function collectPageViewAnalytics(args: { - req: NextRequest; - userId?: string; - tenantId?: string; -}): Promise { - try { - const host = args.req.nextUrl.host; - if (host.startsWith("localhost") || host.startsWith("127.0.0.1")) { - // console.debug(`not collecting analytics for ${host}`); - return; - } - - const ip = args.req.ip; - if (!ip) { - console.debug("not collecting analytics for unknown ip"); - return; - } - - const now = new Date(); - const sessionId = toBase64( - await crypto.subtle.digest( - "sha-256", - new TextEncoder().encode( - [ip, now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()].join("-"), - ), - ), - ); - - // replace ids to make aggregations easier - const path = args.req.nextUrl.pathname - .replace(/\/(api_[a-zA-Z0-9]+)/g, "[apiId]") - .replace(/\/(key_[a-zA-Z0-9]+)/g, "[keyId]"); - - await ingestPageView({ - time: now.getTime(), - sessionId, - userId: args.userId, - tenantId: args.tenantId, - path, - region: args.req.geo?.region, - country: args.req.geo?.country, - city: args.req.geo?.city, - userAgent: args.req.headers.get("User-Agent") ?? undefined, - referrer: args.req.headers.get("Referer") ?? undefined, - }); - } catch (e) { - console.error("error collecting analytics", e); - } -} - -function toBase64(buffer: ArrayBuffer): string { - let binary = ""; - const bytes = new Uint8Array(buffer); - const len = bytes.byteLength; - for (let i = 0; i < len; i++) { - binary += String.fromCharCode(bytes[i]); - } - return btoa(binary); -} diff --git a/apps/dashboard/lib/charts/sparkline.tsx b/apps/dashboard/lib/charts/sparkline.tsx index d76e895097..e4a96bc460 100644 --- a/apps/dashboard/lib/charts/sparkline.tsx +++ b/apps/dashboard/lib/charts/sparkline.tsx @@ -61,7 +61,7 @@ function SparkLineInner({ startDate: dates[times.indexOf(Math.min(...times))], endDate: dates[times.indexOf(Math.max(...times))], }; - }, data); + }, [data]); const { minY, maxY } = useMemo(() => { const values = series @@ -73,7 +73,7 @@ function SparkLineInner({ minY: Math.min(...values), maxY: Math.max(...values), }; - }, [data, series, padding?.bottom, padding?.top]); + }, [data, series]); const { yScale, xScale } = useMemo(() => { const rangeY = maxY - minY; @@ -89,7 +89,7 @@ function SparkLineInner({ range: [0, width], }), }; - }, [startDate, endDate, minY, maxY, height, width, margin]); + }, [startDate, endDate, minY, maxY, height, width]); const chartContext: ChartContextType = { width, diff --git a/apps/dashboard/lib/clickhouse.ts b/apps/dashboard/lib/clickhouse.ts new file mode 100644 index 0000000000..aae86e7b04 --- /dev/null +++ b/apps/dashboard/lib/clickhouse.ts @@ -0,0 +1,4 @@ +import { ClickHouse } from "@unkey/clickhouse"; +import { env } from "./env"; + +export const clickhouse = new ClickHouse({ url: env().CLICKHOUSE_URL }); diff --git a/apps/dashboard/lib/clickhouse/index.ts b/apps/dashboard/lib/clickhouse/index.ts deleted file mode 100644 index 77b44b606b..0000000000 --- a/apps/dashboard/lib/clickhouse/index.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { type Clickhouse, Client, Noop } from "@unkey/clickhouse-zod"; -import { z } from "zod"; -import { env } from "../env"; - -// dummy example of how to query stuff from clickhouse -export async function getLogs(args: { workspaceId: string; limit: number }) { - const { CLICKHOUSE_URL } = env(); - - const ch: Clickhouse = CLICKHOUSE_URL ? new Client({ url: CLICKHOUSE_URL }) : new Noop(); - const query = ch.query({ - query: ` - SELECT - request_id, - time, - workspace_id, - host, - method, - path, - request_headers, - request_body, - response_status, - response_headers, - response_body, - error, - service_latency - FROM default.raw_api_requests_v1 - WHERE workspace_id = {workspaceId: String} - ORDER BY time DESC - LIMIT {limit: Int}`, - params: z.object({ - workspaceId: z.string(), - limit: z.number().int(), - }), - schema: z.object({ - request_id: z.string(), - time: z.number().int(), - workspace_id: z.string(), - host: z.string(), - method: z.string(), - path: z.string(), - request_headers: z.array(z.string()), - request_body: z.string(), - response_status: z.number().int(), - response_headers: z.array(z.string()), - response_body: z.string(), - error: z.string(), - service_latency: z.number().int(), - }), - }); - - return query(args); -} diff --git a/apps/dashboard/lib/tinybird.ts b/apps/dashboard/lib/tinybird.ts index 37c561a47c..30a2295267 100644 --- a/apps/dashboard/lib/tinybird.ts +++ b/apps/dashboard/lib/tinybird.ts @@ -14,149 +14,7 @@ const datetimeToUnixMilli = z.string().transform((t) => new Date(t).getTime()); * If we transform it as is, we get `1609459200000` which is `2021-01-01 01:00:00` due to fun timezone stuff. * So we split the string at the space and take the date part, and then parse that. */ -const dateToUnixMilli = z.string().transform((t) => new Date(t.split(" ").at(0) ?? t).getTime()); - -export const getActiveCountPerApiPerDay = tb.buildPipe({ - pipe: "endpoint_get_active_keys__v2", - parameters: z.object({ - workspaceId: z.string(), - apiId: z.string().optional(), - start: z.number(), - end: z.number(), - }), - data: z.object({ - active: z.number(), - }), -}); - -export const getTotalVerificationsForWorkspace = tb.buildPipe({ - pipe: "endpoint_billing_get_verifications_usage__v1", - parameters: z.object({ - workspaceId: z.string(), - start: z.number(), - end: z.number(), - }), - data: z.object({ usage: z.number() }), - opts: { - cache: "no-store", - }, -}); - -export const getTotalActiveKeys = tb.buildPipe({ - pipe: "endpoint_billing_get_active_keys_usage__v2", - parameters: z.object({ - workspaceId: z.string(), - apiId: z.string().optional(), - start: z.number(), - end: z.number(), - }), - data: z.object({ usage: z.number() }), - opts: { - cache: "no-store", - }, -}); - -export const getTotalVerifications = tb.buildPipe({ - pipe: "endpoint__all_verifications__v1", - data: z.object({ verifications: z.number() }), - opts: { - cache: "no-store", - }, -}); - -export const getLatestVerifications = tb.buildPipe({ - pipe: "endpoint__get_latest_verifications__v3", - parameters: z.object({ - workspaceId: z.string(), - apiId: z.string(), - keyId: z.string(), - }), - data: z.object({ - time: z.number(), - requestedResource: z.string(), - outcome: z.string(), - region: z.string(), - userAgent: z.string(), - ipAddress: z.string(), - requestBody: z.string().optional(), - responseBody: z.string().optional(), - }), - opts: { - cache: "no-store", - }, -}); - -export const getLastUsed = tb.buildPipe({ - pipe: "endpoint__get_last_used__v1", - parameters: z.object({ - keyId: z.string(), - }), - data: z.object({ - lastUsed: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - -export const getActiveKeysPerHourForAllWorkspaces = tb.buildPipe({ - pipe: "endpoint_billing_get_active_keys_per_workspace_per_hour__v2__v1", - - data: z.object({ - usage: z.number(), - workspaceId: z.string(), - time: datetimeToUnixMilli, - }), - opts: { - cache: "no-store", - }, -}); - -export const getVerificationsPerHourForAllWorkspaces = tb.buildPipe({ - pipe: "endpoint__billing_verifications_per_hour__v1", - - data: z.object({ - verifications: z.number(), - workspaceId: z.string(), - time: datetimeToUnixMilli, - }), - opts: { - cache: "no-store", - }, -}); - -export const activeKeys = tb.buildPipe({ - pipe: "endpoint__active_keys_by_workspace__v1", - parameters: z.object({ - workspaceId: z.string(), - year: z.number().int(), - month: z.number().int().min(1).max(12), - }), - data: z.object({ - keys: z.number().int().nullable().default(0), - }), - opts: { - cache: "no-store", - }, -}); - -export const verifications = tb.buildPipe({ - pipe: "endpoint__verifications_by_workspace__v1", - parameters: z.object({ - workspaceId: z.string(), - year: z.number().int(), - month: z.number().int().min(1).max(12), - }), - - data: z.object({ - success: z.number().int().nullable().default(0), - ratelimited: z.number().int().nullable().default(0), - usageExceeded: z.number().int().nullable().default(0), - }), - opts: { - cache: "no-store", - }, -}); +const _dateToUnixMilli = z.string().transform((t) => new Date(t.split(" ").at(0) ?? t).getTime()); export const ratelimits = tb.buildPipe({ pipe: "endpoint__ratelimits_by_workspace__v1", @@ -175,229 +33,6 @@ export const ratelimits = tb.buildPipe({ }, }); -export const getVerificationsMonthly = tb.buildPipe({ - pipe: "get_verifications_monthly__v2", - parameters: z.object({ - workspaceId: z.string(), - apiId: z.string(), - keyId: z.string().optional(), - start: z.number().optional(), - end: z.number().optional(), - }), - data: z.object({ - time: dateToUnixMilli, - success: z.number(), - rateLimited: z.number(), - usageExceeded: z.number(), - disabled: z.number(), - insufficientPermissions: z.number(), - forbidden: z.number(), - expired: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - -export const getVerificationsWeekly = tb.buildPipe({ - pipe: "get_verifications_weekly__v2", - parameters: z.object({ - workspaceId: z.string(), - apiId: z.string(), - keyId: z.string().optional(), - start: z.number().optional(), - end: z.number().optional(), - }), - data: z.object({ - time: dateToUnixMilli, - success: z.number(), - rateLimited: z.number(), - usageExceeded: z.number(), - disabled: z.number(), - insufficientPermissions: z.number(), - forbidden: z.number(), - expired: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - -export const getVerificationsDaily = tb.buildPipe({ - pipe: "get_verifications_daily__v2", - parameters: z.object({ - workspaceId: z.string(), - apiId: z.string(), - keyId: z.string().optional(), - start: z.number().optional(), - end: z.number().optional(), - }), - data: z.object({ - time: dateToUnixMilli, - success: z.number(), - rateLimited: z.number(), - usageExceeded: z.number(), - disabled: z.number(), - insufficientPermissions: z.number(), - forbidden: z.number(), - expired: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - -export const getVerificationsHourly = tb.buildPipe({ - pipe: "get_verifications_hourly__v2", - parameters: z.object({ - workspaceId: z.string(), - apiId: z.string(), - keyId: z.string().optional(), - start: z.number().optional(), - end: z.number().optional(), - }), - data: z.object({ - time: datetimeToUnixMilli, - success: z.number(), - rateLimited: z.number(), - usageExceeded: z.number(), - disabled: z.number(), - insufficientPermissions: z.number(), - forbidden: z.number(), - expired: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - -export const getActiveKeysHourly = tb.buildPipe({ - pipe: "get_active_keys_hourly__v1", - parameters: z.object({ - workspaceId: z.string(), - apiId: z.string(), - start: z.number().optional(), - end: z.number().optional(), - }), - data: z.object({ - time: datetimeToUnixMilli, - keys: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - -export const getActiveKeysDaily = tb.buildPipe({ - pipe: "get_active_keys_daily__v1", - parameters: z.object({ - workspaceId: z.string(), - apiId: z.string(), - start: z.number().optional(), - end: z.number().optional(), - }), - data: z.object({ - time: dateToUnixMilli, - keys: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - -export const getActiveKeysWeekly = tb.buildPipe({ - pipe: "get_active_keys_weekly__v1", - parameters: z.object({ - workspaceId: z.string(), - apiId: z.string(), - start: z.number().optional(), - end: z.number().optional(), - }), - data: z.object({ - time: dateToUnixMilli, - keys: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - -export const getActiveKeysMonthly = tb.buildPipe({ - pipe: "get_active_keys_monthly__v1", - parameters: z.object({ - workspaceId: z.string(), - apiId: z.string(), - start: z.number().optional(), - end: z.number().optional(), - }), - data: z.object({ - time: dateToUnixMilli, - keys: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - -/** - * Across the entire time period - */ -export const getActiveKeys = tb.buildPipe({ - pipe: "get_active_keys__v1", - parameters: z.object({ - workspaceId: z.string(), - apiId: z.string(), - start: z.number().optional(), - end: z.number().optional(), - }), - data: z.object({ - keys: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - -export const getQ1ActiveWorkspaces = tb.buildPipe({ - pipe: "get_q1_goal_distinct_workspaces__v1", - parameters: z.object({}), - data: z.object({ - workspaces: z.number(), - time: datetimeToUnixMilli, - }), - opts: { - cache: "no-store", - }, -}); - -export const getMonthlyActiveWorkspaces = tb.buildPipe({ - pipe: "monthly_active_workspaces__v1", - parameters: z.object({}), - data: z.object({ - workspaces: z.number(), - time: datetimeToUnixMilli, - }), - opts: { - cache: "no-store", - }, -}); - -export const getAuditLogActors = tb.buildPipe({ - pipe: "endpoint__audit_log_actor_ids__v1", - parameters: z.object({ - workspaceId: z.string(), - type: z.enum(["user", "key"]).optional(), - }), - data: z.object({ - actorId: z.string(), - actorType: z.enum(["user", "key"]).optional(), - lastSeen: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - export const auditLogsDataSchema = z .object({ workspaceId: z.string(), @@ -445,23 +80,6 @@ export const auditLogsDataSchema = z }, })); -export const getAuditLogs = tb.buildPipe({ - pipe: "endpoint__audit_logs__v1", - parameters: z.object({ - workspaceId: z.string(), - bucket: z.string().default("unkey_mutations"), - before: z.number().int().optional(), - after: z.number().int(), - events: z.array(z.string()).optional(), - actorIds: z.array(z.string()).optional(), - }), - - data: auditLogsDataSchema, - opts: { - cache: "no-store", - }, -}); - export type UnkeyAuditLog = { workspaceId: string; event: z.infer; @@ -500,208 +118,6 @@ export type UnkeyAuditLog = { location: string; }; }; - -export const getRatelimitsHourly = tb.buildPipe({ - pipe: "get_ratelimits_hourly__v1", - parameters: z.object({ - workspaceId: z.string(), - namespaceId: z.string(), - identifier: z.array(z.string()).optional(), - start: z.number().optional(), - end: z.number().optional(), - }), - data: z.object({ - time: datetimeToUnixMilli, - success: z.number(), - total: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - -export const getRatelimitsMinutely = tb.buildPipe({ - pipe: "get_ratelimits_minutely__v1", - parameters: z.object({ - workspaceId: z.string(), - namespaceId: z.string(), - identifier: z.array(z.string()).optional(), - start: z.number().optional(), - end: z.number().optional(), - }), - data: z.object({ - time: datetimeToUnixMilli, - success: z.number(), - total: z.number(), - }), - opts: { - cache: "no-store", - }, -}); -export const getRatelimitsDaily = tb.buildPipe({ - pipe: "get_ratelimits_daily__v1", - parameters: z.object({ - workspaceId: z.string(), - namespaceId: z.string(), - identifier: z.array(z.string()).optional(), - start: z.number().optional(), - end: z.number().optional(), - }), - data: z.object({ - time: dateToUnixMilli, - success: z.number(), - total: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - -export const getRatelimitsMonthly = tb.buildPipe({ - pipe: "get_ratelimits_monthly__v1", - parameters: z.object({ - workspaceId: z.string(), - namespaceId: z.string(), - identifier: z.array(z.string()).optional(), - start: z.number().optional(), - end: z.number().optional(), - }), - data: z.object({ - time: dateToUnixMilli, - success: z.number(), - total: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - -export const getRatelimitIdentifiersMinutely = tb.buildPipe({ - pipe: "get_ratelimit_identifiers_minutely__v1", - parameters: z.object({ - workspaceId: z.string(), - namespaceId: z.string(), - start: z.number(), - end: z.number(), - orderBy: z.enum(["success", "total"]).optional().default("total"), - }), - data: z.object({ - identifier: z.string(), - success: z.number(), - total: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - -export const getRatelimitIdentifiersHourly = tb.buildPipe({ - pipe: "get_ratelimit_identifiers_hourly__v1", - parameters: z.object({ - workspaceId: z.string(), - namespaceId: z.string(), - start: z.number(), - end: z.number(), - orderBy: z.enum(["success", "total"]).optional().default("total"), - }), - data: z.object({ - identifier: z.string(), - success: z.number(), - total: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - -export const getRatelimitIdentifiersDaily = tb.buildPipe({ - pipe: "get_ratelimit_identifiers_daily__v1", - parameters: z.object({ - workspaceId: z.string(), - namespaceId: z.string(), - start: z.number(), - end: z.number(), - orderBy: z.enum(["success", "total"]).optional().default("total"), - }), - data: z.object({ - identifier: z.string(), - success: z.number(), - total: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - -export const getRatelimitIdentifiersMonthly = tb.buildPipe({ - pipe: "get_ratelimit_identifiers_monthly__v1", - parameters: z.object({ - workspaceId: z.string(), - namespaceId: z.string(), - start: z.number(), - end: z.number(), - orderBy: z.enum(["success", "total"]).optional().default("total"), - }), - data: z.object({ - identifier: z.string(), - success: z.number(), - total: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - -export const getRatelimitLastUsed = tb.buildPipe({ - pipe: "get_ratelimits_last_used__v1", - parameters: z.object({ - workspaceId: z.string(), - namespaceId: z.string(), - identifier: z.array(z.string()).optional(), - }), - data: z.object({ - lastUsed: z.number(), - }), - opts: { - cache: "no-store", - }, -}); - -export const getRatelimitEvents = tb.buildPipe({ - pipe: "get_ratelimit_events__v1", - parameters: z.object({ - workspaceId: z.string(), - namespaceId: z.string(), - after: z.number().optional(), - before: z.number().optional(), - limit: z.number().optional(), - success: z - .boolean() - .optional() - .transform((b) => (typeof b === "boolean" ? (b ? 1 : 0) : undefined)), - ipAddress: z.array(z.string()).optional(), - country: z.array(z.string()).optional(), - identifier: z.array(z.string()).optional(), - }), - data: z.object({ - identifier: z.string(), - requestId: z.string(), - time: z.number(), - success: z - .number() - .transform((n) => n > 0) - .optional(), - - remaining: z.number(), - limit: z.number(), - country: z.string(), - ipAddress: z.string(), - }), - opts: { - cache: "no-store", - }, -}); - export const getAllSemanticCacheLogs = tb.buildPipe({ pipe: "get_all_semantic_cache_logs__v1", parameters: z.object({ @@ -786,20 +202,3 @@ export const getSemanticCachesHourly = tb.buildPipe({ cache: "no-store", }, }); - -// public get getVerificationsByOwnerId() { -// return this.client.buildPipe({ -// pipe: "get_verifictions_by_keySpaceId__v1", -// parameters: z.object({ -// workspaceId: z.string(), -// keySpaceId: z.string(), -// start: z.number(), -// end: z.number(), -// }), -// data: z.object({ -// ownerId: z.string(), -// verifications: z.number(), -// }), -// }); -// } -// } diff --git a/apps/dashboard/middleware.ts b/apps/dashboard/middleware.ts index 1cdd3f7c0c..60f483a49d 100644 --- a/apps/dashboard/middleware.ts +++ b/apps/dashboard/middleware.ts @@ -1,4 +1,3 @@ -import { collectPageViewAnalytics } from "@/lib/analytics"; import { db } from "@/lib/db"; import { authMiddleware, clerkClient } from "@clerk/nextjs"; import { redirectToSignIn } from "@clerk/nextjs"; @@ -12,8 +11,6 @@ const findWorkspace = async ({ tenantId }: { tenantId: string }) => { }; export default async function (req: NextRequest, evt: NextFetchEvent) { - let userId: string | undefined = undefined; - let tenantId: string | undefined = undefined; const privateMatch = "^/"; console.debug(req.url); const res = await authMiddleware({ @@ -22,8 +19,6 @@ export default async function (req: NextRequest, evt: NextFetchEvent) { if (!auth.userId && privateMatch.match(req.nextUrl.pathname)) { return redirectToSignIn({ returnBackUrl: req.url }); } - userId = auth.userId ?? undefined; - tenantId = auth.orgId ?? auth.userId ?? undefined; if (auth.orgId && privateMatch.match(req.nextUrl.pathname)) { const workspace = await findWorkspace({ tenantId: auth.orgId }); if (!workspace && req.nextUrl.pathname !== "/new") { @@ -49,8 +44,6 @@ export default async function (req: NextRequest, evt: NextFetchEvent) { }, })(req, evt); - evt.waitUntil(collectPageViewAnalytics({ req, userId, tenantId })); - return res; } diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index c7ee132fcc..6b1861334a 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -42,7 +42,7 @@ "@trpc/react-query": "^10.45.2", "@trpc/server": "^10.45.2", "@unkey/billing": "workspace:^", - "@unkey/clickhouse-zod": "workspace:^", + "@unkey/clickhouse": "workspace:^", "@unkey/db": "workspace:^", "@unkey/encryption": "workspace:^", "@unkey/error": "workspace:^", diff --git a/apps/play/app/page-bk.tsx b/apps/play/app/page-bk.tsx index 5c510e33d3..94d2fc6b2d 100644 --- a/apps/play/app/page-bk.tsx +++ b/apps/play/app/page-bk.tsx @@ -21,7 +21,7 @@ export default function Home() { useEffect(() => { scrollRef?.current?.scrollIntoView({ behavior: "smooth" }); - }, [historyItems, scrollRef]); + }, [scrollRef]); const parseCurlCommand = useCallback( (stepString: string) => { diff --git a/apps/www/.env.example b/apps/www/.env.example index 97f64fe508..256d591eb4 100644 --- a/apps/www/.env.example +++ b/apps/www/.env.example @@ -1,5 +1,4 @@ -TINYBIRD_TOKEN= DATABASE_HOST= DATABASE_USERNAME= DATABASE_PASSWORD= - \ No newline at end of file + diff --git a/apps/www/components/stats.tsx b/apps/www/components/stats.tsx index c900143730..7022937f1e 100644 --- a/apps/www/components/stats.tsx +++ b/apps/www/components/stats.tsx @@ -1,10 +1,9 @@ import { Container } from "@/components/container"; import { StatList, StatListItem } from "@/components/stat-list"; import { db, schema, sql } from "@/lib/db"; -import { getTotalVerifications } from "@/lib/tinybird"; import { FadeInStagger } from "./fade-in"; -const [workspaces, apis, keys, totalVerifications] = await Promise.all([ +const [workspaces, apis, keys] = await Promise.all([ db .select({ count: sql`count(*)` }) .from(schema.workspaces) @@ -29,15 +28,6 @@ const [workspaces, apis, keys, totalVerifications] = await Promise.all([ console.error(err); return 0; }), - getTotalVerifications({}) - .then((res) => { - return res.data.reduce((acc, curr) => acc + curr.verifications, 0); - }) - .catch((err) => { - console.error(err); - return 0; - }), - { next: { revalidate: 3600 } }, ]); export function Stats() { @@ -47,7 +37,6 @@ export function Stats() { - diff --git a/apps/www/lib/env.ts b/apps/www/lib/env.ts index ee7ed88eb0..dd07832cde 100644 --- a/apps/www/lib/env.ts +++ b/apps/www/lib/env.ts @@ -3,7 +3,6 @@ import { z } from "zod"; export const env = () => z .object({ - TINYBIRD_TOKEN: z.string().optional(), NEXT_PUBLIC_BASE_URL: z.string().url().default("https://unkey.com"), }) .parse(process.env); diff --git a/apps/www/lib/tinybird.ts b/apps/www/lib/tinybird.ts deleted file mode 100644 index 94fcf37b69..0000000000 --- a/apps/www/lib/tinybird.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { NoopTinybird, Tinybird } from "@chronark/zod-bird"; -import { z } from "zod"; -import { env } from "./env"; - -const token = env().TINYBIRD_TOKEN; -const tb = token ? new Tinybird({ token }) : new NoopTinybird(); - -export const getTotalVerifications = tb.buildPipe({ - pipe: "endpoint__all_verifications__v1", - data: z.object({ verifications: z.number() }), -}); diff --git a/deployment/grafana/grafana.yaml b/deployment/grafana/grafana.yaml index 8224f01fc1..d442154352 100644 --- a/deployment/grafana/grafana.yaml +++ b/deployment/grafana/grafana.yaml @@ -14,3 +14,15 @@ datasources: isDefault: true access: proxy editable: true +- name: ClickHouse + type: grafana-clickhouse-datasource + jsonData: + defaultDatabase: database + port: 9000 + host: clickhouse + username: default + isDefault: true + editable: true + tlsSkipVerify: false + secureJsonData: + password: password diff --git a/internal/clickhouse-zod/package.json b/internal/clickhouse/package.json similarity index 90% rename from internal/clickhouse-zod/package.json rename to internal/clickhouse/package.json index 19e5ea3f59..75c7133945 100644 --- a/internal/clickhouse-zod/package.json +++ b/internal/clickhouse/package.json @@ -1,5 +1,5 @@ { - "name": "@unkey/clickhouse-zod", + "name": "@unkey/clickhouse", "version": "1.0.0", "description": "", "main": "./src/index.ts", diff --git a/apps/agent/pkg/clickhouse/schema/000_README.md b/internal/clickhouse/schema/000_README.md similarity index 87% rename from apps/agent/pkg/clickhouse/schema/000_README.md rename to internal/clickhouse/schema/000_README.md index 95f3f26825..b1d380abf9 100644 --- a/apps/agent/pkg/clickhouse/schema/000_README.md +++ b/internal/clickhouse/schema/000_README.md @@ -16,7 +16,6 @@ Format: `[prefix]_[domain]_[description]_[version]` ### Prefixes - `raw_`: Input data tables -- `mv_`: Materialized views - `tmp_{yourname}_`: Temporary tables for experiments, add your name, so it's easy to identify ownership. ### Domain/Category @@ -42,9 +41,9 @@ For aggregated or summary tables, use suffixes like: ## Materialized View Naming Convention -Format: `mv_[description]_[aggregation]` +Format: `[description]_[aggregation]_mv_[version]` -- Always prefix with `mv_` +- Always suffix with `mv_[version]` - Include a description of the view's purpose - Add aggregation level if applicable @@ -54,19 +53,20 @@ Format: `mv_[description]_[aggregation]` `raw_sales_transactions_v1` 2. Materialized View: - `mv_active_users_per_day_v2` + `active_users_per_day_mv_v2` 3. Temporary Table: `tmp_andreas_user_analysis_v1` 4. Aggregated Table: - `mv_sales_summary_per_hour_v1` + `sales_summary_per_hour_mv_v1` ## Consistency Across Related Objects Maintain consistent naming across related tables, views, and other objects: - `raw_user_activity_v1` -- `mv_user_activity_per_day_v1` +- `user_activity_per_day_v1` +- `user_activity_per_day_mv_v1` By following these conventions, we ensure a clear, consistent, and scalable naming structure for our ClickHouse setup. diff --git a/internal/clickhouse/schema/001_create_databases.sql b/internal/clickhouse/schema/001_create_databases.sql new file mode 100644 index 0000000000..c8b10934e7 --- /dev/null +++ b/internal/clickhouse/schema/001_create_databases.sql @@ -0,0 +1,17 @@ +-- +goose up + +CREATE DATABASE verifications; +CREATE DATABASE telemetry; +CREATE DATABASE metrics; +CREATE DATABASE ratelimits; +CREATE DATABASE business; +CREATE DATABASE billing; + + +-- +goose down +DROP DATABASE verifications; +DROP DATABASE telemetry; +DROP DATABASE metrics; +DROP DATABASE ratelimits; +DROP DATABASE business; +DROP DATABASE billing; diff --git a/apps/agent/pkg/clickhouse/schema/001_create_raw_api_requests_table.sql b/internal/clickhouse/schema/002_create_metrics_raw_api_requests_v1.sql similarity index 76% rename from apps/agent/pkg/clickhouse/schema/001_create_raw_api_requests_table.sql rename to internal/clickhouse/schema/002_create_metrics_raw_api_requests_v1.sql index e8db0d3b0c..829f5f26e2 100644 --- a/apps/agent/pkg/clickhouse/schema/001_create_raw_api_requests_table.sql +++ b/internal/clickhouse/schema/002_create_metrics_raw_api_requests_v1.sql @@ -1,5 +1,5 @@ -- +goose up -CREATE TABLE default.raw_api_requests_v1( +CREATE TABLE metrics.raw_api_requests_v1( request_id String, -- unix milli time Int64, @@ -27,9 +27,17 @@ CREATE TABLE default.raw_api_requests_v1( service_latency Int64, user_agent String, - ip_address String + ip_address String, + country String, + city String, + colo String, + continent String, + ) ENGINE = MergeTree() ORDER BY (workspace_id, time, request_id) ; + +-- +goose down +DROP TABLE metrics.raw_api_requests_v1; diff --git a/apps/agent/pkg/clickhouse/schema/002_create_raw_key_verifications_table.sql b/internal/clickhouse/schema/003_create_verifications_raw_key_verifications_v1.sql similarity index 85% rename from apps/agent/pkg/clickhouse/schema/002_create_raw_key_verifications_table.sql rename to internal/clickhouse/schema/003_create_verifications_raw_key_verifications_v1.sql index 58ead77dc0..9edb56bd49 100644 --- a/apps/agent/pkg/clickhouse/schema/002_create_raw_key_verifications_table.sql +++ b/internal/clickhouse/schema/003_create_verifications_raw_key_verifications_v1.sql @@ -1,5 +1,5 @@ -- +goose up -CREATE TABLE default.raw_key_verifications_v1( +CREATE TABLE verifications.raw_key_verifications_v1( -- the api request id, so we can correlate the verification with traces and logs request_id String, @@ -30,3 +30,6 @@ CREATE TABLE default.raw_key_verifications_v1( ENGINE = MergeTree() ORDER BY (workspace_id, key_space_id, key_id, time) ; + +-- +goose down +DROP TABLE verifications.raw_key_verifications_v1; diff --git a/internal/clickhouse/schema/004_create_verifications_key_verifications_per_hour_v1.sql b/internal/clickhouse/schema/004_create_verifications_key_verifications_per_hour_v1.sql new file mode 100644 index 0000000000..bfdf27092a --- /dev/null +++ b/internal/clickhouse/schema/004_create_verifications_key_verifications_per_hour_v1.sql @@ -0,0 +1,18 @@ +-- +goose up +CREATE TABLE verifications.key_verifications_per_hour_v1 +( + time DateTime, + workspace_id String, + key_space_id String, + identity_id String, + key_id String, + outcome LowCardinality(String), + count Int64 +) +ENGINE = SummingMergeTree() +ORDER BY (workspace_id, key_space_id, time, identity_id, key_id) +; + + +-- +goose down +DROP TABLE verifications.key_verifications_per_hour_v1; diff --git a/apps/agent/pkg/clickhouse/schema/004_create_key_verifications_per_day.sql b/internal/clickhouse/schema/005_create_verifications_key_verifications_per_day_v1.sql similarity index 57% rename from apps/agent/pkg/clickhouse/schema/004_create_key_verifications_per_day.sql rename to internal/clickhouse/schema/005_create_verifications_key_verifications_per_day_v1.sql index 839a417ca7..d2d8ceb2e6 100644 --- a/apps/agent/pkg/clickhouse/schema/004_create_key_verifications_per_day.sql +++ b/internal/clickhouse/schema/005_create_verifications_key_verifications_per_day_v1.sql @@ -1,5 +1,5 @@ -- +goose up -CREATE TABLE default.key_verifications_per_day_v1 +CREATE TABLE verifications.key_verifications_per_day_v1 ( time DateTime, workspace_id String, @@ -7,8 +7,13 @@ CREATE TABLE default.key_verifications_per_day_v1 identity_id String, key_id String, outcome LowCardinality(String), - count AggregateFunction(count, UInt64) + count Int64 ) -ENGINE = AggregatingMergeTree() +ENGINE = SummingMergeTree() ORDER BY (workspace_id, key_space_id, time, identity_id, key_id) ; + + +-- +goose down +DROP TABLE verifications.key_verifications_per_day_v1; + diff --git a/internal/clickhouse/schema/006_create_verifications_key_verifications_per_month_v1.sql b/internal/clickhouse/schema/006_create_verifications_key_verifications_per_month_v1.sql new file mode 100644 index 0000000000..f849f136ed --- /dev/null +++ b/internal/clickhouse/schema/006_create_verifications_key_verifications_per_month_v1.sql @@ -0,0 +1,17 @@ +-- +goose up +CREATE TABLE verifications.key_verifications_per_month_v1 +( + time DateTime, + workspace_id String, + key_space_id String, + identity_id String, + key_id String, + outcome LowCardinality(String), + count Int64 +) +ENGINE = SummingMergeTree() +ORDER BY (workspace_id, key_space_id, time, identity_id, key_id) +; + +-- +goose down +DROP TABLE verifications.key_verifications_per_month_v1; diff --git a/internal/clickhouse/schema/007_create_verifications_key_verifications_per_hour_mv_v1.sql b/internal/clickhouse/schema/007_create_verifications_key_verifications_per_hour_mv_v1.sql new file mode 100644 index 0000000000..4219ab2404 --- /dev/null +++ b/internal/clickhouse/schema/007_create_verifications_key_verifications_per_hour_mv_v1.sql @@ -0,0 +1,25 @@ +-- +goose up +CREATE MATERIALIZED VIEW verifications.key_verifications_per_hour_mv_v1 +TO verifications.key_verifications_per_hour_v1 +AS +SELECT + workspace_id, + key_space_id, + identity_id, + key_id, + outcome, + count(*) as count, + toStartOfHour(fromUnixTimestamp64Milli(time)) AS time +FROM verifications.raw_key_verifications_v1 +GROUP BY + workspace_id, + key_space_id, + identity_id, + key_id, + outcome, + time +; + + +-- +goose down +DROP VIEW verifications.key_verifications_per_hour_mv_v1; diff --git a/internal/clickhouse/schema/008_create_verifications_key_verifications_per_day_mv_v1.sql b/internal/clickhouse/schema/008_create_verifications_key_verifications_per_day_mv_v1.sql new file mode 100644 index 0000000000..76a050afd2 --- /dev/null +++ b/internal/clickhouse/schema/008_create_verifications_key_verifications_per_day_mv_v1.sql @@ -0,0 +1,25 @@ +-- +goose up +CREATE MATERIALIZED VIEW verifications.key_verifications_per_day_mv_v1 +TO verifications.key_verifications_per_day_v1 +AS +SELECT + workspace_id, + key_space_id, + identity_id, + key_id, + outcome, + count(*) as count, + toStartOfDay(fromUnixTimestamp64Milli(time)) AS time +FROM verifications.raw_key_verifications_v1 +GROUP BY + workspace_id, + key_space_id, + identity_id, + key_id, + outcome, + time +; + + +-- +goose down +DROP VIEW verifications.key_verifications_per_day_mv_v1; diff --git a/internal/clickhouse/schema/009_create_verifications_key_verifications_per_month_mv_v1.sql b/internal/clickhouse/schema/009_create_verifications_key_verifications_per_month_mv_v1.sql new file mode 100644 index 0000000000..e37e73bf4f --- /dev/null +++ b/internal/clickhouse/schema/009_create_verifications_key_verifications_per_month_mv_v1.sql @@ -0,0 +1,25 @@ +-- +goose up +CREATE MATERIALIZED VIEW verifications.key_verifications_per_month_mv_v1 +TO verifications.key_verifications_per_month_v1 +AS +SELECT + workspace_id, + key_space_id, + identity_id, + key_id, + outcome, + count(*) as count, + toStartOfMonth(fromUnixTimestamp64Milli(time)) AS time +FROM verifications.raw_key_verifications_v1 +GROUP BY + workspace_id, + key_space_id, + identity_id, + key_id, + outcome, + time +; + + +-- +goose down +DROP VIEW verifications.key_verifications_per_month_mv_v1; diff --git a/internal/clickhouse/schema/010_create_ratelimits_raw_ratelimits_table.sql b/internal/clickhouse/schema/010_create_ratelimits_raw_ratelimits_table.sql new file mode 100644 index 0000000000..755dd1d95d --- /dev/null +++ b/internal/clickhouse/schema/010_create_ratelimits_raw_ratelimits_table.sql @@ -0,0 +1,19 @@ +-- +goose up +CREATE TABLE ratelimits.raw_ratelimits_v1( + request_id String, + -- unix milli + time Int64, + workspace_id String, + namespace_id String, + identifier String, + passed Bool + +) +ENGINE = MergeTree() +ORDER BY (workspace_id, namespace_id, time, identifier) +; + + + +-- +goose down +DROP TABLE ratelimits.raw_ratelimits_v1; diff --git a/apps/agent/pkg/clickhouse/schema/003_create_raw_telemetry_sdks_table.sql b/internal/clickhouse/schema/011_create_telemetry_raw_sdks_v1.sql similarity index 80% rename from apps/agent/pkg/clickhouse/schema/003_create_raw_telemetry_sdks_table.sql rename to internal/clickhouse/schema/011_create_telemetry_raw_sdks_v1.sql index a318647fb1..3b7a6ad67c 100644 --- a/apps/agent/pkg/clickhouse/schema/003_create_raw_telemetry_sdks_table.sql +++ b/internal/clickhouse/schema/011_create_telemetry_raw_sdks_v1.sql @@ -1,5 +1,5 @@ -- +goose up -CREATE TABLE default.raw_telemetry_sdks_v1( +CREATE TABLE telemetry.raw_sdks_v1( -- the api request id, so we can correlate the telemetry with traces and logs request_id String, @@ -17,3 +17,8 @@ CREATE TABLE default.raw_telemetry_sdks_v1( ENGINE = MergeTree() ORDER BY (request_id, time) ; + + + +-- +goose down +DROP TABLE telemetry.raw_sdks_v1; diff --git a/internal/clickhouse/schema/012_create_billing_billable_verifications_per_month_v1.sql b/internal/clickhouse/schema/012_create_billing_billable_verifications_per_month_v1.sql new file mode 100644 index 0000000000..31dfaf6fe4 --- /dev/null +++ b/internal/clickhouse/schema/012_create_billing_billable_verifications_per_month_v1.sql @@ -0,0 +1,15 @@ +-- +goose up +CREATE TABLE billing.billable_verifications_per_month_v1 +( + year Int, + month Int, + workspace_id String, + count Int64 +) +ENGINE = SummingMergeTree() +ORDER BY (workspace_id, year, month) +; + + +-- +goose down +DROP TABLE billing.billable_verifications_per_month_v1; diff --git a/internal/clickhouse/schema/013_create_billing_billable_verifications_per_month_mv_v1.sql b/internal/clickhouse/schema/013_create_billing_billable_verifications_per_month_mv_v1.sql new file mode 100644 index 0000000000..00e8005f1d --- /dev/null +++ b/internal/clickhouse/schema/013_create_billing_billable_verifications_per_month_mv_v1.sql @@ -0,0 +1,21 @@ +-- +goose up +CREATE MATERIALIZED VIEW billing.billable_verifications_per_month_mv_v1 +TO billing.billable_verifications_per_month_v1 +AS +SELECT + workspace_id, + count(*) AS count, + toYear(time) AS year, + toMonth(time) AS month +FROM verifications.key_verifications_per_month_v1 +WHERE outcome = 'VALID' +GROUP BY + workspace_id, + year, + month +; + + + +-- +goose down +DROP VIEW billing.billable_verifications_per_month_mv_v1; diff --git a/internal/clickhouse/schema/014_create_ratelimits_ratelimits_per_minute_v1.sql b/internal/clickhouse/schema/014_create_ratelimits_ratelimits_per_minute_v1.sql new file mode 100644 index 0000000000..cfc1b4b6a9 --- /dev/null +++ b/internal/clickhouse/schema/014_create_ratelimits_ratelimits_per_minute_v1.sql @@ -0,0 +1,19 @@ +-- +goose up +CREATE TABLE ratelimits.ratelimits_per_minute_v1 +( + time DateTime, + workspace_id String, + namespace_id String, + identifier String, + + passed Int64, + total Int64 +) +ENGINE = SummingMergeTree() +ORDER BY (workspace_id, namespace_id, time, identifier) +; + + + +-- +goose down +DROP TABLE ratelimits.ratelimits_per_minute_v1; diff --git a/internal/clickhouse/schema/015_create_ratelimits_ratelimits_per_hour_v1.sql b/internal/clickhouse/schema/015_create_ratelimits_ratelimits_per_hour_v1.sql new file mode 100644 index 0000000000..1cc5f6aca2 --- /dev/null +++ b/internal/clickhouse/schema/015_create_ratelimits_ratelimits_per_hour_v1.sql @@ -0,0 +1,18 @@ +-- +goose up +CREATE TABLE ratelimits.ratelimits_per_hour_v1 +( + time DateTime, + workspace_id String, + namespace_id String, + identifier String, + + passed Int64, + total Int64 +) +ENGINE = SummingMergeTree() +ORDER BY (workspace_id, namespace_id, time, identifier) +; + + +-- +goose down +DROP TABLE ratelimits.ratelimits_per_hour_v1; diff --git a/internal/clickhouse/schema/016_create_ratelimits_ratelimits_per_day_v1.sql b/internal/clickhouse/schema/016_create_ratelimits_ratelimits_per_day_v1.sql new file mode 100644 index 0000000000..57b7b9febf --- /dev/null +++ b/internal/clickhouse/schema/016_create_ratelimits_ratelimits_per_day_v1.sql @@ -0,0 +1,19 @@ +-- +goose up +CREATE TABLE ratelimits.ratelimits_per_day_v1 +( + time DateTime, + workspace_id String, + namespace_id String, + identifier String, + + passed Int64, + total Int64 +) +ENGINE = SummingMergeTree() +ORDER BY (workspace_id, namespace_id, time, identifier) +; + + + +-- +goose down +DROP TABLE ratelimits.ratelimits_per_day_v1; diff --git a/internal/clickhouse/schema/017_create_ratelimits_ratelimits_per_month_v1.sql b/internal/clickhouse/schema/017_create_ratelimits_ratelimits_per_month_v1.sql new file mode 100644 index 0000000000..ffd91a5f63 --- /dev/null +++ b/internal/clickhouse/schema/017_create_ratelimits_ratelimits_per_month_v1.sql @@ -0,0 +1,19 @@ +-- +goose up +CREATE TABLE ratelimits.ratelimits_per_month_v1 +( + time DateTime, + workspace_id String, + namespace_id String, + identifier String, + + passed Int64, + total Int64 +) +ENGINE = SummingMergeTree() +ORDER BY (workspace_id, namespace_id, time, identifier) +; + + + +-- +goose down +DROP TABLE ratelimits.ratelimits_per_month_v1; diff --git a/internal/clickhouse/schema/018_create_ratelimits_ratelimits_per_minute_mv_v1.sql b/internal/clickhouse/schema/018_create_ratelimits_ratelimits_per_minute_mv_v1.sql new file mode 100644 index 0000000000..93487cd52e --- /dev/null +++ b/internal/clickhouse/schema/018_create_ratelimits_ratelimits_per_minute_mv_v1.sql @@ -0,0 +1,23 @@ +-- +goose up +CREATE MATERIALIZED VIEW ratelimits.ratelimits_per_minute_mv_v1 +TO ratelimits.ratelimits_per_minute_v1 +AS +SELECT + workspace_id, + namespace_id, + identifier, + countIf(passed > 0) as passed, + count(*) as total, + toStartOfMinute(fromUnixTimestamp64Milli(time)) AS time +FROM ratelimits.raw_ratelimits_v1 +GROUP BY + workspace_id, + namespace_id, + identifier, + time +; + + + +-- +goose down +DROP VIEW ratelimits.ratelimits_per_minute_mv_v1; diff --git a/internal/clickhouse/schema/019_create_ratelimits_ratelimits_per_hour_mv_v1.sql b/internal/clickhouse/schema/019_create_ratelimits_ratelimits_per_hour_mv_v1.sql new file mode 100644 index 0000000000..fb423d933e --- /dev/null +++ b/internal/clickhouse/schema/019_create_ratelimits_ratelimits_per_hour_mv_v1.sql @@ -0,0 +1,22 @@ +-- +goose up +CREATE MATERIALIZED VIEW ratelimits.ratelimits_per_hour_mv_v1 +TO ratelimits.ratelimits_per_hour_v1 +AS +SELECT + workspace_id, + namespace_id, + identifier, + countIf(passed > 0) as passed, + count(*) as total, + toStartOfHour(fromUnixTimestamp64Milli(time)) AS time +FROM ratelimits.raw_ratelimits_v1 +GROUP BY + workspace_id, + namespace_id, + identifier, + time +; + + +-- +goose down +DROP VIEW ratelimits.ratelimits_per_hour_mv_v1; diff --git a/internal/clickhouse/schema/020_create_ratelimits_ratelimits_per_day_mv_v1.sql b/internal/clickhouse/schema/020_create_ratelimits_ratelimits_per_day_mv_v1.sql new file mode 100644 index 0000000000..8e76285e3b --- /dev/null +++ b/internal/clickhouse/schema/020_create_ratelimits_ratelimits_per_day_mv_v1.sql @@ -0,0 +1,23 @@ +-- +goose up +CREATE MATERIALIZED VIEW ratelimits.ratelimits_per_day_mv_v1 +TO ratelimits.ratelimits_per_day_v1 +AS +SELECT + workspace_id, + namespace_id, + identifier, + count(*) as total, + countIf(passed > 0) as passed, + toStartOfDay(fromUnixTimestamp64Milli(time)) AS time +FROM ratelimits.raw_ratelimits_v1 +GROUP BY + workspace_id, + namespace_id, + identifier, + time +; + + + +-- +goose down +DROP VIEW ratelimits.ratelimits_per_day_mv_v1; diff --git a/internal/clickhouse/schema/021_create_ratelimits_ratelimits_per_month_mv_v1.sql b/internal/clickhouse/schema/021_create_ratelimits_ratelimits_per_month_mv_v1.sql new file mode 100644 index 0000000000..a97c911186 --- /dev/null +++ b/internal/clickhouse/schema/021_create_ratelimits_ratelimits_per_month_mv_v1.sql @@ -0,0 +1,22 @@ +-- +goose up +CREATE MATERIALIZED VIEW ratelimits.ratelimits_per_month_mv_v1 +TO ratelimits.ratelimits_per_month_v1 +AS +SELECT + workspace_id, + namespace_id, + identifier, + countIf(passed > 0) as passed, + count(*) as total, + toStartOfMonth(fromUnixTimestamp64Milli(time)) AS time +FROM ratelimits.raw_ratelimits_v1 +GROUP BY + workspace_id, + namespace_id, + identifier, + time +; + + +-- +goose down +DROP VIEW ratelimits.ratelimits_per_month_mv_v1; diff --git a/internal/clickhouse/schema/022_create_business_active_workspaces_per_month_v1.sql b/internal/clickhouse/schema/022_create_business_active_workspaces_per_month_v1.sql new file mode 100644 index 0000000000..29be93dd73 --- /dev/null +++ b/internal/clickhouse/schema/022_create_business_active_workspaces_per_month_v1.sql @@ -0,0 +1,13 @@ +-- +goose up +CREATE TABLE business.active_workspaces_per_month_v1 +( + time Date, + workspace_id String +) +ENGINE = MergeTree() +ORDER BY (time) +; + + +-- +goose down +DROP TABLE business.active_workspaces_per_month_v1; diff --git a/internal/clickhouse/schema/023_create_business_active_workspaces_per_month_mv_v1.sql b/internal/clickhouse/schema/023_create_business_active_workspaces_per_month_mv_v1.sql new file mode 100644 index 0000000000..a22401696b --- /dev/null +++ b/internal/clickhouse/schema/023_create_business_active_workspaces_per_month_mv_v1.sql @@ -0,0 +1,21 @@ +-- +goose up +CREATE MATERIALIZED VIEW IF NOT EXISTS business.active_workspaces_keys_per_month_mv_v1 +TO business.active_workspaces_per_month_v1 +AS +SELECT + workspace_id, toDate(time) as time +FROM verifications.key_verifications_per_month_v1 +; + +CREATE MATERIALIZED VIEW IF NOT EXISTS business.active_workspaces_ratelimits_per_month_mv_v1 +TO business.active_workspaces_per_month_v1 +AS +SELECT + workspace_id, toDate(time) as time +FROM ratelimits.ratelimits_per_month_v1 +; + + +-- +goose down +DROP VIEW business.active_workspaces_keys_per_month_mv_v1; +DROP VIEW business.active_workspaces_ratelimits_per_month_mv_v1; diff --git a/internal/clickhouse/schema/024_create_ratelimits_last_used_mv_v1.sql b/internal/clickhouse/schema/024_create_ratelimits_last_used_mv_v1.sql new file mode 100644 index 0000000000..83e1bf66cc --- /dev/null +++ b/internal/clickhouse/schema/024_create_ratelimits_last_used_mv_v1.sql @@ -0,0 +1,19 @@ +-- +goose up +CREATE MATERIALIZED VIEW ratelimits.ratelimits_last_used_mv_v1 +TO ratelimits.ratelimits_last_used_v1 +AS +SELECT + workspace_id, + namespace_id, + identifier, + maxSimpleState(time) as time +FROM ratelimits.raw_ratelimits_v1 +GROUP BY + workspace_id, + namespace_id, + identifier +; + + +-- +goose down +DROP VIEW ratelimits.ratelimits_last_used_mv_v1; diff --git a/internal/clickhouse/src/active_keys.ts b/internal/clickhouse/src/active_keys.ts new file mode 100644 index 0000000000..f4475fa3c7 --- /dev/null +++ b/internal/clickhouse/src/active_keys.ts @@ -0,0 +1,114 @@ +import { z } from "zod"; +import type { Querier } from "./client/interface"; + +export function getActiveKeysPerHour(ch: Querier) { + return async (args: { + workspaceId: string; + keySpaceId: string; + start: number; + end: number; + }) => { + const query = ch.query({ + query: ` SELECT count(DISTINCT keyId) as keys, time, FROM verifications.key_verifications_per_hour_v1 WHERE workspace_id = {workspaceId: String} AND key_space_id = {keySpaceId: String} AND time >= {start: Int64} AND time < {end: Int64} GROUP BY time + ORDER BY time ASC + WITH FILL + FROM toStartOfHour(fromUnixTimestamp64Milli({start: Int64})) + TO toStartOfHour(fromUnixTimestamp64Milli({end: Int64})) + STEP INTERVAL 1 HOUR + ;`, + params: z.object({ + workspaceId: z.string(), + keySpaceId: z.string(), + start: z.number().int(), + end: z.number().int(), + }), + schema: z.object({ + keys: z.number().int(), + time: z.number().int(), + }), + }); + + return query(args); + }; +} + +export function getActiveKeysPerDay(ch: Querier) { + return async (args: { + workspaceId: string; + keySpaceId: string; + start: number; + end: number; + }) => { + const query = ch.query({ + query: ` + SELECT + count(DISTINCT keyId) as keys, + time, + FROM verifications.key_verifications_per_day_v1 + WHERE + workspace_id = {workspaceId: String} + AND key_space_id = {keySpaceId: String} + AND time >= {start: Int64} + AND time < {end: Int64} + GROUP BY time + ORDER BY time ASC + WITH FILL + FROM toStartOfDay(fromUnixTimestamp64Milli({start: Int64})) + TO toStartOfDay(fromUnixTimestamp64Milli({end: Int64})) + STEP INTERVAL 1 DAY + ;`, + params: z.object({ + workspaceId: z.string(), + keySpaceId: z.string(), + start: z.number().int(), + end: z.number().int(), + }), + schema: z.object({ + keys: z.number().int(), + time: z.number().int(), + }), + }); + + return query(args); + }; +} +export function getActiveKeysPerMonth(ch: Querier) { + return async (args: { + workspaceId: string; + keySpaceId: string; + start: number; + end: number; + }) => { + const query = ch.query({ + query: ` + SELECT + count(DISTINCT keyId) as keys, + time, + FROM verifications.key_verifications_per_month_v1 + WHERE + workspace_id = {workspaceId: String} + AND key_space_id = {keySpaceId: String} + AND time >= {start: Int64} + AND time < {end: Int64} + GROUP BY time + ORDER BY time ASC + WITH FILL + FROM toStartOfMonth(fromUnixTimestamp64Milli({start: Int64})) + TO toStartOfMonth(fromUnixTimestamp64Milli({end: Int64})) + STEP INTERVAL 1 MONTH + ;`, + params: z.object({ + workspaceId: z.string(), + keySpaceId: z.string(), + start: z.number().int(), + end: z.number().int(), + }), + schema: z.object({ + keys: z.number().int(), + time: z.number().int(), + }), + }); + + return query(args); + }; +} diff --git a/internal/clickhouse/src/billing.ts b/internal/clickhouse/src/billing.ts new file mode 100644 index 0000000000..599b68b2ac --- /dev/null +++ b/internal/clickhouse/src/billing.ts @@ -0,0 +1,38 @@ +import { z } from "zod"; +import type { Querier } from "./client"; + +// get the billable verifications for a workspace in a specific month. +// month is not zero-indexed -> January = 1 +export function getBillableVerifications(ch: Querier) { + return async (args: { + workspaceId: string; + year: number; + month: number; + }): Promise => { + const query = ch.query({ + query: ` + SELECT + sum(count) as count + FROM billing.billable_verifications_per_month_v1 + WHERE workspace_id = {workspaceId: String} + AND year = {year: Int64} + AND month = {month: Int64} + GROUP BY workspace_id, year, month + `, + params: z.object({ + workspaceId: z.string(), + year: z.number().int(), + month: z.number().int().min(1).max(12), + }), + schema: z.object({ + count: z.number().int(), + }), + }); + + const res = await query(args); + if (!res) { + return 0; + } + return res.at(0)?.count ?? 0; + }; +} diff --git a/internal/clickhouse-zod/src/client.ts b/internal/clickhouse/src/client/client.ts similarity index 77% rename from internal/clickhouse-zod/src/client.ts rename to internal/clickhouse/src/client/client.ts index 06ce9b7a8c..0773cca031 100644 --- a/internal/clickhouse-zod/src/client.ts +++ b/internal/clickhouse/src/client/client.ts @@ -1,12 +1,12 @@ import { type ClickHouseClient, createClient } from "@clickhouse/client-web"; import { z } from "zod"; -import type { Clickhouse } from "./interface"; +import type { Inserter, Querier } from "./interface"; export type Config = { url: string; }; -export class Client implements Clickhouse { +export class Client implements Querier, Inserter { private readonly client: ClickHouseClient; constructor(config: Config) { @@ -34,11 +34,19 @@ export class Client implements Clickhouse { schema: TOut; }): (params: z.input) => Promise[]> { return async (params: z.input): Promise[]> => { - const res = await this.client.query({ - query: req.query, - query_params: req.params?.safeParse(params), - format: "JSONEachRow", - }); + const validParams = req.params?.safeParse(params); + if (validParams?.error) { + throw new Error(`Bad params: ${validParams.error.message}`); + } + const res = await this.client + .query({ + query: req.query, + query_params: validParams?.data, + format: "JSONEachRow", + }) + .catch((err) => { + throw new Error(`${err.message} ${req.query}, params: ${JSON.stringify(params)}`); + }); const rows = await res.json(); return z.array(req.schema).parse(rows); }; diff --git a/internal/clickhouse-zod/src/index.ts b/internal/clickhouse/src/client/index.ts similarity index 100% rename from internal/clickhouse-zod/src/index.ts rename to internal/clickhouse/src/client/index.ts diff --git a/internal/clickhouse-zod/src/interface.ts b/internal/clickhouse/src/client/interface.ts similarity index 92% rename from internal/clickhouse-zod/src/interface.ts rename to internal/clickhouse/src/client/interface.ts index e950fabc60..ea4d373d05 100644 --- a/internal/clickhouse-zod/src/interface.ts +++ b/internal/clickhouse/src/client/interface.ts @@ -1,6 +1,6 @@ import type { z } from "zod"; -export interface Clickhouse { +export interface Querier { query, TOut extends z.ZodSchema>(req: { // The SQL query to run. // Use {paramName: Type} to define parameters @@ -13,7 +13,9 @@ export interface Clickhouse { // Example: z.object({ id: z.string() }) schema: TOut; }): (params: z.input) => Promise[]>; +} +export interface Inserter { insert>(req: { table: string; schema: TSchema; diff --git a/internal/clickhouse-zod/src/noop.ts b/internal/clickhouse/src/client/noop.ts similarity index 92% rename from internal/clickhouse-zod/src/noop.ts rename to internal/clickhouse/src/client/noop.ts index c5164b328f..7eef057cc0 100644 --- a/internal/clickhouse-zod/src/noop.ts +++ b/internal/clickhouse/src/client/noop.ts @@ -1,6 +1,6 @@ import type { z } from "zod"; -import type { Clickhouse } from "./interface"; -export class Noop implements Clickhouse { +import type { Inserter, Querier } from "./interface"; +export class Noop implements Querier, Inserter { public query, TOut extends z.ZodSchema>(req: { // The SQL query to run. // Use {paramName: Type} to define parameters diff --git a/internal/clickhouse/src/index.ts b/internal/clickhouse/src/index.ts new file mode 100644 index 0000000000..f7d9062d13 --- /dev/null +++ b/internal/clickhouse/src/index.ts @@ -0,0 +1,80 @@ +import { getActiveKeysPerDay, getActiveKeysPerHour, getActiveKeysPerMonth } from "./active_keys"; +import { getBillableVerifications } from "./billing"; +import { Client, type Inserter, Noop, type Querier } from "./client"; +import { getLatestVerifications } from "./latest_verifications"; +import { getLogs } from "./logs"; +import { + getRatelimitLastUsed, + getRatelimitLogs, + getRatelimitsPerDay, + getRatelimitsPerHour, + getRatelimitsPerMinute, + getRatelimitsPerMonth, +} from "./ratelimits"; +import { getActiveWorkspacesPerMonth } from "./success"; +import { + getVerificationsPerDay, + getVerificationsPerHour, + getVerificationsPerMonth, +} from "./verifications"; + +export type ClickHouseConfig = { + url?: string; +}; + +export class ClickHouse { + public readonly client: Querier & Inserter; + + constructor(config: ClickHouseConfig) { + if (config.url) { + this.client = new Client({ url: config.url }); + } else { + this.client = new Noop(); + } + } + + static fromEnv(): ClickHouse { + return new ClickHouse({ url: process.env.CLICKHOUSE_URL }); + } + public get verifications() { + return { + logs: getLatestVerifications(this.client), + perHour: getVerificationsPerHour(this.client), + perDay: getVerificationsPerDay(this.client), + perMonth: getVerificationsPerMonth(this.client), + latest: getLatestVerifications(this.client), + }; + } + public get activeKeys() { + return { + perHour: getActiveKeysPerHour(this.client), + perDay: getActiveKeysPerDay(this.client), + perMonth: getActiveKeysPerMonth(this.client), + }; + } + public get ratelimits() { + return { + logs: getRatelimitLogs(this.client), + latest: getRatelimitLastUsed(this.client), + perMinute: getRatelimitsPerMinute(this.client), + perHour: getRatelimitsPerHour(this.client), + perDay: getRatelimitsPerDay(this.client), + perMonth: getRatelimitsPerMonth(this.client), + }; + } + public get billing() { + return { + billableVerifications: getBillableVerifications(this.client), + }; + } + public get api() { + return { + logs: getLogs(this.client), + }; + } + public get business() { + return { + activeWorkspaces: getActiveWorkspacesPerMonth(this.client), + }; + } +} diff --git a/internal/clickhouse/src/last_used.ts b/internal/clickhouse/src/last_used.ts new file mode 100644 index 0000000000..3cbd0f85d7 --- /dev/null +++ b/internal/clickhouse/src/last_used.ts @@ -0,0 +1,36 @@ +import { type Clickhouse, Client, Noop } from "@unkey/clickhouse-zod"; +import { z } from "zod"; +import { env } from "../env"; + +export async function getLastUsed(args: { + workspaceId: string; + keySpaceId: string; + keyId: string; +}) { + const { CLICKHOUSE_URL } = env(); + + const ch: Clickhouse = CLICKHOUSE_URL ? new Client({ url: CLICKHOUSE_URL }) : new Noop(); + const query = ch.query({ + query: ` + SELECT + time, + FROM verifications.raw_key_verifications_v1 + WHERE + workspace_id = {workspaceId: String} + AND key_space_id = {keySpaceId: String} + AND key_id = {keyId:String} + ORDER BY time DESC + LIMIT 1 + ;`, + params: z.object({ + workspaceId: z.string(), + keySpaceId: z.string(), + keyId: z.string(), + }), + schema: z.object({ + time: z.number().int(), + }), + }); + + return query(args); +} diff --git a/internal/clickhouse/src/latest_verifications.ts b/internal/clickhouse/src/latest_verifications.ts new file mode 100644 index 0000000000..5917440be5 --- /dev/null +++ b/internal/clickhouse/src/latest_verifications.ts @@ -0,0 +1,33 @@ +import { z } from "zod"; +import type { Querier } from "./client"; + +const params = z.object({ + workspaceId: z.string(), + keySpaceId: z.string(), + keyId: z.string(), +}); +export function getLatestVerifications(ch: Querier) { + return async (args: z.infer) => { + const query = ch.query({ + query: ` + SELECT + time, + outcome, + region, + FROM default.raw_key_verifications_v1 + WHERE workspace_id = {workspaceId: String} + AND key_space_id = {keySpaceId: String} + AND key_id = {keyId: String} + ORDER BY time DESC + LIMIT 1`, + params, + schema: z.object({ + time: z.number(), + outcome: z.string(), + region: z.string(), + }), + }); + + return query(args); + }; +} diff --git a/internal/clickhouse/src/logs.ts b/internal/clickhouse/src/logs.ts new file mode 100644 index 0000000000..ceefb882ba --- /dev/null +++ b/internal/clickhouse/src/logs.ts @@ -0,0 +1,49 @@ +import { z } from "zod"; +import type { Querier } from "./client"; + +export function getLogs(ch: Querier) { + return async (args: { workspaceId: string; limit: number }) => { + const query = ch.query({ + query: ` + SELECT + request_id, + time, + workspace_id, + host, + method, + path, + request_headers, + request_body, + response_status, + response_headers, + response_body, + error, + service_latency + FROM default.raw_api_requests_v1 + WHERE workspace_id = {workspaceId: String} + ORDER BY time DESC + LIMIT {limit: Int}`, + params: z.object({ + workspaceId: z.string(), + limit: z.number().int(), + }), + schema: z.object({ + request_id: z.string(), + time: z.number().int(), + workspace_id: z.string(), + host: z.string(), + method: z.string(), + path: z.string(), + request_headers: z.array(z.string()), + request_body: z.string(), + response_status: z.number().int(), + response_headers: z.array(z.string()), + response_body: z.string(), + error: z.string(), + service_latency: z.number().int(), + }), + }); + + return query(args); + }; +} diff --git a/internal/clickhouse/src/ratelimits.ts b/internal/clickhouse/src/ratelimits.ts new file mode 100644 index 0000000000..c36547d2f7 --- /dev/null +++ b/internal/clickhouse/src/ratelimits.ts @@ -0,0 +1,212 @@ +import { z } from "zod"; +import type { Querier } from "./client"; +import { dateTimeToUnix } from "./util"; + +const params = z.object({ + workspaceId: z.string(), + namespaceId: z.string(), + identifier: z.array(z.string()).optional(), + start: z.number().default(0), + end: z.number().default(() => Date.now()), +}); + +export function getRatelimitsPerMinute(ch: Querier) { + return async (args: z.infer) => { + const query = ch.query({ + query: ` + SELECT + time, + sum(passed) as passed, + sum(total) as total + FROM ratelimits.ratelimits_per_minute_v1 + WHERE workspace_id = {workspaceId: String} + AND namespace_id = {namespaceId: String} + ${args.identifier ? "AND multiSearchAny(identifier, {identifier: Array(String)}) > 0" : ""} + AND time >= fromUnixTimestamp64Milli({start: Int64}) + AND time <= fromUnixTimestamp64Milli({end: Int64}) + GROUP BY time + ORDER BY time ASC + WITH FILL + FROM toStartOfMinute(fromUnixTimestamp64Milli({start: Int64})) + TO toStartOfMinute(fromUnixTimestamp64Milli({end: Int64})) + STEP INTERVAL 1 MINUTE +;`, + params, + schema: z.object({ + time: dateTimeToUnix, + passed: z.number(), + total: z.number(), + }), + }); + + return query(args); + }; +} + +export function getRatelimitsPerHour(ch: Querier) { + return async (args: z.infer) => { + const query = ch.query({ + query: ` + SELECT + time, + sum(passed) as passed, + sum(total) as total + FROM ratelimits.ratelimits_per_hour_v1 + WHERE workspace_id = {workspaceId: String} + AND namespace_id = {namespaceId: String} + ${args.identifier ? "AND multiSearchAny(identifier, {identifier: Array(String)}) > 0" : ""} + AND time >= fromUnixTimestamp64Milli({start: Int64}) + AND time <= fromUnixTimestamp64Milli({end: Int64}) + GROUP BY time + ORDER BY time ASC + WITH FILL + FROM toStartOfHour(fromUnixTimestamp64Milli({start: Int64})) + TO toStartOfHour(fromUnixTimestamp64Milli({end: Int64})) + STEP INTERVAL 1 HOUR +;`, + params, + schema: z.object({ + time: dateTimeToUnix, + passed: z.number(), + total: z.number(), + }), + }); + + return query(args); + }; +} +export function getRatelimitsPerDay(ch: Querier) { + return async (args: z.infer) => { + const query = ch.query({ + query: ` + SELECT + time, + sum(passed) as passed, + sum(total) as total + FROM ratelimits.ratelimits_per_day_v1 + WHERE workspace_id = {workspaceId: String} + AND namespace_id = {namespaceId: String} + ${args.identifier ? "AND multiSearchAny(identifier, {identifier: Array(String)}) > 0" : ""} + AND time >= fromUnixTimestamp64Milli({start: Int64}) + AND time <= fromUnixTimestamp64Milli({end: Int64}) + GROUP BY time + ORDER BY time ASC + WITH FILL + FROM toStartOfDay(fromUnixTimestamp64Milli({start: Int64})) + TO toStartOfDay(fromUnixTimestamp64Milli({end: Int64})) + STEP INTERVAL 1 DAY +;`, + params, + schema: z.object({ + time: dateTimeToUnix, + passed: z.number(), + total: z.number(), + }), + }); + + return query(args); + }; +} +export function getRatelimitsPerMonth(ch: Querier) { + return async (args: z.input) => { + const query = ch.query({ + query: ` + SELECT + time, + sum(passed) as passed, + sum(total) as total + FROM ratelimits.ratelimits_per_month_v1 + WHERE workspace_id = {workspaceId: String} + AND namespace_id = {namespaceId: String} + ${args.identifier ? "AND multiSearchAny(identifier, {identifier: Array(String)}) > 0" : ""} + AND time >= fromUnixTimestamp64Milli({start: Int64}) + AND time <= fromUnixTimestamp64Milli({end: Int64}) + GROUP BY time + ORDER BY time ASC + WITH FILL + FROM toStartOfMonth(fromUnixTimestamp64Milli({start: Int64})) + TO toStartOfMonth(fromUnixTimestamp64Milli({end: Int64})) + STEP INTERVAL 1 MONTH +;`, + params, + schema: z.object({ + time: dateTimeToUnix, + passed: z.number(), + total: z.number(), + }), + }); + + return query(args); + }; +} +const getRatelimitLogsParameters = z.object({ + workspaceId: z.string(), + namespaceId: z.string(), + identifier: z.array(z.string()).optional(), + start: z.number().optional().default(0), + end: z + .number() + .optional() + .default(() => Date.now()), + limit: z.number().optional().default(100), +}); + +export function getRatelimitLogs(ch: Querier) { + return async (args: z.input) => { + const query = ch.query({ + query: ` + SELECT + request_id, + time, + identifier, + passed + FROM ratelimits.raw_ratelimits + WHERE workspace_id = {workspaceId: String} + AND namespace_id = {namespaceId: String} + ${args.identifier ? "AND multiSearchAny(identifier, {identifier: Array(String)}) > 0" : ""} + AND time >= {start: Int64} + AND time <= {end: Int64} + LIMIT {limit: Int64} +;`, + params: getRatelimitLogsParameters, + schema: z.object({ + request_id: z.string(), + time: z.number(), + identifier: z.string(), + passed: z.boolean(), + }), + }); + + return query(args); + }; +} +const getRatelimitLastUsedParameters = z.object({ + workspaceId: z.string(), + namespaceId: z.string(), + identifier: z.array(z.string()).optional(), +}); + +export function getRatelimitLastUsed(ch: Querier) { + return async (args: z.input) => { + const query = ch.query({ + query: ` + SELECT + identifier, + max(time) as time + FROM ratelimits.ratelimits_last_used_v1 + WHERE + workspace_id = {workspaceId: String} + AND namespace_id = {namespaceId: String} + ${args.identifier ? "AND multiSearchAny(identifier, {identifier: Array(String)}) > 0" : ""} + GROUP BY identifier +;`, + params: getRatelimitLastUsedParameters, + schema: z.object({ + identifier: z.string(), + time: z.number(), + }), + }); + + return query(args); + }; +} diff --git a/internal/clickhouse/src/success.ts b/internal/clickhouse/src/success.ts new file mode 100644 index 0000000000..5d0bd830fb --- /dev/null +++ b/internal/clickhouse/src/success.ts @@ -0,0 +1,26 @@ +import { z } from "zod"; +import type { Querier } from "./client"; +import { dateTimeToUnix } from "./util"; + +// get the billable verifications for a workspace in a specific month. +// month is not zero-indexed -> January = 1 +export function getActiveWorkspacesPerMonth(ch: Querier) { + return async () => { + const query = ch.query({ + query: ` + SELECT + count(DISTINCT workspace_id) as workspaces, + time + FROM business.active_workspaces_per_month_v1 + GROUP BY time + ORDER BY time ASC + ;`, + schema: z.object({ + time: dateTimeToUnix, + workspaces: z.number().int(), + }), + }); + + return await query({}); + }; +} diff --git a/internal/clickhouse/src/util.ts b/internal/clickhouse/src/util.ts new file mode 100644 index 0000000000..3efef6903a --- /dev/null +++ b/internal/clickhouse/src/util.ts @@ -0,0 +1,3 @@ +import { z } from "zod"; +// clickhouse DateTime returns a string, which we need to parse +export const dateTimeToUnix = z.string().transform((t) => new Date(t).getTime()); diff --git a/internal/clickhouse/src/verifications.ts b/internal/clickhouse/src/verifications.ts new file mode 100644 index 0000000000..d73f8fbbc8 --- /dev/null +++ b/internal/clickhouse/src/verifications.ts @@ -0,0 +1,109 @@ +import { z } from "zod"; +import type { Querier } from "./client"; +import { dateTimeToUnix } from "./util"; + +const outcome = z.enum([ + "VALID", + "INSUFFICIENT_PERMISSIONS", + "RATE_LIMITED", + "FORBIDDEN", + "DISABLED", + "EXPIRED", + "USAGE_EXCEEDED", +]); + +const params = z.object({ + workspaceId: z.string(), + keySpaceId: z.string(), + keyId: z.string().optional(), + start: z.number().int(), + end: z.number().int(), +}); +const schema = z.object({ + time: dateTimeToUnix, + outcome, + count: z.number().int(), +}); + +export function getVerificationsPerHour(ch: Querier) { + return async (args: z.input) => { + const query = ` + SELECT + time, + outcome async, + count + FROM verifications.key_verifications_per_hour_v1 + WHERE + workspace_id = {workspaceId: String} + AND key_space_id = {keySpaceId: String} AND time >= {start: Int64} + AND time < {end: Int64} + ${args.keyId ? "AND key_id = {keyId: String}" : ""} + GROUP BY time + ORDER BY time ASC + WITH FILL + FROM toStartOfHour(fromUnixTimestamp64Milli({start: Int64})) + TO toStartOfHour(fromUnixTimestamp64Milli({end: Int64})) + STEP INTERVAL 1 HOUR + ;`; + + return ch.query({ + query, + params, + schema, + })(args); + }; +} + +export function getVerificationsPerDay(ch: Querier) { + return async (args: z.input) => { + const query = ` + SELECT + time, + outcome, + count + FROM verifications.key_verifications_per_day_v1 + WHERE + workspace_id = {workspaceId: String} + AND key_space_id = {keySpaceId: String} AND time >= {start: Int64} + AND time < {end: Int64} + ${args.keyId ? "AND key_id = {keyId: String}" : ""} + GROUP BY time + ORDER BY time ASC + WITH FILL + FROM toStartOfDay(fromUnixTimestamp64Milli({start: Int64})) + TO toStartOfDay(fromUnixTimestamp64Milli({end: Int64})) + STEP INTERVAL 1D AY + ;`; + + return ch.query({ query, params, schema })(args); + }; +} + +export function getVerificationsPerMonth(ch: Querier) { + return async (args: z.input) => { + const query = ` + SELECT + time, + outcome, + count + FROM verifications.key_verifications_per_month_v1 + WHERE + workspace_id = {workspaceId: String} + AND key_space_id = {keySpaceId: String} AND time >= {start: Int64} + AND time < {end: Int64} + ${args.keyId ? "AND key_id = {keyId: String}" : ""} + GROUP BY time + ORDER BY time ASC + WITH FILL + FROM toStartOfMonth(fromUnixTimestamp64Milli({start: Int64})) + TO toStartOfMonth(fromUnixTimestamp64Milli({end: Int64})) + STEP INTERVAL 1 MONTH + ;`; + + return ch.query({ + query, + params, + schema, + })(args); + }; +} diff --git a/internal/db/src/schema/workspaces.ts b/internal/db/src/schema/workspaces.ts index 95e483d101..94233aeb69 100644 --- a/internal/db/src/schema/workspaces.ts +++ b/internal/db/src/schema/workspaces.ts @@ -133,5 +133,7 @@ export const workspacesRelations = relations(workspaces, ({ many }) => ({ verificationMonitors: many(verificationMonitors), keySpaces: many(keyAuth), identities: many(identities), - auditLogBuckets: many(auditLogBucket), + auditLogBuckets: many(auditLogBucket, { + relationName: "workspace_audit_log_bucket_relation", + }), })); diff --git a/internal/db/src/types.ts b/internal/db/src/types.ts index 0487367b5c..ee288c50ba 100644 --- a/internal/db/src/types.ts +++ b/internal/db/src/types.ts @@ -20,3 +20,5 @@ export type KeyRole = InferSelectModel; export type KeyPermission = InferSelectModel; export type Ratelimit = InferSelectModel; export type Identity = InferSelectModel; +export type AuditLog = InferSelectModel; +export type AuditLogTarget = InferSelectModel; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 09cb2d8aef..8ab0cfc1fe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -79,9 +79,9 @@ importers: '@unkey/cache': specifier: workspace:^ version: link:../../packages/cache - '@unkey/clickhouse-zod': + '@unkey/clickhouse': specifier: workspace:^ - version: link:../../internal/clickhouse-zod + version: link:../../internal/clickhouse '@unkey/db': specifier: workspace:^ version: link:../../internal/db @@ -163,7 +163,7 @@ importers: version: 0.3.9 '@clerk/nextjs': specifier: ^4.29.10 - version: 4.29.10(next@15.0.2)(react-dom@18.3.1)(react@18.3.1) + version: 4.29.10(next@14.2.10)(react-dom@18.3.1)(react@18.3.1) '@mendable/firecrawl-js': specifier: ^1.5.2 version: 1.5.2(ws@8.18.0) @@ -172,7 +172,7 @@ importers: version: 1.18.0 '@trigger.dev/nextjs': specifier: 3.1.0 - version: 3.1.0(@trigger.dev/sdk@3.1.0)(next@15.0.2) + version: 3.1.0(@trigger.dev/sdk@3.1.0)(next@14.2.10) '@trigger.dev/sdk': specifier: 3.1.0 version: 3.1.0 @@ -199,7 +199,7 @@ importers: version: link:../../internal/schema ai: specifier: ^3.4.7 - version: 3.4.7(react@18.3.1)(svelte@5.1.9)(vue@3.5.12)(zod@3.23.8) + version: 3.4.7(openai@4.52.1)(react@18.3.1)(svelte@5.1.9)(vue@3.5.12)(zod@3.23.8) drizzle-orm: specifier: ^0.33.0 version: 0.33.0(@opentelemetry/api@1.4.1)(@planetscale/database@1.18.0)(@types/react@18.3.11)(react@18.3.1) @@ -333,9 +333,9 @@ importers: '@unkey/billing': specifier: workspace:^ version: link:../../internal/billing - '@unkey/clickhouse-zod': + '@unkey/clickhouse': specifier: workspace:^ - version: link:../../internal/clickhouse-zod + version: link:../../internal/clickhouse '@unkey/db': specifier: workspace:^ version: link:../../internal/db @@ -437,7 +437,7 @@ importers: version: 3.6.0 drizzle-orm: specifier: generated - version: 0.32.0-aaf764c(@planetscale/database@1.18.0)(@types/react@18.3.11)(react@18.3.1) + version: 0.32.0-aaf764c(@opentelemetry/api@1.4.1)(@planetscale/database@1.18.0)(@types/react@18.3.11)(react@18.3.1) export-to-csv: specifier: ^1.4.0 version: 1.4.0 @@ -766,7 +766,7 @@ importers: version: 0.3.0(react-dom@18.3.1)(react@18.3.1) posthog-js: specifier: ^1.130.1 - version: 1.130.1 + version: 1.179.0 prism-react-renderer: specifier: ^2.3.1 version: 2.3.1(react@18.3.1) @@ -863,7 +863,7 @@ importers: version: link:../../internal/worker-logging ai: specifier: ^3.0.23 - version: 3.4.7(openai@4.52.1)(react@18.3.1)(svelte@5.1.8)(vue@3.5.12)(zod@3.23.8) + version: 3.4.7(openai@4.52.1)(react@18.3.1)(svelte@5.1.9)(vue@3.5.12)(zod@3.23.8) drizzle-orm: specifier: generated version: 0.32.0-aaf764c(@cloudflare/workers-types@4.20240603.0)(@planetscale/database@1.18.0)(react@18.3.1) @@ -942,7 +942,7 @@ importers: version: 0.32.0-aaf764c(@opentelemetry/api@1.4.1)(@planetscale/database@1.18.0)(@types/react@18.3.11)(react@18.3.1) next: specifier: 14.2.10 - version: 14.2.10(@opentelemetry/api@1.4.1)(react-dom@18.3.1)(react@18.3.1) + version: 14.2.10(@babel/core@7.26.0)(@opentelemetry/api@1.4.1)(react-dom@18.3.1)(react@18.3.1) react: specifier: ^18 version: 18.3.1 @@ -1042,7 +1042,7 @@ importers: version: 3.6.0 drizzle-orm: specifier: generated - version: 0.32.0-aaf764c(@planetscale/database@1.18.0)(@types/react@18.3.11)(react@18.3.1) + version: 0.32.0-aaf764c(@opentelemetry/api@1.4.1)(@planetscale/database@1.18.0)(@types/react@18.3.11)(react@18.3.1) framer-motion: specifier: 11.0.23 version: 11.0.23(react-dom@18.3.1)(react@18.3.1) @@ -1175,7 +1175,7 @@ importers: version: link:../../packages/error ai: specifier: ^3.0.23 - version: 3.4.7(openai@4.52.1)(react@18.3.1)(svelte@5.1.8)(vue@3.5.12)(zod@3.23.8) + version: 3.4.7(openai@4.52.1)(react@18.3.1)(svelte@5.1.9)(vue@3.5.12)(zod@3.23.8) zod: specifier: ^3.23.5 version: 3.23.8 @@ -1197,15 +1197,15 @@ importers: devDependencies: checkly: specifier: latest - version: 4.7.0(@types/node@22.8.6)(typescript@5.5.3) + version: 4.9.1(@types/node@20.14.9)(typescript@5.5.3) ts-node: specifier: 10.9.1 - version: 10.9.1(@types/node@22.8.6)(typescript@5.5.3) + version: 10.9.1(@types/node@20.14.9)(typescript@5.5.3) typescript: specifier: 5.5.3 version: 5.5.3 - internal/clickhouse-zod: + internal/clickhouse: dependencies: '@clickhouse/client-web': specifier: ^1.6.0 @@ -1418,7 +1418,7 @@ importers: version: 18.3.1 react-email: specifier: 2.1.1 - version: 2.1.1(@babel/core@7.26.0)(eslint@9.13.0)(ts-node@10.9.2) + version: 2.1.1(@babel/core@7.26.0)(eslint@9.14.0)(ts-node@10.9.2) resend: specifier: ^4.0.0 version: 4.0.0(react-dom@18.3.1)(react@18.3.1) @@ -1697,6 +1697,9 @@ importers: '@chronark/zod-bird': specifier: ^0.3.9 version: 0.3.9 + '@unkey/clickhouse': + specifier: workspace:^ + version: link:../../internal/clickhouse '@unkey/db': specifier: workspace:^ version: link:../../internal/db @@ -1706,6 +1709,9 @@ importers: base-x: specifier: ^4.0.0 version: 4.0.0 + ms: + specifier: ^2.1.3 + version: 2.1.3 mysql2: specifier: ^3.10.3 version: 3.10.3 @@ -1719,6 +1725,9 @@ importers: '@planetscale/database': specifier: ^1.18.0 version: 1.18.0 + '@types/ms': + specifier: ^0.7.34 + version: 0.7.34 '@types/node': specifier: 20.14.9 version: 20.14.9 @@ -1875,23 +1884,6 @@ packages: - zod dev: false - /@ai-sdk/svelte@0.0.51(svelte@5.1.8)(zod@3.23.8): - resolution: {integrity: sha512-aIZJaIds+KpCt19yUDCRDWebzF/17GCY7gN9KkcA2QM6IKRO5UmMcqEYja0ZmwFQPm1kBZkF2njhr8VXis2mAw==} - engines: {node: '>=18'} - peerDependencies: - svelte: ^3.0.0 || ^4.0.0 - peerDependenciesMeta: - svelte: - optional: true - dependencies: - '@ai-sdk/provider-utils': 1.0.20(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.46(zod@3.23.8) - sswr: 2.1.0(svelte@5.1.8) - svelte: 5.1.8 - transitivePeerDependencies: - - zod - dev: false - /@ai-sdk/svelte@0.0.51(svelte@5.1.9)(zod@3.23.8): resolution: {integrity: sha512-aIZJaIds+KpCt19yUDCRDWebzF/17GCY7gN9KkcA2QM6IKRO5UmMcqEYja0ZmwFQPm1kBZkF2njhr8VXis2mAw==} engines: {node: '>=18'} @@ -2000,7 +1992,7 @@ packages: resolution: {integrity: sha512-DVhcFSQ8YQnMNW34Mk8BSsfc61iC1sAnmcfYoXTAshYHuU50p/6b7x3QYaGctDNKWGvi1ub7mPcSY0bK+aN0qg==} dependencies: '@antv/util': 2.0.17 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/attr@0.3.5: @@ -2009,14 +2001,14 @@ packages: '@antv/color-util': 2.0.6 '@antv/scale': 0.3.18 '@antv/util': 2.0.17 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/color-util@2.0.6: resolution: {integrity: sha512-KnPEaAH+XNJMjax9U35W67nzPI+QQ2x27pYlzmSIWrbj4/k8PGrARXfzDTjwoozHJY8qG62Z+Ww6Alhu2FctXQ==} dependencies: '@antv/util': 2.0.17 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/component@0.8.35: @@ -2030,7 +2022,7 @@ packages: '@antv/scale': 0.3.18 '@antv/util': 2.0.17 fecha: 4.2.3 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/coord@0.3.1: @@ -2038,13 +2030,13 @@ packages: dependencies: '@antv/matrix-util': 3.1.0-beta.3 '@antv/util': 2.0.17 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/dom-util@2.0.4: resolution: {integrity: sha512-2shXUl504fKwt82T3GkuT4Uoc6p9qjCKnJ8gXGLSW4T1W37dqf9AV28aCfoVPHp2BUXpSsB+PAJX2rG/jLHsLQ==} dependencies: - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/event-emitter@0.1.3: @@ -2064,7 +2056,7 @@ packages: d3-interpolate: 3.0.1 d3-timer: 1.0.10 detect-browser: 5.3.0 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/g-canvas@0.5.17: @@ -2076,7 +2068,7 @@ packages: '@antv/path-util': 2.0.15 '@antv/util': 2.0.17 gl-matrix: 3.4.3 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/g-math@0.1.9: @@ -2093,7 +2085,7 @@ packages: '@antv/g-math': 0.1.9 '@antv/util': 2.0.17 detect-browser: 5.3.0 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/g-webgpu-core@0.5.6: @@ -2184,7 +2176,7 @@ packages: '@antv/path-util': 2.0.15 '@antv/scale': 0.3.18 '@antv/util': 2.0.17 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/g2plot@2.4.32: @@ -2203,7 +2195,7 @@ packages: fmin: 0.0.2 pdfast: 0.2.0 size-sensor: 1.0.2 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/g6-core@0.8.24: @@ -2218,7 +2210,7 @@ packages: '@antv/path-util': 2.0.15 '@antv/util': 2.0.17 ml-matrix: 6.12.0 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/g6-element@0.8.24(@antv/g6@4.8.24): @@ -2230,7 +2222,7 @@ packages: '@antv/g6': 4.8.24 '@antv/g6-core': 0.8.24 '@antv/util': 2.0.17 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/g6-pc@0.8.24(@antv/g6@4.8.24): @@ -2257,7 +2249,7 @@ packages: dagre: 0.8.5 insert-css: 2.0.0 ml-matrix: 6.12.0 - tslib: 2.8.0 + tslib: 2.8.1 transitivePeerDependencies: - '@antv/g6' dev: false @@ -2323,7 +2315,7 @@ packages: dependencies: '@antv/util': 2.0.17 gl-matrix: 3.4.3 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/matrix-util@3.1.0-beta.3: @@ -2331,7 +2323,7 @@ packages: dependencies: '@antv/util': 2.0.17 gl-matrix: 3.4.3 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/path-util@2.0.15: @@ -2339,7 +2331,7 @@ packages: dependencies: '@antv/matrix-util': 3.0.4 '@antv/util': 2.0.17 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/path-util@3.0.1: @@ -2347,7 +2339,7 @@ packages: dependencies: gl-matrix: 3.4.3 lodash-es: 4.17.21 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/scale@0.3.18: @@ -2355,14 +2347,14 @@ packages: dependencies: '@antv/util': 2.0.17 fecha: 4.2.3 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/util@2.0.17: resolution: {integrity: sha512-o6I9hi5CIUvLGDhth0RxNSFDRwXeywmt6ExR4+RmVAzIi48ps6HUy+svxOCayvrPBN37uE6TAc2KDofRo0nK9Q==} dependencies: csstype: 3.1.3 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@antv/util@3.3.10: @@ -2370,7 +2362,7 @@ packages: dependencies: fast-deep-equal: 3.1.3 gl-matrix: 3.4.3 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@apidevtools/json-schema-ref-parser@9.0.6: @@ -2422,16 +2414,16 @@ packages: uuid: 8.3.2 dev: false - /@babel/code-frame@7.26.0: - resolution: {integrity: sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==} + /@babel/code-frame@7.26.2: + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-validator-identifier': 7.25.9 js-tokens: 4.0.0 picocolors: 1.1.1 - /@babel/compat-data@7.26.0: - resolution: {integrity: sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA==} + /@babel/compat-data@7.26.2: + resolution: {integrity: sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==} engines: {node: '>=6.9.0'} /@babel/core@7.26.0: @@ -2439,12 +2431,12 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.26.0 - '@babel/generator': 7.26.0 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.2 '@babel/helper-compilation-targets': 7.25.9 '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) '@babel/helpers': 7.26.0 - '@babel/parser': 7.26.1 + '@babel/parser': 7.26.2 '@babel/template': 7.25.9 '@babel/traverse': 7.25.9 '@babel/types': 7.26.0 @@ -2456,11 +2448,11 @@ packages: transitivePeerDependencies: - supports-color - /@babel/generator@7.26.0: - resolution: {integrity: sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==} + /@babel/generator@7.26.2: + resolution: {integrity: sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/parser': 7.26.1 + '@babel/parser': 7.26.2 '@babel/types': 7.26.0 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 @@ -2470,7 +2462,7 @@ packages: resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/compat-data': 7.26.0 + '@babel/compat-data': 7.26.2 '@babel/helper-validator-option': 7.25.9 browserslist: 4.24.2 lru-cache: 5.1.1 @@ -2525,20 +2517,12 @@ packages: '@babel/types': 7.26.0 dev: false - /@babel/parser@7.26.1: - resolution: {integrity: sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.26.0 - /@babel/parser@7.26.2: resolution: {integrity: sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==} engines: {node: '>=6.0.0'} hasBin: true dependencies: '@babel/types': 7.26.0 - dev: false /@babel/runtime@7.26.0: resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} @@ -2550,17 +2534,17 @@ packages: resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.26.0 - '@babel/parser': 7.26.1 + '@babel/code-frame': 7.26.2 + '@babel/parser': 7.26.2 '@babel/types': 7.26.0 /@babel/traverse@7.25.9: resolution: {integrity: sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.26.0 - '@babel/generator': 7.26.0 - '@babel/parser': 7.26.1 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.2 + '@babel/parser': 7.26.2 '@babel/template': 7.25.9 '@babel/types': 7.26.0 debug: 4.3.7(supports-color@8.1.1) @@ -2946,27 +2930,7 @@ packages: '@clerk/clerk-sdk-node': 4.13.12(react@18.3.1) '@clerk/shared': 1.4.0(react@18.3.1) '@clerk/types': 3.63.0 - next: 14.2.10(@opentelemetry/api@1.4.1)(react-dom@18.3.1)(react@18.3.1) - path-to-regexp: 6.2.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - tslib: 2.4.1 - dev: false - - /@clerk/nextjs@4.29.10(next@15.0.2)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-nu8tIzZxxwqFYupBp66BSYSp+qAEUvGUFJ1eAqvgQjOUWh+rElQTfOMI0sm3hOX85sZv04WKA9hiAT93qLILsg==} - engines: {node: '>=14'} - peerDependencies: - next: '>=10' - react: ^17.0.2 || ^18.0.0-0 - react-dom: ^17.0.2 || ^18.0.0-0 - dependencies: - '@clerk/backend': 0.38.4(react@18.3.1) - '@clerk/clerk-react': 4.30.8(react@18.3.1) - '@clerk/clerk-sdk-node': 4.13.12(react@18.3.1) - '@clerk/shared': 1.4.0(react@18.3.1) - '@clerk/types': 3.63.0 - next: 15.0.2(@opentelemetry/api@1.4.1)(react-dom@18.3.1)(react@18.3.1) + next: 14.2.10(@babel/core@7.26.0)(@opentelemetry/api@1.4.1)(react-dom@18.3.1)(react@18.3.1) path-to-regexp: 6.2.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -3239,7 +3203,7 @@ packages: peerDependencies: typescript: ^5.0.2 dependencies: - '@parcel/watcher': 2.4.1 + '@parcel/watcher': 2.5.0 camelcase: 8.0.0 esbuild: 0.21.5 gray-matter: 4.0.3 @@ -3307,14 +3271,14 @@ packages: engines: {node: '>=10.0.0'} dev: false - /@drizzle-team/brocli@0.10.1: - resolution: {integrity: sha512-AHy0vjc+n/4w/8Mif+w86qpppHuF3AyXbcWW+R/W7GNA3F5/p2nuhlkCJaTXSLZheB4l1rtHzOfr9A7NwoR/Zg==} + /@drizzle-team/brocli@0.10.2: + resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} dev: true /@electric-sql/client@0.6.3: resolution: {integrity: sha512-/AYkRrEASKIGcjtNp8IVJ3sAUm+IQ2l0NrGgDvvAG/n1+ifOl7kD1E4dRyg1qdY/b+HdKhGNYlNgsPuwMKO2Mg==} optionalDependencies: - '@rollup/rollup-darwin-arm64': 4.24.3 + '@rollup/rollup-darwin-arm64': 4.24.4 dev: false /@emnapi/runtime@1.3.1: @@ -4818,13 +4782,13 @@ packages: requiresBuild: true optional: true - /@eslint-community/eslint-utils@4.4.1(eslint@9.13.0): + /@eslint-community/eslint-utils@4.4.1(eslint@9.14.0): resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 9.13.0 + eslint: 9.14.0 eslint-visitor-keys: 3.4.3 dev: false @@ -4866,8 +4830,8 @@ packages: - supports-color dev: false - /@eslint/js@9.13.0: - resolution: {integrity: sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==} + /@eslint/js@9.14.0: + resolution: {integrity: sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dev: false @@ -4901,8 +4865,8 @@ packages: dependencies: '@floating-ui/utils': 0.2.8 - /@floating-ui/dom@1.6.11: - resolution: {integrity: sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==} + /@floating-ui/dom@1.6.12: + resolution: {integrity: sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==} dependencies: '@floating-ui/core': 1.6.8 '@floating-ui/utils': 0.2.8 @@ -4913,17 +4877,17 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' dependencies: - '@floating-ui/dom': 1.6.11 + '@floating-ui/dom': 1.6.12 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) /@floating-ui/utils@0.2.8: resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==} - /@formatjs/intl-localematcher@0.5.6: - resolution: {integrity: sha512-roz1+Ba5e23AHX6KUAWmLEyTRZegM5YDuxuvkHCyK3RJddf/UXB2f+s7pOMm9ktfPGla0g+mQXOn5vsuYirnaA==} + /@formatjs/intl-localematcher@0.5.7: + resolution: {integrity: sha512-GGFtfHGQVFe/niOZp24Kal5b2i36eE2bNL0xi9Sg/yd0TR8aLjcteApZdHmismP5QQax1cMnZM9yWySUUjJteA==} dependencies: - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@google-cloud/precise-date@4.0.0: @@ -5012,6 +4976,11 @@ packages: engines: {node: '>=18.18'} dev: false + /@humanwhocodes/retry@0.4.0: + resolution: {integrity: sha512-xnRgu9DxZbkWak/te3fcytNyp8MTbuiZIaueg2rgEvBuN55n04nwLYLU9TX/VVlusc9L2ZNXi99nUFNkHXtr5g==} + engines: {node: '>=18.18'} + dev: false + /@img/sharp-darwin-arm64@0.33.4: resolution: {integrity: sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA==} engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5022,17 +4991,6 @@ packages: '@img/sharp-libvips-darwin-arm64': 1.0.2 optional: true - /@img/sharp-darwin-arm64@0.33.5: - resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [darwin] - requiresBuild: true - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.0.4 - dev: false - optional: true - /@img/sharp-darwin-x64@0.33.4: resolution: {integrity: sha512-0l7yRObwtTi82Z6ebVI2PnHT8EB2NxBgpK2MiKJZJ7cz32R4lxd001ecMhzzsZig3Yv9oclvqqdV93jo9hy+Dw==} engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5043,17 +5001,6 @@ packages: '@img/sharp-libvips-darwin-x64': 1.0.2 optional: true - /@img/sharp-darwin-x64@0.33.5: - resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [darwin] - requiresBuild: true - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.0.4 - dev: false - optional: true - /@img/sharp-libvips-darwin-arm64@1.0.2: resolution: {integrity: sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==} engines: {macos: '>=11', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5062,14 +5009,6 @@ packages: requiresBuild: true optional: true - /@img/sharp-libvips-darwin-arm64@1.0.4: - resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: false - optional: true - /@img/sharp-libvips-darwin-x64@1.0.2: resolution: {integrity: sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==} engines: {macos: '>=10.13', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5078,14 +5017,6 @@ packages: requiresBuild: true optional: true - /@img/sharp-libvips-darwin-x64@1.0.4: - resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: false - optional: true - /@img/sharp-libvips-linux-arm64@1.0.2: resolution: {integrity: sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==} engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5094,14 +5025,6 @@ packages: requiresBuild: true optional: true - /@img/sharp-libvips-linux-arm64@1.0.4: - resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: false - optional: true - /@img/sharp-libvips-linux-arm@1.0.2: resolution: {integrity: sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==} engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5110,14 +5033,6 @@ packages: requiresBuild: true optional: true - /@img/sharp-libvips-linux-arm@1.0.5: - resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: false - optional: true - /@img/sharp-libvips-linux-s390x@1.0.2: resolution: {integrity: sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==} engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5126,14 +5041,6 @@ packages: requiresBuild: true optional: true - /@img/sharp-libvips-linux-s390x@1.0.4: - resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: false - optional: true - /@img/sharp-libvips-linux-x64@1.0.2: resolution: {integrity: sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==} engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5142,14 +5049,6 @@ packages: requiresBuild: true optional: true - /@img/sharp-libvips-linux-x64@1.0.4: - resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: false - optional: true - /@img/sharp-libvips-linuxmusl-arm64@1.0.2: resolution: {integrity: sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==} engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5158,14 +5057,6 @@ packages: requiresBuild: true optional: true - /@img/sharp-libvips-linuxmusl-arm64@1.0.4: - resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: false - optional: true - /@img/sharp-libvips-linuxmusl-x64@1.0.2: resolution: {integrity: sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==} engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5174,14 +5065,6 @@ packages: requiresBuild: true optional: true - /@img/sharp-libvips-linuxmusl-x64@1.0.4: - resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: false - optional: true - /@img/sharp-linux-arm64@0.33.4: resolution: {integrity: sha512-2800clwVg1ZQtxwSoTlHvtm9ObgAax7V6MTAB/hDT945Tfyy3hVkmiHpeLPCKYqYR1Gcmv1uDZ3a4OFwkdBL7Q==} engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5192,17 +5075,6 @@ packages: '@img/sharp-libvips-linux-arm64': 1.0.2 optional: true - /@img/sharp-linux-arm64@0.33.5: - resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - requiresBuild: true - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.0.4 - dev: false - optional: true - /@img/sharp-linux-arm@0.33.4: resolution: {integrity: sha512-RUgBD1c0+gCYZGCCe6mMdTiOFS0Zc/XrN0fYd6hISIKcDUbAW5NtSQW9g/powkrXYm6Vzwd6y+fqmExDuCdHNQ==} engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5213,17 +5085,6 @@ packages: '@img/sharp-libvips-linux-arm': 1.0.2 optional: true - /@img/sharp-linux-arm@0.33.5: - resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm] - os: [linux] - requiresBuild: true - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.0.5 - dev: false - optional: true - /@img/sharp-linux-s390x@0.33.4: resolution: {integrity: sha512-h3RAL3siQoyzSoH36tUeS0PDmb5wINKGYzcLB5C6DIiAn2F3udeFAum+gj8IbA/82+8RGCTn7XW8WTFnqag4tQ==} engines: {glibc: '>=2.31', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5234,17 +5095,6 @@ packages: '@img/sharp-libvips-linux-s390x': 1.0.2 optional: true - /@img/sharp-linux-s390x@0.33.5: - resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [s390x] - os: [linux] - requiresBuild: true - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.0.4 - dev: false - optional: true - /@img/sharp-linux-x64@0.33.4: resolution: {integrity: sha512-GoR++s0XW9DGVi8SUGQ/U4AeIzLdNjHka6jidVwapQ/JebGVQIpi52OdyxCNVRE++n1FCLzjDovJNozif7w/Aw==} engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5255,17 +5105,6 @@ packages: '@img/sharp-libvips-linux-x64': 1.0.2 optional: true - /@img/sharp-linux-x64@0.33.5: - resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - requiresBuild: true - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.0.4 - dev: false - optional: true - /@img/sharp-linuxmusl-arm64@0.33.4: resolution: {integrity: sha512-nhr1yC3BlVrKDTl6cO12gTpXMl4ITBUZieehFvMntlCXFzH2bvKG76tBL2Y/OqhupZt81pR7R+Q5YhJxW0rGgQ==} engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5276,17 +5115,6 @@ packages: '@img/sharp-libvips-linuxmusl-arm64': 1.0.2 optional: true - /@img/sharp-linuxmusl-arm64@0.33.5: - resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - requiresBuild: true - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 - dev: false - optional: true - /@img/sharp-linuxmusl-x64@0.33.4: resolution: {integrity: sha512-uCPTku0zwqDmZEOi4ILyGdmW76tH7dm8kKlOIV1XC5cLyJ71ENAAqarOHQh0RLfpIpbV5KOpXzdU6XkJtS0daw==} engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5297,17 +5125,6 @@ packages: '@img/sharp-libvips-linuxmusl-x64': 1.0.2 optional: true - /@img/sharp-linuxmusl-x64@0.33.5: - resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - requiresBuild: true - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.0.4 - dev: false - optional: true - /@img/sharp-wasm32@0.33.4: resolution: {integrity: sha512-Bmmauh4sXUsUqkleQahpdNXKvo+wa1V9KhT2pDA4VJGKwnKMJXiSTGphn0gnJrlooda0QxCtXc6RX1XAU6hMnQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5317,16 +5134,6 @@ packages: '@emnapi/runtime': 1.3.1 optional: true - /@img/sharp-wasm32@0.33.5: - resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [wasm32] - requiresBuild: true - dependencies: - '@emnapi/runtime': 1.3.1 - dev: false - optional: true - /@img/sharp-win32-ia32@0.33.4: resolution: {integrity: sha512-99SJ91XzUhYHbx7uhK3+9Lf7+LjwMGQZMDlO/E/YVJ7Nc3lyDFZPGhjwiYdctoH2BOzW9+TnfqcaMKt0jHLdqw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5335,15 +5142,6 @@ packages: requiresBuild: true optional: true - /@img/sharp-win32-ia32@0.33.5: - resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: false - optional: true - /@img/sharp-win32-x64@0.33.4: resolution: {integrity: sha512-3QLocdTRVIrFNye5YocZl+KKpYKP+fksi1QhmOArgx7GyhIbQp/WrJRu176jm8IxromS7RIkzMiMINVdBtC8Aw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} @@ -5352,15 +5150,6 @@ packages: requiresBuild: true optional: true - /@img/sharp-win32-x64@0.33.5: - resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: false - optional: true - /@inquirer/confirm@3.2.0: resolution: {integrity: sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==} engines: {node: '>=18'} @@ -5376,7 +5165,7 @@ packages: '@inquirer/figures': 1.0.7 '@inquirer/type': 2.0.0 '@types/mute-stream': 0.0.4 - '@types/node': 22.8.6 + '@types/node': 22.9.0 '@types/wrap-ansi': 3.0.0 ansi-escapes: 4.3.2 cli-width: 4.1.0 @@ -5533,7 +5322,7 @@ packages: /@libsql/isomorphic-ws@0.1.5: resolution: {integrity: sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg==} dependencies: - '@types/ws': 8.5.12 + '@types/ws': 8.5.13 ws: 8.18.0 transitivePeerDependencies: - bufferutil @@ -5725,7 +5514,7 @@ packages: react-dom: 18.3.1(react@18.3.1) react-draggable: 4.4.6(react-dom@18.3.1)(react@18.3.1) react-reconciler: 0.29.2(react@18.3.1) - unplugin: 1.14.1 + unplugin: 1.15.0 zustand: 4.5.5(@types/react@18.3.11)(react@18.3.1) transitivePeerDependencies: - '@types/react' @@ -6005,10 +5794,6 @@ packages: /@next/env@14.2.10: resolution: {integrity: sha512-dZIu93Bf5LUtluBXIv4woQw2cZVZ2DJTjax5/5DOs3lzEOeKLy7GxRSr4caK9/SCPdaW6bCgpye6+n4Dh9oJPw==} - /@next/env@15.0.2: - resolution: {integrity: sha512-c0Zr0ModK5OX7D4ZV8Jt/wqoXtitLNPwUfG9zElCZztdaZyNVnN40rDXVZ/+FGuR4CcNV5AEfM6N8f+Ener7Dg==} - dev: false - /@next/swc-darwin-arm64@14.1.0: resolution: {integrity: sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==} engines: {node: '>= 10'} @@ -6026,15 +5811,6 @@ packages: requiresBuild: true optional: true - /@next/swc-darwin-arm64@15.0.2: - resolution: {integrity: sha512-GK+8w88z+AFlmt+ondytZo2xpwlfAR8U6CRwXancHImh6EdGfHMIrTSCcx5sOSBei00GyLVL0ioo1JLKTfprgg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: false - optional: true - /@next/swc-darwin-x64@14.1.0: resolution: {integrity: sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==} engines: {node: '>= 10'} @@ -6052,15 +5828,6 @@ packages: requiresBuild: true optional: true - /@next/swc-darwin-x64@15.0.2: - resolution: {integrity: sha512-KUpBVxIbjzFiUZhiLIpJiBoelqzQtVZbdNNsehhUn36e2YzKHphnK8eTUW1s/4aPy5kH/UTid8IuVbaOpedhpw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: false - optional: true - /@next/swc-linux-arm64-gnu@14.1.0: resolution: {integrity: sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==} engines: {node: '>= 10'} @@ -6078,15 +5845,6 @@ packages: requiresBuild: true optional: true - /@next/swc-linux-arm64-gnu@15.0.2: - resolution: {integrity: sha512-9J7TPEcHNAZvwxXRzOtiUvwtTD+fmuY0l7RErf8Yyc7kMpE47MIQakl+3jecmkhOoIyi/Rp+ddq7j4wG6JDskQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: false - optional: true - /@next/swc-linux-arm64-musl@14.1.0: resolution: {integrity: sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==} engines: {node: '>= 10'} @@ -6104,15 +5862,6 @@ packages: requiresBuild: true optional: true - /@next/swc-linux-arm64-musl@15.0.2: - resolution: {integrity: sha512-BjH4ZSzJIoTTZRh6rG+a/Ry4SW0HlizcPorqNBixBWc3wtQtj4Sn9FnRZe22QqrPnzoaW0ctvSz4FaH4eGKMww==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: false - optional: true - /@next/swc-linux-x64-gnu@14.1.0: resolution: {integrity: sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==} engines: {node: '>= 10'} @@ -6130,15 +5879,6 @@ packages: requiresBuild: true optional: true - /@next/swc-linux-x64-gnu@15.0.2: - resolution: {integrity: sha512-i3U2TcHgo26sIhcwX/Rshz6avM6nizrZPvrDVDY1bXcLH1ndjbO8zuC7RoHp0NSK7wjJMPYzm7NYL1ksSKFreA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: false - optional: true - /@next/swc-linux-x64-musl@14.1.0: resolution: {integrity: sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==} engines: {node: '>= 10'} @@ -6156,15 +5896,6 @@ packages: requiresBuild: true optional: true - /@next/swc-linux-x64-musl@15.0.2: - resolution: {integrity: sha512-AMfZfSVOIR8fa+TXlAooByEF4OB00wqnms1sJ1v+iu8ivwvtPvnkwdzzFMpsK5jA2S9oNeeQ04egIWVb4QWmtQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: false - optional: true - /@next/swc-win32-arm64-msvc@14.1.0: resolution: {integrity: sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==} engines: {node: '>= 10'} @@ -6182,15 +5913,6 @@ packages: requiresBuild: true optional: true - /@next/swc-win32-arm64-msvc@15.0.2: - resolution: {integrity: sha512-JkXysDT0/hEY47O+Hvs8PbZAeiCQVxKfGtr4GUpNAhlG2E0Mkjibuo8ryGD29Qb5a3IOnKYNoZlh/MyKd2Nbww==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: false - optional: true - /@next/swc-win32-ia32-msvc@14.1.0: resolution: {integrity: sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==} engines: {node: '>= 10'} @@ -6225,15 +5947,6 @@ packages: requiresBuild: true optional: true - /@next/swc-win32-x64-msvc@15.0.2: - resolution: {integrity: sha512-foaUL0NqJY/dX0Pi/UcZm5zsmSk5MtP/gxx3xOPyREkMFN+CTjctPfu3QaqrQHinaKdPnMWPJDKt4VjDfTBe/Q==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: false - optional: true - /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -6281,7 +5994,7 @@ packages: chalk: 4.1.2 strip-ansi: 6.0.1 supports-color: 8.1.1 - tslib: 2.8.0 + tslib: 2.8.1 dev: true /@oclif/core@1.26.2: @@ -6313,12 +6026,12 @@ packages: strip-ansi: 6.0.1 supports-color: 8.1.1 supports-hyperlinks: 2.3.0 - tslib: 2.8.0 + tslib: 2.8.1 widest-line: 3.1.0 wrap-ansi: 7.0.0 dev: true - /@oclif/core@2.8.11(@types/node@22.8.6)(typescript@5.5.3): + /@oclif/core@2.8.11(@types/node@20.14.9)(typescript@5.5.3): resolution: {integrity: sha512-9wYW6KRSWfB/D+tqeyl/jxmEz/xPXkFJGVWfKaptqHz6FPWNJREjAM945MuJL2Y8NRhMe+ScRlZ3WpdToX5aVQ==} engines: {node: '>=14.0.0'} dependencies: @@ -6346,8 +6059,8 @@ packages: strip-ansi: 6.0.1 supports-color: 8.1.1 supports-hyperlinks: 2.3.0 - ts-node: 10.9.1(@types/node@22.8.6)(typescript@5.5.3) - tslib: 2.8.0 + ts-node: 10.9.2(@types/node@20.14.9)(typescript@5.5.3) + tslib: 2.8.1 widest-line: 3.1.0 wordwrap: 1.0.0 wrap-ansi: 7.0.0 @@ -6358,35 +6071,25 @@ packages: - typescript dev: true - /@oclif/core@3.27.0: - resolution: {integrity: sha512-Fg93aNFvXzBq5L7ztVHFP2nYwWU1oTCq48G0TjF/qC1UN36KWa2H5Hsm72kERd5x/sjy2M2Tn4kDEorUlpXOlw==} + /@oclif/core@4.0.31: + resolution: {integrity: sha512-7oyIZv/C1TP+fPc2tSzVPYqG1zU+nel1QvJxjAWyVhud0J8B5SpKZnryedxs3nlSVPJ6K1MT31C9esupCBYgZw==} engines: {node: '>=18.0.0'} dependencies: - '@types/cli-progress': 3.11.6 ansi-escapes: 4.3.2 - ansi-styles: 4.3.0 - cardinal: 2.1.1 - chalk: 4.1.2 + ansis: 3.3.2 clean-stack: 3.0.1 - cli-progress: 3.12.0 - color: 4.2.3 + cli-spinners: 2.9.2 debug: 4.3.7(supports-color@8.1.1) ejs: 3.1.10 get-package-type: 0.1.0 globby: 11.1.0 - hyperlinker: 1.0.0 indent-string: 4.0.0 is-wsl: 2.2.0 - js-yaml: 3.14.1 + lilconfig: 3.1.2 minimatch: 9.0.5 - natural-orderby: 2.0.3 - object-treeify: 1.1.33 - password-prompt: 1.1.3 - slice-ansi: 4.0.0 + semver: 7.6.3 string-width: 4.2.3 - strip-ansi: 6.0.1 supports-color: 8.1.1 - supports-hyperlinks: 2.3.0 widest-line: 3.1.0 wordwrap: 1.0.0 wrap-ansi: 7.0.0 @@ -6403,12 +6106,12 @@ packages: '@oclif/core': 1.26.2 dev: true - /@oclif/plugin-not-found@2.3.23(@types/node@22.8.6)(typescript@5.5.3): + /@oclif/plugin-not-found@2.3.23(@types/node@20.14.9)(typescript@5.5.3): resolution: {integrity: sha512-UZM8aolxXvqwH8WcmJxRNASDWgMoSQm/pgCdkc1AGCRevYc8+LBSO+U6nLWq+Dx8H/dn9RyIv5oiUIOGkKDlZA==} engines: {node: '>=12.0.0'} dependencies: '@oclif/color': 1.0.13 - '@oclif/core': 2.8.11(@types/node@22.8.6)(typescript@5.5.3) + '@oclif/core': 2.8.11(@types/node@20.14.9)(typescript@5.5.3) fast-levenshtein: 3.0.0 lodash: 4.17.21 transitivePeerDependencies: @@ -6418,28 +6121,30 @@ packages: - typescript dev: true - /@oclif/plugin-plugins@4.1.12: - resolution: {integrity: sha512-lYNoqoQJz+p4AOMZ9N5k7OkLk/HZJSdaybJ4rWfDZ76pQ7AcrLEPtREi2wLfcwcrzKoBMsrwBoMTf3PnmpW7ZQ==} + /@oclif/plugin-plugins@5.4.4: + resolution: {integrity: sha512-p30fo3JPtbOqTJOX9A/8qKV/14XWt8xFgG/goVfIkuKBAO+cdY78ag8pYatlpzsYzJhO27X1MFn0WkkPWo36Ww==} engines: {node: '>=18.0.0'} dependencies: - '@oclif/core': 3.27.0 - chalk: 5.3.0 + '@oclif/core': 4.0.31 + ansis: 3.3.2 debug: 4.3.7(supports-color@8.1.1) - npm: 10.2.3 - npm-run-path: 4.0.1 + npm: 10.9.0 + npm-package-arg: 11.0.3 + npm-run-path: 5.3.0 + object-treeify: 4.0.1 semver: 7.6.3 - shelljs: 0.8.5 validate-npm-package-name: 5.0.1 + which: 4.0.0 yarn: 1.22.22 transitivePeerDependencies: - supports-color dev: true - /@oclif/plugin-warn-if-update-available@2.0.24(@types/node@22.8.6)(typescript@5.5.3): + /@oclif/plugin-warn-if-update-available@2.0.24(@types/node@20.14.9)(typescript@5.5.3): resolution: {integrity: sha512-Rq8/EZ8wQawvPWS6W59Zhf/zSz/umLc3q75I1ybi7pul6YMNwf/E1eDVHytSUEQ6yQV+p3cCs034IItz4CVdjw==} engines: {node: '>=12.0.0'} dependencies: - '@oclif/core': 2.8.11(@types/node@22.8.6)(typescript@5.5.3) + '@oclif/core': 2.8.11(@types/node@20.14.9)(typescript@5.5.3) chalk: 4.1.2 debug: 4.3.7(supports-color@8.1.1) fs-extra: 9.1.0 @@ -7107,8 +6812,8 @@ packages: engines: {node: '>=14'} dev: false - /@parcel/watcher-android-arm64@2.4.1: - resolution: {integrity: sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==} + /@parcel/watcher-android-arm64@2.5.0: + resolution: {integrity: sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [android] @@ -7116,8 +6821,8 @@ packages: dev: true optional: true - /@parcel/watcher-darwin-arm64@2.4.1: - resolution: {integrity: sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==} + /@parcel/watcher-darwin-arm64@2.5.0: + resolution: {integrity: sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [darwin] @@ -7125,8 +6830,8 @@ packages: dev: true optional: true - /@parcel/watcher-darwin-x64@2.4.1: - resolution: {integrity: sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==} + /@parcel/watcher-darwin-x64@2.5.0: + resolution: {integrity: sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [darwin] @@ -7134,8 +6839,8 @@ packages: dev: true optional: true - /@parcel/watcher-freebsd-x64@2.4.1: - resolution: {integrity: sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==} + /@parcel/watcher-freebsd-x64@2.5.0: + resolution: {integrity: sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [freebsd] @@ -7143,8 +6848,17 @@ packages: dev: true optional: true - /@parcel/watcher-linux-arm-glibc@2.4.1: - resolution: {integrity: sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==} + /@parcel/watcher-linux-arm-glibc@2.5.0: + resolution: {integrity: sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@parcel/watcher-linux-arm-musl@2.5.0: + resolution: {integrity: sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] @@ -7152,8 +6866,8 @@ packages: dev: true optional: true - /@parcel/watcher-linux-arm64-glibc@2.4.1: - resolution: {integrity: sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==} + /@parcel/watcher-linux-arm64-glibc@2.5.0: + resolution: {integrity: sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] @@ -7161,8 +6875,8 @@ packages: dev: true optional: true - /@parcel/watcher-linux-arm64-musl@2.4.1: - resolution: {integrity: sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==} + /@parcel/watcher-linux-arm64-musl@2.5.0: + resolution: {integrity: sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] @@ -7170,8 +6884,8 @@ packages: dev: true optional: true - /@parcel/watcher-linux-x64-glibc@2.4.1: - resolution: {integrity: sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==} + /@parcel/watcher-linux-x64-glibc@2.5.0: + resolution: {integrity: sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] @@ -7179,8 +6893,8 @@ packages: dev: true optional: true - /@parcel/watcher-linux-x64-musl@2.4.1: - resolution: {integrity: sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==} + /@parcel/watcher-linux-x64-musl@2.5.0: + resolution: {integrity: sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] @@ -7188,8 +6902,8 @@ packages: dev: true optional: true - /@parcel/watcher-win32-arm64@2.4.1: - resolution: {integrity: sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==} + /@parcel/watcher-win32-arm64@2.5.0: + resolution: {integrity: sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [win32] @@ -7197,8 +6911,8 @@ packages: dev: true optional: true - /@parcel/watcher-win32-ia32@2.4.1: - resolution: {integrity: sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==} + /@parcel/watcher-win32-ia32@2.5.0: + resolution: {integrity: sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==} engines: {node: '>= 10.0.0'} cpu: [ia32] os: [win32] @@ -7206,8 +6920,8 @@ packages: dev: true optional: true - /@parcel/watcher-win32-x64@2.4.1: - resolution: {integrity: sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==} + /@parcel/watcher-win32-x64@2.5.0: + resolution: {integrity: sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [win32] @@ -7215,27 +6929,29 @@ packages: dev: true optional: true - /@parcel/watcher@2.4.1: - resolution: {integrity: sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==} + /@parcel/watcher@2.5.0: + resolution: {integrity: sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==} engines: {node: '>= 10.0.0'} + requiresBuild: true dependencies: detect-libc: 1.0.3 is-glob: 4.0.3 micromatch: 4.0.8 node-addon-api: 7.1.1 optionalDependencies: - '@parcel/watcher-android-arm64': 2.4.1 - '@parcel/watcher-darwin-arm64': 2.4.1 - '@parcel/watcher-darwin-x64': 2.4.1 - '@parcel/watcher-freebsd-x64': 2.4.1 - '@parcel/watcher-linux-arm-glibc': 2.4.1 - '@parcel/watcher-linux-arm64-glibc': 2.4.1 - '@parcel/watcher-linux-arm64-musl': 2.4.1 - '@parcel/watcher-linux-x64-glibc': 2.4.1 - '@parcel/watcher-linux-x64-musl': 2.4.1 - '@parcel/watcher-win32-arm64': 2.4.1 - '@parcel/watcher-win32-ia32': 2.4.1 - '@parcel/watcher-win32-x64': 2.4.1 + '@parcel/watcher-android-arm64': 2.5.0 + '@parcel/watcher-darwin-arm64': 2.5.0 + '@parcel/watcher-darwin-x64': 2.5.0 + '@parcel/watcher-freebsd-x64': 2.5.0 + '@parcel/watcher-linux-arm-glibc': 2.5.0 + '@parcel/watcher-linux-arm-musl': 2.5.0 + '@parcel/watcher-linux-arm64-glibc': 2.5.0 + '@parcel/watcher-linux-arm64-musl': 2.5.0 + '@parcel/watcher-linux-x64-glibc': 2.5.0 + '@parcel/watcher-linux-x64-musl': 2.5.0 + '@parcel/watcher-win32-arm64': 2.5.0 + '@parcel/watcher-win32-ia32': 2.5.0 + '@parcel/watcher-win32-x64': 2.5.0 dev: true /@peculiar/asn1-schema@2.3.13: @@ -7243,14 +6959,14 @@ packages: dependencies: asn1js: 3.0.5 pvtsutils: 1.3.5 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@peculiar/json-schema@1.1.12: resolution: {integrity: sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==} engines: {node: '>=8.0.0'} dependencies: - tslib: 2.4.1 + tslib: 2.8.1 dev: false /@peculiar/webcrypto@1.4.1: @@ -7260,7 +6976,7 @@ packages: '@peculiar/asn1-schema': 2.3.13 '@peculiar/json-schema': 1.1.12 pvtsutils: 1.3.5 - tslib: 2.4.1 + tslib: 2.8.1 webcrypto-core: 1.8.1 dev: false @@ -9809,152 +9525,143 @@ packages: picomatch: 4.0.2 dev: true - /@rollup/rollup-android-arm-eabi@4.24.2: - resolution: {integrity: sha512-ufoveNTKDg9t/b7nqI3lwbCG/9IJMhADBNjjz/Jn6LxIZxD7T5L8l2uO/wD99945F1Oo8FvgbbZJRguyk/BdzA==} + /@rollup/rollup-android-arm-eabi@4.24.4: + resolution: {integrity: sha512-jfUJrFct/hTA0XDM5p/htWKoNNTbDLY0KRwEt6pyOA6k2fmk0WVwl65PdUdJZgzGEHWx+49LilkcSaumQRyNQw==} cpu: [arm] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-android-arm64@4.24.2: - resolution: {integrity: sha512-iZoYCiJz3Uek4NI0J06/ZxUgwAfNzqltK0MptPDO4OR0a88R4h0DSELMsflS6ibMCJ4PnLvq8f7O1d7WexUvIA==} + /@rollup/rollup-android-arm64@4.24.4: + resolution: {integrity: sha512-j4nrEO6nHU1nZUuCfRKoCcvh7PIywQPUCBa2UsootTHvTHIoIu2BzueInGJhhvQO/2FTRdNYpf63xsgEqH9IhA==} cpu: [arm64] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-arm64@4.24.2: - resolution: {integrity: sha512-/UhrIxobHYCBfhi5paTkUDQ0w+jckjRZDZ1kcBL132WeHZQ6+S5v9jQPVGLVrLbNUebdIRpIt00lQ+4Z7ys4Rg==} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-darwin-arm64@4.24.3: - resolution: {integrity: sha512-QPW2YmkWLlvqmOa2OwrfqLJqkHm7kJCIMq9kOz40Zo9Ipi40kf9ONG5Sz76zszrmIZZ4hgRIkez69YnTHgEz1w==} + /@rollup/rollup-darwin-arm64@4.24.4: + resolution: {integrity: sha512-GmU/QgGtBTeraKyldC7cDVVvAJEOr3dFLKneez/n7BvX57UdhOqDsVwzU7UOnYA7AAOt+Xb26lk79PldDHgMIQ==} cpu: [arm64] os: [darwin] requiresBuild: true - dev: false optional: true - /@rollup/rollup-darwin-x64@4.24.2: - resolution: {integrity: sha512-1F/jrfhxJtWILusgx63WeTvGTwE4vmsT9+e/z7cZLKU8sBMddwqw3UV5ERfOV+H1FuRK3YREZ46J4Gy0aP3qDA==} + /@rollup/rollup-darwin-x64@4.24.4: + resolution: {integrity: sha512-N6oDBiZCBKlwYcsEPXGDE4g9RoxZLK6vT98M8111cW7VsVJFpNEqvJeIPfsCzbf0XEakPslh72X0gnlMi4Ddgg==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-freebsd-arm64@4.24.2: - resolution: {integrity: sha512-1YWOpFcGuC6iGAS4EI+o3BV2/6S0H+m9kFOIlyFtp4xIX5rjSnL3AwbTBxROX0c8yWtiWM7ZI6mEPTI7VkSpZw==} + /@rollup/rollup-freebsd-arm64@4.24.4: + resolution: {integrity: sha512-py5oNShCCjCyjWXCZNrRGRpjWsF0ic8f4ieBNra5buQz0O/U6mMXCpC1LvrHuhJsNPgRt36tSYMidGzZiJF6mw==} cpu: [arm64] os: [freebsd] requiresBuild: true dev: true optional: true - /@rollup/rollup-freebsd-x64@4.24.2: - resolution: {integrity: sha512-3qAqTewYrCdnOD9Gl9yvPoAoFAVmPJsBvleabvx4bnu1Kt6DrB2OALeRVag7BdWGWLhP1yooeMLEi6r2nYSOjg==} + /@rollup/rollup-freebsd-x64@4.24.4: + resolution: {integrity: sha512-L7VVVW9FCnTTp4i7KrmHeDsDvjB4++KOBENYtNYAiYl96jeBThFfhP6HVxL74v4SiZEVDH/1ILscR5U9S4ms4g==} cpu: [x64] os: [freebsd] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.24.2: - resolution: {integrity: sha512-ArdGtPHjLqWkqQuoVQ6a5UC5ebdX8INPuJuJNWRe0RGa/YNhVvxeWmCTFQ7LdmNCSUzVZzxAvUznKaYx645Rig==} + /@rollup/rollup-linux-arm-gnueabihf@4.24.4: + resolution: {integrity: sha512-10ICosOwYChROdQoQo589N5idQIisxjaFE/PAnX2i0Zr84mY0k9zul1ArH0rnJ/fpgiqfu13TFZR5A5YJLOYZA==} cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm-musleabihf@4.24.2: - resolution: {integrity: sha512-B6UHHeNnnih8xH6wRKB0mOcJGvjZTww1FV59HqJoTJ5da9LCG6R4SEBt6uPqzlawv1LoEXSS0d4fBlHNWl6iYw==} + /@rollup/rollup-linux-arm-musleabihf@4.24.4: + resolution: {integrity: sha512-ySAfWs69LYC7QhRDZNKqNhz2UKN8LDfbKSMAEtoEI0jitwfAG2iZwVqGACJT+kfYvvz3/JgsLlcBP+WWoKCLcw==} cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-gnu@4.24.2: - resolution: {integrity: sha512-kr3gqzczJjSAncwOS6i7fpb4dlqcvLidqrX5hpGBIM1wtt0QEVtf4wFaAwVv8QygFU8iWUMYEoJZWuWxyua4GQ==} + /@rollup/rollup-linux-arm64-gnu@4.24.4: + resolution: {integrity: sha512-uHYJ0HNOI6pGEeZ/5mgm5arNVTI0nLlmrbdph+pGXpC9tFHFDQmDMOEqkmUObRfosJqpU8RliYoGz06qSdtcjg==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-musl@4.24.2: - resolution: {integrity: sha512-TDdHLKCWgPuq9vQcmyLrhg/bgbOvIQ8rtWQK7MRxJ9nvaxKx38NvY7/Lo6cYuEnNHqf6rMqnivOIPIQt6H2AoA==} + /@rollup/rollup-linux-arm64-musl@4.24.4: + resolution: {integrity: sha512-38yiWLemQf7aLHDgTg85fh3hW9stJ0Muk7+s6tIkSUOMmi4Xbv5pH/5Bofnsb6spIwD5FJiR+jg71f0CH5OzoA==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-powerpc64le-gnu@4.24.2: - resolution: {integrity: sha512-xv9vS648T3X4AxFFZGWeB5Dou8ilsv4VVqJ0+loOIgDO20zIhYfDLkk5xoQiej2RiSQkld9ijF/fhLeonrz2mw==} + /@rollup/rollup-linux-powerpc64le-gnu@4.24.4: + resolution: {integrity: sha512-q73XUPnkwt9ZNF2xRS4fvneSuaHw2BXuV5rI4cw0fWYVIWIBeDZX7c7FWhFQPNTnE24172K30I+dViWRVD9TwA==} cpu: [ppc64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-riscv64-gnu@4.24.2: - resolution: {integrity: sha512-tbtXwnofRoTt223WUZYiUnbxhGAOVul/3StZ947U4A5NNjnQJV5irKMm76G0LGItWs6y+SCjUn/Q0WaMLkEskg==} + /@rollup/rollup-linux-riscv64-gnu@4.24.4: + resolution: {integrity: sha512-Aie/TbmQi6UXokJqDZdmTJuZBCU3QBDA8oTKRGtd4ABi/nHgXICulfg1KI6n9/koDsiDbvHAiQO3YAUNa/7BCw==} cpu: [riscv64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-s390x-gnu@4.24.2: - resolution: {integrity: sha512-gc97UebApwdsSNT3q79glOSPdfwgwj5ELuiyuiMY3pEWMxeVqLGKfpDFoum4ujivzxn6veUPzkGuSYoh5deQ2Q==} + /@rollup/rollup-linux-s390x-gnu@4.24.4: + resolution: {integrity: sha512-P8MPErVO/y8ohWSP9JY7lLQ8+YMHfTI4bAdtCi3pC2hTeqFJco2jYspzOzTUB8hwUWIIu1xwOrJE11nP+0JFAQ==} cpu: [s390x] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-gnu@4.24.2: - resolution: {integrity: sha512-jOG/0nXb3z+EM6SioY8RofqqmZ+9NKYvJ6QQaa9Mvd3RQxlH68/jcB/lpyVt4lCiqr04IyaC34NzhUqcXbB5FQ==} + /@rollup/rollup-linux-x64-gnu@4.24.4: + resolution: {integrity: sha512-K03TljaaoPK5FOyNMZAAEmhlyO49LaE4qCsr0lYHUKyb6QacTNF9pnfPpXnFlFD3TXuFbFbz7tJ51FujUXkXYA==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-musl@4.24.2: - resolution: {integrity: sha512-XAo7cJec80NWx9LlZFEJQxqKOMz/lX3geWs2iNT5CHIERLFfd90f3RYLLjiCBm1IMaQ4VOX/lTC9lWfzzQm14Q==} + /@rollup/rollup-linux-x64-musl@4.24.4: + resolution: {integrity: sha512-VJYl4xSl/wqG2D5xTYncVWW+26ICV4wubwN9Gs5NrqhJtayikwCXzPL8GDsLnaLU3WwhQ8W02IinYSFJfyo34Q==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-arm64-msvc@4.24.2: - resolution: {integrity: sha512-A+JAs4+EhsTjnPQvo9XY/DC0ztaws3vfqzrMNMKlwQXuniBKOIIvAAI8M0fBYiTCxQnElYu7mLk7JrhlQ+HeOw==} + /@rollup/rollup-win32-arm64-msvc@4.24.4: + resolution: {integrity: sha512-ku2GvtPwQfCqoPFIJCqZ8o7bJcj+Y54cZSr43hHca6jLwAiCbZdBUOrqE6y29QFajNAzzpIOwsckaTFmN6/8TA==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-ia32-msvc@4.24.2: - resolution: {integrity: sha512-ZhcrakbqA1SCiJRMKSU64AZcYzlZ/9M5LaYil9QWxx9vLnkQ9Vnkve17Qn4SjlipqIIBFKjBES6Zxhnvh0EAEw==} + /@rollup/rollup-win32-ia32-msvc@4.24.4: + resolution: {integrity: sha512-V3nCe+eTt/W6UYNr/wGvO1fLpHUrnlirlypZfKCT1fG6hWfqhPgQV/K/mRBXBpxc0eKLIF18pIOFVPh0mqHjlg==} cpu: [ia32] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-x64-msvc@4.24.2: - resolution: {integrity: sha512-2mLH46K1u3r6uwc95hU+OR9q/ggYMpnS7pSp83Ece1HUQgF9Nh/QwTK5rcgbFnV9j+08yBrU5sA/P0RK2MSBNA==} + /@rollup/rollup-win32-x64-msvc@4.24.4: + resolution: {integrity: sha512-LTw1Dfd0mBIEqUVCxbvTE/LLo+9ZxVC9k99v1v4ahg9Aak6FpqOfNu5kRkeTAn0wphoC4JU7No1/rL+bBCEwhg==} cpu: [x64] os: [win32] requiresBuild: true @@ -9978,51 +9685,51 @@ packages: '@types/hast': 3.0.4 dev: false - /@shikijs/core@1.22.1: - resolution: {integrity: sha512-bqAhT/Ri5ixV4oYsvJNH8UJjpjbINWlWyXY6tBTsP4OmD6XnFv43nRJ+lTdxd2rmG5pgam/x+zGR6kLRXrpEKA==} + /@shikijs/core@1.22.2: + resolution: {integrity: sha512-bvIQcd8BEeR1yFvOYv6HDiyta2FFVePbzeowf5pPS1avczrPK+cjmaxxh0nx5QzbON7+Sv0sQfQVciO7bN72sg==} dependencies: - '@shikijs/engine-javascript': 1.22.1 - '@shikijs/engine-oniguruma': 1.22.1 - '@shikijs/types': 1.22.1 + '@shikijs/engine-javascript': 1.22.2 + '@shikijs/engine-oniguruma': 1.22.2 + '@shikijs/types': 1.22.2 '@shikijs/vscode-textmate': 9.3.0 '@types/hast': 3.0.4 hast-util-to-html: 9.0.3 dev: false - /@shikijs/engine-javascript@1.22.1: - resolution: {integrity: sha512-540pyoy0LWe4jj2BVbgELwOFu1uFvRI7lg4hdsExrSXA9x7gqfzZ/Nnh4RfX86aDAgJ647gx4TCmRwACbnQSvw==} + /@shikijs/engine-javascript@1.22.2: + resolution: {integrity: sha512-iOvql09ql6m+3d1vtvP8fLCVCK7BQD1pJFmHIECsujB0V32BJ0Ab6hxk1ewVSMFA58FI0pR2Had9BKZdyQrxTw==} dependencies: - '@shikijs/types': 1.22.1 + '@shikijs/types': 1.22.2 '@shikijs/vscode-textmate': 9.3.0 oniguruma-to-js: 0.4.3 dev: false - /@shikijs/engine-oniguruma@1.22.1: - resolution: {integrity: sha512-L+1Vmd+a2kk8HtogUFymQS6BjUfJnzcWoUp1BUgxoDiklbKSMvrsMuLZGevTOP1m0rEjgnC5MsDmsr8lX1lC+Q==} + /@shikijs/engine-oniguruma@1.22.2: + resolution: {integrity: sha512-GIZPAGzQOy56mGvWMoZRPggn0dTlBf1gutV5TdceLCZlFNqWmuc7u+CzD0Gd9vQUTgLbrt0KLzz6FNprqYAxlA==} dependencies: - '@shikijs/types': 1.22.1 + '@shikijs/types': 1.22.2 '@shikijs/vscode-textmate': 9.3.0 dev: false - /@shikijs/rehype@1.22.1: - resolution: {integrity: sha512-Uuuqj/poMLj1Z02sJsS1qEqpfUNp2hV2YFVt/SY5+Ielo5Ms1bRfYQHs3hTbsEDB6FmYuOu5ieqNumPlWBmrng==} + /@shikijs/rehype@1.22.2: + resolution: {integrity: sha512-A0RHgiYR5uiHvddwHehBN9j8PhOvfT6/GebSTWrapur6M+fD/4i3mlfUv7aFK4b+4GQ1R42L8fC5N98whZjNcg==} dependencies: - '@shikijs/types': 1.22.1 + '@shikijs/types': 1.22.2 '@types/hast': 3.0.4 hast-util-to-string: 3.0.1 - shiki: 1.22.1 + shiki: 1.22.2 unified: 11.0.5 unist-util-visit: 5.0.0 dev: false - /@shikijs/transformers@1.22.1: - resolution: {integrity: sha512-KvG49YFV6gV116sC4L3Sn1Rp6HXsioMKBBG373j088rw849440hm8s2r+/dgjsGLvT4p+QB7newev+5a3ARM6w==} + /@shikijs/transformers@1.22.2: + resolution: {integrity: sha512-8f78OiBa6pZDoZ53lYTmuvpFPlWtevn23bzG+azpPVvZg7ITax57o/K3TC91eYL3OMJOO0onPbgnQyZjRos8XQ==} dependencies: - shiki: 1.22.1 + shiki: 1.22.2 dev: false - /@shikijs/types@1.22.1: - resolution: {integrity: sha512-+45f8mu/Hxqs6Kyhfm98Nld5n7Q7lwhjU8UtdQwrOPs7BnM4VAb929O3IQ2ce+4D7SlNFlZGd8CnKRSnwbQreQ==} + /@shikijs/types@1.22.2: + resolution: {integrity: sha512-NCWDa6LGZqTuzjsGfXOBWfjS/fDIbDdmVDug+7ykVe1IKT4c1gakrvlfFYp5NhAXH/lyqLM8wsAPo5wNy73Feg==} dependencies: '@shikijs/vscode-textmate': 9.3.0 '@types/hast': 3.0.4 @@ -10241,23 +9948,17 @@ packages: /@swc/counter@0.1.3: resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - /@swc/helpers@0.5.13: - resolution: {integrity: sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==} - dependencies: - tslib: 2.8.1 - dev: false - /@swc/helpers@0.5.2: resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} dependencies: - tslib: 2.8.0 + tslib: 2.8.1 dev: false /@swc/helpers@0.5.5: resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} dependencies: '@swc/counter': 0.1.3 - tslib: 2.8.0 + tslib: 2.8.1 /@swc/types@0.1.12: resolution: {integrity: sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==} @@ -10400,22 +10101,8 @@ packages: next: '>=12.0.0' dependencies: '@trigger.dev/sdk': 3.1.0 - debug: 4.3.7 - next: 14.2.10(@opentelemetry/api@1.4.1)(react-dom@18.3.1)(react@18.3.1) - transitivePeerDependencies: - - supports-color - dev: false - - /@trigger.dev/nextjs@3.1.0(@trigger.dev/sdk@3.1.0)(next@15.0.2): - resolution: {integrity: sha512-/VsJqt6s3vWnTFkeC0Qz08Fr3gUyeTffcSwQ/UAAWLRyfW+CGKhxf8W4+PT1Z+HG/W6xsyvmGRprcOk1O1h6sQ==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@trigger.dev/sdk': ~2.3.0 || ^3.0.0 - next: '>=12.0.0' - dependencies: - '@trigger.dev/sdk': 3.1.0 - debug: 4.3.7 - next: 15.0.2(@opentelemetry/api@1.4.1)(react-dom@18.3.1)(react@18.3.1) + debug: 4.3.7(supports-color@8.1.1) + next: 14.2.10(@babel/core@7.26.0)(@opentelemetry/api@1.4.1)(react-dom@18.3.1)(react@18.3.1) transitivePeerDependencies: - supports-color dev: false @@ -10430,7 +10117,7 @@ packages: '@trigger.dev/core': 3.1.0 chalk: 5.3.0 cronstrue: 2.51.0 - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) evt: 2.5.7 slug: 6.1.0 terminal-link: 3.0.0 @@ -10513,7 +10200,7 @@ packages: /@types/cli-progress@3.11.6: resolution: {integrity: sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA==} dependencies: - '@types/node': 22.8.6 + '@types/node': 20.14.9 dev: true /@types/connect@3.4.38: @@ -10791,6 +10478,20 @@ packages: resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==} dev: false + /@types/eslint-scope@3.7.7: + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.6 + dev: false + + /@types/eslint@9.6.1: + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + dependencies: + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 + dev: false + /@types/estree-jsx@1.0.5: resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} dependencies: @@ -10870,8 +10571,8 @@ packages: resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} dev: false - /@types/lodash@4.17.12: - resolution: {integrity: sha512-sviUmCE8AYdaF/KIHLDJBQgeYzPBI0vf/17NaYehBJfYD1j6/L95Slh07NlyK2iNyBNaEkb3En2jRt+a8y3xZQ==} + /@types/lodash@4.17.13: + resolution: {integrity: sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==} dev: false /@types/mdast@3.0.15: @@ -10927,7 +10628,7 @@ packages: /@types/node-forge@1.3.11: resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} dependencies: - '@types/node': 22.8.6 + '@types/node': 20.14.9 dev: true /@types/node@12.20.55: @@ -10938,8 +10639,8 @@ packages: resolution: {integrity: sha512-vmYJF0REqDyyU0gviezF/KHq/fYaUbFhkcNbQCuPGFQj6VTbXuHZoxs/Y7mutWe73C8AC6l9fFu8mSYiBAqkGA==} dev: false - /@types/node@18.19.59: - resolution: {integrity: sha512-vizm2EqwV/7Zay+A6J3tGl9Lhr7CjZe2HmWS988sefiEmsyP9CeXEleho6i4hJk/8UtZAo0bWN4QPZZr83RxvQ==} + /@types/node@18.19.64: + resolution: {integrity: sha512-955mDqvO2vFf/oL7V3WiUtiz+BugyX8uVbaT2H8oj3+8dRyH2FLiNdowe7eNqRM7IOIZvzDH76EoAT+gwm6aIQ==} dependencies: undici-types: 5.26.5 dev: false @@ -10955,8 +10656,8 @@ packages: undici-types: 6.19.8 dev: true - /@types/node@22.8.6: - resolution: {integrity: sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==} + /@types/node@22.9.0: + resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==} dependencies: undici-types: 6.19.8 dev: true @@ -11054,7 +10755,7 @@ packages: dependencies: '@types/node': 20.14.9 tapable: 2.2.1 - webpack: 5.95.0(@swc/core@1.3.101)(esbuild@0.19.11) + webpack: 5.96.1(@swc/core@1.3.101)(esbuild@0.19.11) transitivePeerDependencies: - '@swc/core' - esbuild @@ -11066,8 +10767,8 @@ packages: resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==} dev: true - /@types/ws@8.5.12: - resolution: {integrity: sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==} + /@types/ws@8.5.13: + resolution: {integrity: sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==} dependencies: '@types/node': 20.14.9 dev: true @@ -11076,7 +10777,7 @@ packages: resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} requiresBuild: true dependencies: - '@types/node': 22.8.6 + '@types/node': 20.14.9 dev: true optional: true @@ -11238,7 +10939,7 @@ packages: peerDependencies: react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 dependencies: - '@types/lodash': 4.17.12 + '@types/lodash': 4.17.13 '@types/react': 18.3.11 lodash: 4.17.21 prop-types: 15.8.1 @@ -11258,7 +10959,7 @@ packages: dependencies: '@types/d3-path': 1.0.11 '@types/d3-shape': 1.3.12 - '@types/lodash': 4.17.12 + '@types/lodash': 4.17.13 '@types/react': 18.3.11 '@visx/curve': 3.3.0 '@visx/group': 3.3.0(react@18.3.1) @@ -11276,7 +10977,7 @@ packages: peerDependencies: react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 dependencies: - '@types/lodash': 4.17.12 + '@types/lodash': 4.17.13 '@types/react': 18.3.11 classnames: 2.5.1 lodash: 4.17.21 @@ -11718,51 +11419,7 @@ packages: indent-string: 5.0.0 dev: true - /ai@3.4.7(openai@4.52.1)(react@18.3.1)(svelte@5.1.8)(vue@3.5.12)(zod@3.23.8): - resolution: {integrity: sha512-SutkVjFE86+xNql7fJERJkSEwpILEuiQvCoogJef6ZX/PGHvu3yepwHwVwedgABXe9SudOIKN48EQESrXX/xCw==} - engines: {node: '>=18'} - peerDependencies: - openai: ^4.42.0 - react: ^18 || ^19 - sswr: ^2.1.0 - svelte: ^3.0.0 || ^4.0.0 - zod: ^3.0.0 - peerDependenciesMeta: - openai: - optional: true - react: - optional: true - sswr: - optional: true - svelte: - optional: true - zod: - optional: true - dependencies: - '@ai-sdk/provider': 0.0.24 - '@ai-sdk/provider-utils': 1.0.20(zod@3.23.8) - '@ai-sdk/react': 0.0.62(react@18.3.1)(zod@3.23.8) - '@ai-sdk/solid': 0.0.49(zod@3.23.8) - '@ai-sdk/svelte': 0.0.51(svelte@5.1.8)(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.46(zod@3.23.8) - '@ai-sdk/vue': 0.0.53(vue@3.5.12)(zod@3.23.8) - '@opentelemetry/api': 1.4.1 - eventsource-parser: 1.1.2 - json-schema: 0.4.0 - jsondiffpatch: 0.6.0 - nanoid: 3.3.6 - openai: 4.52.1 - react: 18.3.1 - secure-json-parse: 2.7.0 - svelte: 5.1.8 - zod: 3.23.8 - zod-to-json-schema: 3.23.2(zod@3.23.8) - transitivePeerDependencies: - - solid-js - - vue - dev: false - - /ai@3.4.7(react@18.3.1)(svelte@5.1.9)(vue@3.5.12)(zod@3.23.8): + /ai@3.4.7(openai@4.52.1)(react@18.3.1)(svelte@5.1.9)(vue@3.5.12)(zod@3.23.8): resolution: {integrity: sha512-SutkVjFE86+xNql7fJERJkSEwpILEuiQvCoogJef6ZX/PGHvu3yepwHwVwedgABXe9SudOIKN48EQESrXX/xCw==} engines: {node: '>=18'} peerDependencies: @@ -11795,6 +11452,7 @@ packages: json-schema: 0.4.0 jsondiffpatch: 0.6.0 nanoid: 3.3.6 + openai: 4.52.1 react: 18.3.1 secure-json-parse: 2.7.0 svelte: 5.1.9 @@ -11941,6 +11599,11 @@ packages: resolution: {integrity: sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==} dev: true + /ansis@3.3.2: + resolution: {integrity: sha512-cFthbBlt+Oi0i9Pv/j6YdVWJh54CtjGACaMPCIrEV4Ha7HWsIjXDwseYV79TIL0B4+KfSwD5S70PeQDkPUd1rA==} + engines: {node: '>=15'} + dev: true + /any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} @@ -11969,7 +11632,7 @@ packages: resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==} engines: {node: '>=10'} dependencies: - tslib: 2.8.0 + tslib: 2.8.1 /aria-query@5.3.2: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} @@ -12047,7 +11710,7 @@ packages: dependencies: pvtsutils: 1.3.5 pvutils: 1.1.3 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /assert@2.1.0: @@ -12068,12 +11731,7 @@ packages: resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} engines: {node: '>=4'} dependencies: - tslib: 2.8.0 - dev: true - - /astral-regex@2.0.0: - resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} - engines: {node: '>=8'} + tslib: 2.8.1 dev: true /astring@1.9.0: @@ -12115,7 +11773,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.24.2 - caniuse-lite: 1.0.30001673 + caniuse-lite: 1.0.30001677 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -12131,7 +11789,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.24.2 - caniuse-lite: 1.0.30001673 + caniuse-lite: 1.0.30001677 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -12146,7 +11804,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.24.2 - caniuse-lite: 1.0.30001673 + caniuse-lite: 1.0.30001677 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -12164,8 +11822,8 @@ packages: resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} engines: {node: '>= 6.0.0'} - /axios@1.6.2: - resolution: {integrity: sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==} + /axios@1.7.4: + resolution: {integrity: sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==} dependencies: follow-redirects: 1.15.9 form-data: 4.0.1 @@ -12315,8 +11973,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001673 - electron-to-chromium: 1.5.47 + caniuse-lite: 1.0.30001677 + electron-to-chromium: 1.5.51 node-releases: 2.0.18 update-browserslist-db: 1.1.1(browserslist@4.24.2) @@ -12447,18 +12105,14 @@ packages: resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} dev: false - /caniuse-lite@1.0.30001673: - resolution: {integrity: sha512-WTrjUCSMp3LYX0nE12ECkV0a+e6LC85E0Auz75555/qr78Oc8YWhEPNfDd6SHdtlCMSzqtuXY0uyEMNRcsKpKw==} - - /caniuse-lite@1.0.30001676: - resolution: {integrity: sha512-Qz6zwGCiPghQXGJvgQAem79esjitvJ+CxSbSQkW9H/UX5hg8XM88d4lp2W+MEQ81j+Hip58Il+jGVdazk1z9cw==} - dev: false + /caniuse-lite@1.0.30001677: + resolution: {integrity: sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==} /capnp-ts@0.7.0: resolution: {integrity: sha512-XKxXAC3HVPv7r674zP0VC3RTXz+/JKhfyw94ljvF80yynK6VkTnqE3jMuN8b3dUVmmc43TjyxjW4KTsmB3c86g==} dependencies: debug: 4.3.7(supports-color@8.1.1) - tslib: 2.8.0 + tslib: 2.8.1 transitivePeerDependencies: - supports-color dev: true @@ -12560,21 +12214,21 @@ packages: get-func-name: 2.0.2 dev: true - /checkly@4.7.0(@types/node@22.8.6)(typescript@5.5.3): - resolution: {integrity: sha512-OVEn7iUrtTs/YrxmNcWo7jQkH+e5LS0/KhF+MmIi8DbrCc8sCBppO1JyEnjoKcslCkV/Rilad66LShI6V/cqvQ==} + /checkly@4.9.1(@types/node@20.14.9)(typescript@5.5.3): + resolution: {integrity: sha512-BLVLb9EB8iOQVNZIRUjoEYVPdzOSiBwPlbNOYXOWDoMZHo6dUxWB4ckqJhqWfrvmVPEnSw6JMQ0v/XAXaGYmqw==} engines: {node: '>=16.0.0'} hasBin: true dependencies: - '@oclif/core': 2.8.11(@types/node@22.8.6)(typescript@5.5.3) + '@oclif/core': 2.8.11(@types/node@20.14.9)(typescript@5.5.3) '@oclif/plugin-help': 5.1.20 - '@oclif/plugin-not-found': 2.3.23(@types/node@22.8.6)(typescript@5.5.3) - '@oclif/plugin-plugins': 4.1.12 - '@oclif/plugin-warn-if-update-available': 2.0.24(@types/node@22.8.6)(typescript@5.5.3) + '@oclif/plugin-not-found': 2.3.23(@types/node@20.14.9)(typescript@5.5.3) + '@oclif/plugin-plugins': 5.4.4 + '@oclif/plugin-warn-if-update-available': 2.0.24(@types/node@20.14.9)(typescript@5.5.3) '@typescript-eslint/typescript-estree': 6.19.0(typescript@5.5.3) acorn: 8.8.1 acorn-walk: 8.2.0 async-mqtt: 2.6.3 - axios: 1.6.2 + axios: 1.7.4 chalk: 4.1.2 ci-info: 3.8.0 conf: 10.2.0 @@ -12867,6 +12521,23 @@ packages: - '@types/react-dom' dev: false + /cmdk@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-AnsjfHyHpQ/EFeAnG216WY7A5LiYCoZzCSygiLvfXC3H3LFGCprErteUcszaVluGOhuOTbJS3jWHrSDYPBBygg==} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + react-dom: ^18 || ^19 || ^19.0.0-rc + dependencies: + '@radix-ui/react-dialog': 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.11)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + use-sync-external-store: 1.2.2(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + dev: false + /collapse-white-space@2.1.0: resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} @@ -13539,17 +13210,6 @@ packages: ms: 2.1.2 dev: true - /debug@4.3.7: - resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.3 - /debug@4.3.7(supports-color@8.1.1): resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} engines: {node: '>=6.0'} @@ -13872,7 +13532,7 @@ packages: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} dependencies: no-case: 3.0.4 - tslib: 2.4.1 + tslib: 2.8.1 dev: false /dot-prop@6.0.1: @@ -13907,131 +13567,43 @@ packages: engines: {node: '>=12'} dev: true - /dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} - engines: {node: '>=12'} - dev: true - - /dotignore@0.1.2: - resolution: {integrity: sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==} - hasBin: true - dependencies: - minimatch: 3.1.2 - dev: false - - /drizzle-kit@0.22.7: - resolution: {integrity: sha512-9THPCb2l1GPt7wxhws9LvTR0YG565ZlVgTuqGMwjs590Kch1pXu4GyjEArVijSF5m0OBj3qgdeKmuJXhKXgWFw==} - hasBin: true - dependencies: - '@esbuild-kit/esm-loader': 2.6.5 - esbuild: 0.19.12 - esbuild-register: 3.6.0(esbuild@0.19.12) - transitivePeerDependencies: - - supports-color - dev: true - - /drizzle-kit@0.24.2: - resolution: {integrity: sha512-nXOaTSFiuIaTMhS8WJC2d4EBeIcN9OSt2A2cyFbQYBAZbi7lRsVGJNqDpEwPqYfJz38yxbY/UtbvBBahBfnExQ==} - hasBin: true - dependencies: - '@drizzle-team/brocli': 0.10.1 - '@esbuild-kit/esm-loader': 2.6.5 - esbuild: 0.19.12 - esbuild-register: 3.6.0(esbuild@0.19.12) - transitivePeerDependencies: - - supports-color - dev: true - - /drizzle-orm@0.31.2(@planetscale/database@1.18.0)(mysql2@3.10.3)(react@18.3.1): - resolution: {integrity: sha512-QnenevbnnAzmbNzQwbhklvIYrDE8YER8K7kSrAWQSV1YvFCdSQPzj+jzqRdTSsV2cDqSpQ0NXGyL1G9I43LDLg==} - peerDependencies: - '@aws-sdk/client-rds-data': '>=3' - '@cloudflare/workers-types': '>=3' - '@electric-sql/pglite': '>=0.1.1' - '@libsql/client': '*' - '@neondatabase/serverless': '>=0.1' - '@op-engineering/op-sqlite': '>=2' - '@opentelemetry/api': ^1.4.1 - '@planetscale/database': '>=1' - '@tidbcloud/serverless': '*' - '@types/better-sqlite3': '*' - '@types/pg': '*' - '@types/react': '>=18' - '@types/sql.js': '*' - '@vercel/postgres': '>=0.8.0' - '@xata.io/client': '*' - better-sqlite3: '>=7' - bun-types: '*' - expo-sqlite: '>=13.2.0' - knex: '*' - kysely: '*' - mysql2: '>=2' - pg: '>=8' - postgres: '>=3' - react: '>=18' - sql.js: '>=1' - sqlite3: '>=5' - peerDependenciesMeta: - '@aws-sdk/client-rds-data': - optional: true - '@cloudflare/workers-types': - optional: true - '@electric-sql/pglite': - optional: true - '@libsql/client': - optional: true - '@neondatabase/serverless': - optional: true - '@op-engineering/op-sqlite': - optional: true - '@opentelemetry/api': - optional: true - '@planetscale/database': - optional: true - '@tidbcloud/serverless': - optional: true - '@types/better-sqlite3': - optional: true - '@types/pg': - optional: true - '@types/react': - optional: true - '@types/sql.js': - optional: true - '@vercel/postgres': - optional: true - '@xata.io/client': - optional: true - better-sqlite3: - optional: true - bun-types: - optional: true - expo-sqlite: - optional: true - knex: - optional: true - kysely: - optional: true - mysql2: - optional: true - pg: - optional: true - postgres: - optional: true - react: - optional: true - sql.js: - optional: true - sqlite3: - optional: true + /dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + dev: true + + /dotignore@0.1.2: + resolution: {integrity: sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==} + hasBin: true dependencies: - '@planetscale/database': 1.18.0 - mysql2: 3.10.3 - react: 18.3.1 + minimatch: 3.1.2 dev: false - /drizzle-orm@0.32.0-aaf764c(@cloudflare/workers-types@4.20240603.0)(@planetscale/database@1.18.0)(react@18.3.1): - resolution: {integrity: sha512-TpG2xZhiGUyxWMJTXpVwuuFsh0VrNxPzYOZfsDaFbZS0lBbAN1xC2TBUK4pv1DMGxygq+clvqNMd908RznJZRA==} + /drizzle-kit@0.22.7: + resolution: {integrity: sha512-9THPCb2l1GPt7wxhws9LvTR0YG565ZlVgTuqGMwjs590Kch1pXu4GyjEArVijSF5m0OBj3qgdeKmuJXhKXgWFw==} + hasBin: true + dependencies: + '@esbuild-kit/esm-loader': 2.6.5 + esbuild: 0.19.12 + esbuild-register: 3.6.0(esbuild@0.19.12) + transitivePeerDependencies: + - supports-color + dev: true + + /drizzle-kit@0.24.2: + resolution: {integrity: sha512-nXOaTSFiuIaTMhS8WJC2d4EBeIcN9OSt2A2cyFbQYBAZbi7lRsVGJNqDpEwPqYfJz38yxbY/UtbvBBahBfnExQ==} + hasBin: true + dependencies: + '@drizzle-team/brocli': 0.10.2 + '@esbuild-kit/esm-loader': 2.6.5 + esbuild: 0.19.12 + esbuild-register: 3.6.0(esbuild@0.19.12) + transitivePeerDependencies: + - supports-color + dev: true + + /drizzle-orm@0.31.2(@planetscale/database@1.18.0)(mysql2@3.10.3)(react@18.3.1): + resolution: {integrity: sha512-QnenevbnnAzmbNzQwbhklvIYrDE8YER8K7kSrAWQSV1YvFCdSQPzj+jzqRdTSsV2cDqSpQ0NXGyL1G9I43LDLg==} peerDependencies: '@aws-sdk/client-rds-data': '>=3' '@cloudflare/workers-types': '>=3' @@ -14041,7 +13613,6 @@ packages: '@op-engineering/op-sqlite': '>=2' '@opentelemetry/api': ^1.4.1 '@planetscale/database': '>=1' - '@prisma/client': '*' '@tidbcloud/serverless': '*' '@types/better-sqlite3': '*' '@types/pg': '*' @@ -14057,7 +13628,6 @@ packages: mysql2: '>=2' pg: '>=8' postgres: '>=3' - prisma: '*' react: '>=18' sql.js: '>=1' sqlite3: '>=5' @@ -14078,8 +13648,6 @@ packages: optional: true '@planetscale/database': optional: true - '@prisma/client': - optional: true '@tidbcloud/serverless': optional: true '@types/better-sqlite3': @@ -14110,8 +13678,6 @@ packages: optional: true postgres: optional: true - prisma: - optional: true react: optional: true sql.js: @@ -14119,12 +13685,12 @@ packages: sqlite3: optional: true dependencies: - '@cloudflare/workers-types': 4.20240603.0 '@planetscale/database': 1.18.0 + mysql2: 3.10.3 react: 18.3.1 dev: false - /drizzle-orm@0.32.0-aaf764c(@opentelemetry/api@1.4.1)(@planetscale/database@1.18.0)(@types/react@18.3.11)(react@18.3.1): + /drizzle-orm@0.32.0-aaf764c(@cloudflare/workers-types@4.20240603.0)(@planetscale/database@1.18.0)(react@18.3.1): resolution: {integrity: sha512-TpG2xZhiGUyxWMJTXpVwuuFsh0VrNxPzYOZfsDaFbZS0lBbAN1xC2TBUK4pv1DMGxygq+clvqNMd908RznJZRA==} peerDependencies: '@aws-sdk/client-rds-data': '>=3' @@ -14213,13 +13779,12 @@ packages: sqlite3: optional: true dependencies: - '@opentelemetry/api': 1.4.1 + '@cloudflare/workers-types': 4.20240603.0 '@planetscale/database': 1.18.0 - '@types/react': 18.3.11 react: 18.3.1 dev: false - /drizzle-orm@0.32.0-aaf764c(@planetscale/database@1.18.0)(@types/react@18.3.11)(react@18.3.1): + /drizzle-orm@0.32.0-aaf764c(@opentelemetry/api@1.4.1)(@planetscale/database@1.18.0)(@types/react@18.3.11)(react@18.3.1): resolution: {integrity: sha512-TpG2xZhiGUyxWMJTXpVwuuFsh0VrNxPzYOZfsDaFbZS0lBbAN1xC2TBUK4pv1DMGxygq+clvqNMd908RznJZRA==} peerDependencies: '@aws-sdk/client-rds-data': '>=3' @@ -14308,6 +13873,7 @@ packages: sqlite3: optional: true dependencies: + '@opentelemetry/api': 1.4.1 '@planetscale/database': 1.18.0 '@types/react': 18.3.11 react: 18.3.1 @@ -14465,8 +14031,8 @@ packages: jake: 10.9.2 dev: true - /electron-to-chromium@1.5.47: - resolution: {integrity: sha512-zS5Yer0MOYw4rtK2iq43cJagHZ8sXN0jDHDKzB+86gSBSAI4v07S97mcq+Gs2vclAxSh1j7vOAHxSVgduiiuVQ==} + /electron-to-chromium@1.5.51: + resolution: {integrity: sha512-kKeWV57KSS8jH4alKt/jKnvHPmJgBxXzGUSbMd4eQF+iOsVPl7bz2KUmu6eo80eMP8wVioTfTyTzdMgM15WXNg==} /emoji-regex@10.4.0: resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} @@ -14497,7 +14063,7 @@ packages: resolution: {integrity: sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==} dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) engine.io-parser: 5.2.3 ws: 8.17.1 xmlhttprequest-ssl: 2.0.0 @@ -14537,7 +14103,7 @@ packages: dependencies: '@types/cookie': 0.4.1 '@types/cors': 2.8.17 - '@types/node': 22.8.6 + '@types/node': 20.14.9 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.7.2 @@ -14709,7 +14275,7 @@ packages: peerDependencies: esbuild: '>=0.12 <1' dependencies: - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) esbuild: 0.19.12 transitivePeerDependencies: - supports-color @@ -14948,31 +14514,31 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} - /eslint-config-prettier@9.0.0(eslint@9.13.0): + /eslint-config-prettier@9.0.0(eslint@9.14.0): resolution: {integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 9.13.0 + eslint: 9.14.0 dev: false - /eslint-config-turbo@1.10.12(eslint@9.13.0): + /eslint-config-turbo@1.10.12(eslint@9.14.0): resolution: {integrity: sha512-z3jfh+D7UGYlzMWGh+Kqz++hf8LOE96q3o5R8X4HTjmxaBWlLAWG+0Ounr38h+JLR2TJno0hU9zfzoPNkR9BdA==} peerDependencies: eslint: '>6.6.0' dependencies: - eslint: 9.13.0 - eslint-plugin-turbo: 1.10.12(eslint@9.13.0) + eslint: 9.14.0 + eslint-plugin-turbo: 1.10.12(eslint@9.14.0) dev: false - /eslint-plugin-turbo@1.10.12(eslint@9.13.0): + /eslint-plugin-turbo@1.10.12(eslint@9.14.0): resolution: {integrity: sha512-uNbdj+ohZaYo4tFJ6dStRXu2FZigwulR1b3URPXe0Q8YaE7thuekKNP+54CHtZPH9Zey9dmDx5btAQl9mfzGOw==} peerDependencies: eslint: '>6.6.0' dependencies: dotenv: 16.0.3 - eslint: 9.13.0 + eslint: 9.14.0 dev: false /eslint-scope@5.1.1: @@ -15000,8 +14566,8 @@ packages: engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dev: false - /eslint@9.13.0: - resolution: {integrity: sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==} + /eslint@9.14.0: + resolution: {integrity: sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -15010,16 +14576,16 @@ packages: jiti: optional: true dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.13.0) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.14.0) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.18.0 '@eslint/core': 0.7.0 '@eslint/eslintrc': 3.1.0 - '@eslint/js': 9.13.0 + '@eslint/js': 9.14.0 '@eslint/plugin-kit': 0.2.2 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.3.1 + '@humanwhocodes/retry': 0.4.0 '@types/estree': 1.0.6 '@types/json-schema': 7.0.15 ajv: 6.12.6 @@ -15149,8 +14715,8 @@ packages: astring: 1.9.0 source-map: 0.7.4 - /estree-util-value-to-estree@3.1.2: - resolution: {integrity: sha512-S0gW2+XZkmsx00tU2uJ4L9hUT7IFabbml9pHh2WQqFmAbxit++YGZne0sKJbNwkj9Wvg9E4uqWl4nCIFQMmfag==} + /estree-util-value-to-estree@3.2.1: + resolution: {integrity: sha512-Vt2UOjyPbNQQgT5eJh+K5aATti0OjCIAGc9SgMdOFYbohuifsWclR74l0iZTJwePMgWYdX1hlVS+dedH9XV8kw==} dependencies: '@types/estree': 1.0.6 @@ -15352,7 +14918,7 @@ packages: engines: {node: '>= 10.17.0'} hasBin: true dependencies: - debug: 4.3.4 + debug: 4.3.7(supports-color@8.1.1) get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -15702,7 +15268,7 @@ packages: dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tslib: 2.8.0 + tslib: 2.8.1 optionalDependencies: '@emotion/is-prop-valid': 0.8.8 dev: false @@ -15723,7 +15289,7 @@ packages: dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tslib: 2.8.0 + tslib: 2.8.1 dev: false /free-email-domains@1.2.13: @@ -15816,9 +15382,9 @@ packages: react: '>= 18' react-dom: '>= 18' dependencies: - '@formatjs/intl-localematcher': 0.5.6 - '@shikijs/rehype': 1.22.1 - '@shikijs/transformers': 1.22.1 + '@formatjs/intl-localematcher': 0.5.7 + '@shikijs/rehype': 1.22.2 + '@shikijs/transformers': 1.22.2 flexsearch: 0.7.21 github-slugger: 2.0.0 image-size: 1.1.1 @@ -15832,7 +15398,7 @@ packages: remark-gfm: 4.0.0 remark-mdx: 3.1.0 scroll-into-view-if-needed: 3.1.0 - shiki: 1.22.1 + shiki: 1.22.2 swr: 2.2.5(react@18.3.1) unist-util-visit: 5.0.0 transitivePeerDependencies: @@ -15851,7 +15417,7 @@ packages: chokidar: 3.6.0 cross-spawn: 7.0.3 esbuild: 0.23.1 - estree-util-value-to-estree: 3.1.2 + estree-util-value-to-estree: 3.2.1 fast-glob: 3.3.2 fumadocs-core: 13.4.10(@types/react@18.3.11)(next@14.2.10)(react-dom@18.3.1)(react@18.3.1) gray-matter: 4.0.3 @@ -15879,7 +15445,7 @@ packages: '@radix-ui/react-tabs': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) '@tailwindcss/typography': 0.5.15(tailwindcss@3.4.10) class-variance-authority: 0.7.0 - cmdk: 1.0.0(@types/react-dom@18.3.0)(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) + cmdk: 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) fumadocs-core: 13.4.10(@types/react@18.3.11)(next@14.2.10)(react-dom@18.3.1)(react@18.3.1) lucide-react: 0.438.0(react@18.3.1) next: 14.2.10(@babel/core@7.26.0)(@opentelemetry/api@1.4.1)(react-dom@18.3.1)(react@18.3.1) @@ -16326,7 +15892,7 @@ packages: dependencies: '@types/hast': 2.3.10 hast-util-from-parse5: 7.1.2 - parse5: 7.2.0 + parse5: 7.2.1 vfile: 5.3.7 vfile-message: 3.1.4 dev: true @@ -16337,7 +15903,7 @@ packages: '@types/hast': 3.0.4 devlop: 1.1.0 hast-util-from-parse5: 8.0.1 - parse5: 7.2.0 + parse5: 7.2.1 vfile: 6.0.3 vfile-message: 4.0.2 @@ -16424,7 +15990,7 @@ packages: hast-util-to-parse5: 8.0.0 html-void-elements: 3.0.0 mdast-util-to-hast: 13.2.0 - parse5: 7.2.0 + parse5: 7.2.1 unist-util-position: 5.0.0 unist-util-visit: 5.0.0 vfile: 6.0.3 @@ -16667,6 +16233,13 @@ packages: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} dev: true + /hosted-git-info@7.0.2: + resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} + engines: {node: ^16.14.0 || >=18.0.0} + dependencies: + lru-cache: 10.4.3 + dev: true + /html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} dev: false @@ -16934,6 +16507,7 @@ packages: /interpret@1.4.0: resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} engines: {node: '>= 0.10'} + dev: false /invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} @@ -17330,6 +16904,11 @@ packages: /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + /isexe@3.1.1: + resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} + engines: {node: '>=16'} + dev: true + /isomorphic-fetch@3.0.0: resolution: {integrity: sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==} dependencies: @@ -17908,7 +17487,7 @@ packages: /lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} dependencies: - tslib: 2.4.1 + tslib: 2.8.1 dev: false /lowercase-keys@3.0.0: @@ -18113,7 +17692,7 @@ packages: devlop: 1.1.0 escape-string-regexp: 5.0.0 mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 micromark-extension-frontmatter: 2.0.0 transitivePeerDependencies: - supports-color @@ -18150,7 +17729,7 @@ packages: '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 micromark-util-normalize-identifier: 2.0.0 transitivePeerDependencies: - supports-color @@ -18167,7 +17746,7 @@ packages: dependencies: '@types/mdast': 4.0.4 mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color dev: false @@ -18189,7 +17768,7 @@ packages: devlop: 1.1.0 markdown-table: 3.0.4 mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color dev: false @@ -18206,7 +17785,7 @@ packages: '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color dev: false @@ -18233,7 +17812,7 @@ packages: mdast-util-gfm-strikethrough: 2.0.0 mdast-util-gfm-table: 2.0.0 mdast-util-gfm-task-list-item: 2.0.0 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color dev: false @@ -18265,7 +17844,7 @@ packages: '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color @@ -18297,7 +17876,7 @@ packages: ccount: 2.0.1 devlop: 1.1.0 mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 parse-entities: 4.0.1 stringify-entities: 4.0.4 unist-util-stringify-position: 4.0.0 @@ -18323,7 +17902,7 @@ packages: mdast-util-mdx-expression: 2.0.1 mdast-util-mdx-jsx: 3.1.3 mdast-util-mdxjs-esm: 2.0.1 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color @@ -18346,7 +17925,7 @@ packages: '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color @@ -18399,14 +17978,15 @@ packages: unist-util-visit: 4.1.2 zwitch: 2.0.4 - /mdast-util-to-markdown@2.1.0: - resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==} + /mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} dependencies: '@types/mdast': 4.0.4 '@types/unist': 3.0.3 longest-streak: 3.1.0 mdast-util-phrasing: 4.1.0 mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.0 micromark-util-decode-string: 2.0.0 unist-util-visit: 5.0.0 zwitch: 2.0.4 @@ -19320,6 +18900,7 @@ packages: /minipass@6.0.2: resolution: {integrity: sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==} engines: {node: '>=16 || 14 >=14.17'} + dev: true /minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} @@ -19643,7 +19224,7 @@ packages: '@next/env': 14.1.0 '@swc/helpers': 0.5.2 busboy: 1.6.0 - caniuse-lite: 1.0.30001673 + caniuse-lite: 1.0.30001677 graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.3.1 @@ -19686,7 +19267,7 @@ packages: '@opentelemetry/api': 1.4.1 '@swc/helpers': 0.5.5 busboy: 1.6.0 - caniuse-lite: 1.0.30001673 + caniuse-lite: 1.0.30001677 graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.3.1 @@ -19706,95 +19287,6 @@ packages: - '@babel/core' - babel-plugin-macros - /next@14.2.10(@opentelemetry/api@1.4.1)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-sDDExXnh33cY3RkS9JuFEKaS4HmlWmDKP1VJioucCG6z5KuA008DPsDZOzi8UfqEk3Ii+2NCQSJrfbEWtZZfww==} - engines: {node: '>=18.17.0'} - hasBin: true - peerDependencies: - '@opentelemetry/api': ^1.1.0 - '@playwright/test': ^1.41.2 - react: ^18.2.0 - react-dom: ^18.2.0 - sass: ^1.3.0 - peerDependenciesMeta: - '@opentelemetry/api': - optional: true - '@playwright/test': - optional: true - sass: - optional: true - dependencies: - '@next/env': 14.2.10 - '@opentelemetry/api': 1.4.1 - '@swc/helpers': 0.5.5 - busboy: 1.6.0 - caniuse-lite: 1.0.30001673 - graceful-fs: 4.2.11 - postcss: 8.4.31 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.1(react@18.3.1) - optionalDependencies: - '@next/swc-darwin-arm64': 14.2.10 - '@next/swc-darwin-x64': 14.2.10 - '@next/swc-linux-arm64-gnu': 14.2.10 - '@next/swc-linux-arm64-musl': 14.2.10 - '@next/swc-linux-x64-gnu': 14.2.10 - '@next/swc-linux-x64-musl': 14.2.10 - '@next/swc-win32-arm64-msvc': 14.2.10 - '@next/swc-win32-ia32-msvc': 14.2.10 - '@next/swc-win32-x64-msvc': 14.2.10 - transitivePeerDependencies: - - '@babel/core' - - babel-plugin-macros - dev: false - - /next@15.0.2(@opentelemetry/api@1.4.1)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-rxIWHcAu4gGSDmwsELXacqAPUk+j8dV/A9cDF5fsiCMpkBDYkO2AEaL1dfD+nNmDiU6QMCFN8Q30VEKapT9UHQ==} - engines: {node: '>=18.18.0'} - hasBin: true - peerDependencies: - '@opentelemetry/api': ^1.1.0 - '@playwright/test': ^1.41.2 - babel-plugin-react-compiler: '*' - react: ^18.2.0 || 19.0.0-rc-02c0e824-20241028 - react-dom: ^18.2.0 || 19.0.0-rc-02c0e824-20241028 - sass: ^1.3.0 - peerDependenciesMeta: - '@opentelemetry/api': - optional: true - '@playwright/test': - optional: true - babel-plugin-react-compiler: - optional: true - sass: - optional: true - dependencies: - '@next/env': 15.0.2 - '@opentelemetry/api': 1.4.1 - '@swc/counter': 0.1.3 - '@swc/helpers': 0.5.13 - busboy: 1.6.0 - caniuse-lite: 1.0.30001676 - postcss: 8.4.31 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.6(react@18.3.1) - optionalDependencies: - '@next/swc-darwin-arm64': 15.0.2 - '@next/swc-darwin-x64': 15.0.2 - '@next/swc-linux-arm64-gnu': 15.0.2 - '@next/swc-linux-arm64-musl': 15.0.2 - '@next/swc-linux-x64-gnu': 15.0.2 - '@next/swc-linux-x64-musl': 15.0.2 - '@next/swc-win32-arm64-msvc': 15.0.2 - '@next/swc-win32-x64-msvc': 15.0.2 - sharp: 0.33.5 - transitivePeerDependencies: - - '@babel/core' - - babel-plugin-macros - dev: false - /nlcst-to-string@3.1.1: resolution: {integrity: sha512-63mVyqaqt0cmn2VcI2aH6kxe1rLAmSROqHMA0i4qqg1tidkfExgpb0FGMikMCn86mw5dFtBtEANfmSSK7TjNHw==} dependencies: @@ -19805,7 +19297,7 @@ packages: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} dependencies: lower-case: 2.0.2 - tslib: 2.4.1 + tslib: 2.8.1 dev: false /node-addon-api@7.1.1: @@ -19903,6 +19395,16 @@ packages: resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} engines: {node: '>=14.16'} + /npm-package-arg@11.0.3: + resolution: {integrity: sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==} + engines: {node: ^16.14.0 || >=18.0.0} + dependencies: + hosted-git-info: 7.0.2 + proc-log: 4.2.0 + semver: 7.6.3 + validate-npm-package-name: 5.0.1 + dev: true + /npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -19921,8 +19423,8 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: false - /npm@10.2.3: - resolution: {integrity: sha512-GbUui/rHTl0mW8HhJSn4A0Xg89yCR3I9otgJT1i0z1QBPOVlgbh6rlcUTpHT8Gut9O1SJjWRUU0nEcAymhG2tQ==} + /npm@10.9.0: + resolution: {integrity: sha512-ZanDioFylI9helNhl2LNd+ErmVD+H5I53ry41ixlLyCBgkuYb+58CvbAp99hW+zr5L9W4X7CchSoeqKdngOLSw==} engines: {node: ^18.17.0 || >=20.5.0} hasBin: true dev: true @@ -19934,6 +19436,7 @@ packages: - '@npmcli/map-workspaces' - '@npmcli/package-json' - '@npmcli/promise-spawn' + - '@npmcli/redact' - '@npmcli/run-script' - '@sigstore/tuf' - abbrev @@ -19942,8 +19445,6 @@ packages: - chalk - ci-info - cli-columns - - cli-table3 - - columnify - fastest-levenshtein - fs-minipass - glob @@ -19979,7 +19480,6 @@ packages: - npm-profile - npm-registry-fetch - npm-user-validate - - npmlog - p-map - pacote - parse-conflict-json @@ -19989,7 +19489,6 @@ packages: - semver - spdx-expression-parse - ssri - - strip-ansi - supports-color - tar - text-table @@ -20065,6 +19564,11 @@ packages: engines: {node: '>= 10'} dev: true + /object-treeify@4.0.1: + resolution: {integrity: sha512-Y6tg5rHfsefSkfKujv2SwHulInROy/rCL5F4w0QOWxut8AnxYxf0YmNhTh95Zfyxpsudo66uqkux0ACFnyMSgQ==} + engines: {node: '>= 16'} + dev: true + /object-values@1.0.0: resolution: {integrity: sha512-+8hwcz/JnQ9EpLIXzN0Rs7DLsBpJNT/xYehtB/jU93tHYr5BFEO8E+JGQNOSqE7opVzz5cGksKFHt7uUJVLSjQ==} engines: {node: '>=0.10.0'} @@ -20117,7 +19621,7 @@ packages: /oniguruma-to-js@0.4.3: resolution: {integrity: sha512-X0jWUcAlxORhOqqBREgPMgnshB7ZGYszBNspP+tS9hPD3l13CdaXcHbgImoHUHlrvGx/7AvFEkTRhAGYh+jzjQ==} dependencies: - regex: 4.3.3 + regex: 4.4.0 dev: false /open@8.4.0: @@ -20142,7 +19646,7 @@ packages: resolution: {integrity: sha512-kv2hevAWZZ3I/vd2t8znGO2rd8wkowncsfcYpo8i+wU9ML+JEcdqiViANXXjWWGjIhajFNixE6gOY1fEgqILAg==} hasBin: true dependencies: - '@types/node': 18.19.59 + '@types/node': 18.19.64 '@types/node-fetch': 2.6.11 abort-controller: 3.0.0 agentkeepalive: 4.5.0 @@ -20422,7 +19926,7 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} dependencies: - '@babel/code-frame': 7.26.0 + '@babel/code-frame': 7.26.2 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -20453,8 +19957,8 @@ packages: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} dev: true - /parse5@7.2.0: - resolution: {integrity: sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==} + /parse5@7.2.1: + resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} dependencies: entities: 4.5.0 @@ -20506,7 +20010,7 @@ packages: engines: {node: '>=16 || 14 >=14.18'} dependencies: lru-cache: 10.4.3 - minipass: 6.0.2 + minipass: 7.1.2 /path-to-regexp@0.1.10: resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} @@ -20745,13 +20249,6 @@ packages: picocolors: 1.1.1 source-map-js: 1.2.1 - /posthog-js@1.130.1: - resolution: {integrity: sha512-BC283kxeJnVIeAxn7ZPHf5sCTA6oXs4uvo9fdGAsbKwwfmF9g09rnJOOaoF95J/auf8HT4YB6Vt2KytqtJD44w==} - dependencies: - fflate: 0.4.8 - preact: 10.24.3 - dev: false - /posthog-js@1.179.0: resolution: {integrity: sha512-TLmDA3oDdjZfOaNCueXBdlWD+kTzBGvz2QpOY/zdzljbazXXl25xzXBINLIC7IxIS2gNAfCDRuOUosSZALOyUQ==} dependencies: @@ -20872,6 +20369,11 @@ packages: '@probe.gl/stats': 3.6.0 dev: false + /proc-log@4.2.0: + resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true + /process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} dev: true @@ -21017,7 +20519,7 @@ packages: /pvtsutils@1.3.5: resolution: {integrity: sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==} dependencies: - tslib: 2.8.0 + tslib: 2.8.1 dev: false /pvutils@1.1.3: @@ -21088,7 +20590,7 @@ packages: react: 18.3.1 react-syntax-highlighter: 15.6.1(react@18.3.1) styled-components: 6.1.13(react-dom@18.3.1)(react@18.3.1) - tslib: 2.8.0 + tslib: 2.8.1 transitivePeerDependencies: - react-dom dev: false @@ -21144,7 +20646,7 @@ packages: react-dom: 18.3.1(react@18.3.1) dev: true - /react-email@2.1.1(@babel/core@7.26.0)(eslint@9.13.0)(ts-node@10.9.2): + /react-email@2.1.1(@babel/core@7.26.0)(eslint@9.14.0)(ts-node@10.9.2): resolution: {integrity: sha512-09oMVl/jN0/Re0bT0sEqYjyyFSCN/Tg0YmzjC9wfYpnMx02Apk40XXitySDfUBMR9EgTdr6T4lYknACqiLK3mg==} engines: {node: '>=18.0.0'} hasBin: true @@ -21170,8 +20672,8 @@ packages: commander: 11.1.0 debounce: 2.0.0 esbuild: 0.19.11 - eslint-config-prettier: 9.0.0(eslint@9.13.0) - eslint-config-turbo: 1.10.12(eslint@9.13.0) + eslint-config-prettier: 9.0.0(eslint@9.14.0) + eslint-config-turbo: 1.10.12(eslint@9.14.0) framer-motion: 10.17.4(react-dom@18.3.1)(react@18.3.1) glob: 10.3.4 log-symbols: 4.1.0 @@ -21289,7 +20791,7 @@ packages: '@types/react': 18.3.11 react: 18.3.1 react-style-singleton: 2.2.1(@types/react@18.3.11)(react@18.3.1) - tslib: 2.8.0 + tslib: 2.8.1 /react-remove-scroll@2.5.4(react@18.3.1): resolution: {integrity: sha512-xGVKJJr0SJGQVirVFAUZ2k1QLyO6m+2fy0l8Qawbp5Jgrv3DeLalrfMNBFSlmz5kriGGzsVBtGVnf4pTKIhhWA==} @@ -21304,7 +20806,7 @@ packages: react: 18.3.1 react-remove-scroll-bar: 2.3.6(@types/react@18.3.11)(react@18.3.1) react-style-singleton: 2.2.1(@types/react@18.3.11)(react@18.3.1) - tslib: 2.8.0 + tslib: 2.8.1 use-callback-ref: 1.3.2(@types/react@18.3.11)(react@18.3.1) use-sidecar: 1.1.2(@types/react@18.3.11)(react@18.3.1) dev: true @@ -21323,7 +20825,7 @@ packages: react: 18.3.1 react-remove-scroll-bar: 2.3.6(@types/react@18.3.11)(react@18.3.1) react-style-singleton: 2.2.1(@types/react@18.3.11)(react@18.3.1) - tslib: 2.8.0 + tslib: 2.8.1 use-callback-ref: 1.3.2(@types/react@18.3.11)(react@18.3.1) use-sidecar: 1.1.2(@types/react@18.3.11)(react@18.3.1) dev: false @@ -21342,7 +20844,7 @@ packages: react: 18.3.1 react-remove-scroll-bar: 2.3.6(@types/react@18.3.11)(react@18.3.1) react-style-singleton: 2.2.1(@types/react@18.3.11)(react@18.3.1) - tslib: 2.8.0 + tslib: 2.8.1 use-callback-ref: 1.3.2(@types/react@18.3.11)(react@18.3.1) use-sidecar: 1.1.2(@types/react@18.3.11)(react@18.3.1) dev: false @@ -21361,7 +20863,7 @@ packages: react: 18.3.1 react-remove-scroll-bar: 2.3.6(@types/react@18.3.11)(react@18.3.1) react-style-singleton: 2.2.1(@types/react@18.3.11)(react@18.3.1) - tslib: 2.8.0 + tslib: 2.8.1 use-callback-ref: 1.3.2(@types/react@18.3.11)(react@18.3.1) use-sidecar: 1.1.2(@types/react@18.3.11)(react@18.3.1) @@ -21379,7 +20881,7 @@ packages: get-nonce: 1.0.1 invariant: 2.2.4 react: 18.3.1 - tslib: 2.8.0 + tslib: 2.8.1 /react-syntax-highlighter@15.5.0(react@18.3.1): resolution: {integrity: sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==} @@ -21532,7 +21034,7 @@ packages: ast-types: 0.16.1 esprima: 4.0.1 source-map: 0.6.1 - tslib: 2.8.0 + tslib: 2.8.1 dev: true /rechoir@0.6.2: @@ -21540,6 +21042,7 @@ packages: engines: {node: '>= 0.10'} dependencies: resolve: 1.22.8 + dev: false /recma-build-jsx@1.0.0: resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} @@ -21627,8 +21130,8 @@ packages: /regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - /regex@4.3.3: - resolution: {integrity: sha512-r/AadFO7owAq1QJVeZ/nq9jNS1vyZt+6t1p/E59B56Rn2GCya+gr1KSyOzNL/er+r+B7phv5jG2xU2Nz1YkmJg==} + /regex@4.4.0: + resolution: {integrity: sha512-uCUSuobNVeqUupowbdZub6ggI5/JZkYyJdDogddJr60L764oxC2pMZov1fQ3wM9bdyzUILDG+Sqx6NAKAz9rKQ==} dev: false /regexp.prototype.flags@1.5.3: @@ -21786,7 +21289,7 @@ packages: dependencies: '@types/mdast': 4.0.4 estree-util-is-identifier-name: 3.0.0 - estree-util-value-to-estree: 3.1.2 + estree-util-value-to-estree: 3.2.1 toml: 3.0.0 unified: 11.0.5 yaml: 2.6.0 @@ -21865,7 +21368,7 @@ packages: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} dependencies: '@types/mdast': 4.0.4 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 unified: 11.0.5 dev: false @@ -21909,7 +21412,7 @@ packages: resolution: {integrity: sha512-X34iHADNbNDfr6OTStIAHWSAvvKQRYgLO6duASaVf7J2VA3lvmNYboAHOuLC2huav1IwgZJtyEcJCKVzFxOSMQ==} engines: {node: '>=8.6.0'} dependencies: - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) module-details-from-path: 1.0.3 resolve: 1.22.8 transitivePeerDependencies: @@ -22102,31 +21605,31 @@ packages: source-map-support: 0.3.3 dev: false - /rollup@4.24.2: - resolution: {integrity: sha512-do/DFGq5g6rdDhdpPq5qb2ecoczeK6y+2UAjdJ5trjQJj5f1AiVdLRWRc9A9/fFukfvJRgM0UXzxBIYMovm5ww==} + /rollup@4.24.4: + resolution: {integrity: sha512-vGorVWIsWfX3xbcyAS+I047kFKapHYivmkaT63Smj77XwvLSJos6M1xGqZnBPFQFBRZDOcG1QnYEIxAvTr/HjA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true dependencies: '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.24.2 - '@rollup/rollup-android-arm64': 4.24.2 - '@rollup/rollup-darwin-arm64': 4.24.2 - '@rollup/rollup-darwin-x64': 4.24.2 - '@rollup/rollup-freebsd-arm64': 4.24.2 - '@rollup/rollup-freebsd-x64': 4.24.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.24.2 - '@rollup/rollup-linux-arm-musleabihf': 4.24.2 - '@rollup/rollup-linux-arm64-gnu': 4.24.2 - '@rollup/rollup-linux-arm64-musl': 4.24.2 - '@rollup/rollup-linux-powerpc64le-gnu': 4.24.2 - '@rollup/rollup-linux-riscv64-gnu': 4.24.2 - '@rollup/rollup-linux-s390x-gnu': 4.24.2 - '@rollup/rollup-linux-x64-gnu': 4.24.2 - '@rollup/rollup-linux-x64-musl': 4.24.2 - '@rollup/rollup-win32-arm64-msvc': 4.24.2 - '@rollup/rollup-win32-ia32-msvc': 4.24.2 - '@rollup/rollup-win32-x64-msvc': 4.24.2 + '@rollup/rollup-android-arm-eabi': 4.24.4 + '@rollup/rollup-android-arm64': 4.24.4 + '@rollup/rollup-darwin-arm64': 4.24.4 + '@rollup/rollup-darwin-x64': 4.24.4 + '@rollup/rollup-freebsd-arm64': 4.24.4 + '@rollup/rollup-freebsd-x64': 4.24.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.24.4 + '@rollup/rollup-linux-arm-musleabihf': 4.24.4 + '@rollup/rollup-linux-arm64-gnu': 4.24.4 + '@rollup/rollup-linux-arm64-musl': 4.24.4 + '@rollup/rollup-linux-powerpc64le-gnu': 4.24.4 + '@rollup/rollup-linux-riscv64-gnu': 4.24.4 + '@rollup/rollup-linux-s390x-gnu': 4.24.4 + '@rollup/rollup-linux-x64-gnu': 4.24.4 + '@rollup/rollup-linux-x64-musl': 4.24.4 + '@rollup/rollup-win32-arm64-msvc': 4.24.4 + '@rollup/rollup-win32-ia32-msvc': 4.24.4 + '@rollup/rollup-win32-x64-msvc': 4.24.4 fsevents: 2.3.3 dev: true @@ -22163,7 +21666,7 @@ packages: /rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: - tslib: 2.8.0 + tslib: 2.8.1 dev: true /sade@1.8.1: @@ -22376,37 +21879,6 @@ packages: '@img/sharp-win32-ia32': 0.33.4 '@img/sharp-win32-x64': 0.33.4 - /sharp@0.33.5: - resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - requiresBuild: true - dependencies: - color: 4.2.3 - detect-libc: 2.0.3 - semver: 7.6.3 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.33.5 - '@img/sharp-darwin-x64': 0.33.5 - '@img/sharp-libvips-darwin-arm64': 1.0.4 - '@img/sharp-libvips-darwin-x64': 1.0.4 - '@img/sharp-libvips-linux-arm': 1.0.5 - '@img/sharp-libvips-linux-arm64': 1.0.4 - '@img/sharp-libvips-linux-s390x': 1.0.4 - '@img/sharp-libvips-linux-x64': 1.0.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 - '@img/sharp-libvips-linuxmusl-x64': 1.0.4 - '@img/sharp-linux-arm': 0.33.5 - '@img/sharp-linux-arm64': 0.33.5 - '@img/sharp-linux-s390x': 0.33.5 - '@img/sharp-linux-x64': 0.33.5 - '@img/sharp-linuxmusl-arm64': 0.33.5 - '@img/sharp-linuxmusl-x64': 0.33.5 - '@img/sharp-wasm32': 0.33.5 - '@img/sharp-win32-ia32': 0.33.5 - '@img/sharp-win32-x64': 0.33.5 - dev: false - optional: true - /shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} @@ -22441,6 +21913,7 @@ packages: glob: 7.2.3 interpret: 1.4.0 rechoir: 0.6.2 + dev: false /shiki@0.14.7: resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==} @@ -22458,13 +21931,13 @@ packages: '@types/hast': 3.0.4 dev: false - /shiki@1.22.1: - resolution: {integrity: sha512-PbJ6XxrWLMwB2rm3qdjIHNm3zq4SfFnOx0B3rEoi4AN8AUngsdyZ1tRe5slMPtn6jQkbUURLNZPpLR7Do3k78g==} + /shiki@1.22.2: + resolution: {integrity: sha512-3IZau0NdGKXhH2bBlUk4w1IHNxPh6A5B2sUpyY+8utLu2j/h1QpFkAaUA1bAMxOWWGtTWcAh531vnS4NJKS/lA==} dependencies: - '@shikijs/core': 1.22.1 - '@shikijs/engine-javascript': 1.22.1 - '@shikijs/engine-oniguruma': 1.22.1 - '@shikijs/types': 1.22.1 + '@shikijs/core': 1.22.2 + '@shikijs/engine-javascript': 1.22.2 + '@shikijs/engine-oniguruma': 1.22.2 + '@shikijs/types': 1.22.2 '@shikijs/vscode-textmate': 9.3.0 '@types/hast': 3.0.4 dev: false @@ -22531,15 +22004,6 @@ packages: engines: {node: '>=8'} dev: true - /slice-ansi@4.0.0: - resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 - dev: true - /slice-ansi@5.0.0: resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} engines: {node: '>=12'} @@ -22581,7 +22045,7 @@ packages: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} dependencies: dot-case: 3.0.4 - tslib: 2.4.1 + tslib: 2.8.1 dev: false /snakecase-keys@3.2.1: @@ -22630,7 +22094,7 @@ packages: engines: {node: '>=10.0.0'} dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) engine.io-client: 6.5.4 socket.io-parser: 4.2.4 transitivePeerDependencies: @@ -22644,7 +22108,7 @@ packages: engines: {node: '>=10.0.0'} dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -22811,15 +22275,6 @@ packages: resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} engines: {node: '>= 0.6'} - /sswr@2.1.0(svelte@5.1.8): - resolution: {integrity: sha512-Cqc355SYlTAaUt8iDPaC/4DPPXK925PePLMxyBKuWd5kKc5mwsG3nT9+Mq2tyguL5s7b4Jg+IRMpTRsNTAfpSQ==} - peerDependencies: - svelte: ^4.0.0 || ^5.0.0-next.0 - dependencies: - svelte: 5.1.8 - swrev: 4.0.0 - dev: false - /sswr@2.1.0(svelte@5.1.9): resolution: {integrity: sha512-Cqc355SYlTAaUt8iDPaC/4DPPXK925PePLMxyBKuWd5kKc5mwsG3nT9+Mq2tyguL5s7b4Jg+IRMpTRsNTAfpSQ==} peerDependencies: @@ -23083,40 +22538,6 @@ packages: client-only: 0.0.1 react: 18.3.1 - /styled-jsx@5.1.1(react@18.3.1): - resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} - engines: {node: '>= 12.0.0'} - peerDependencies: - '@babel/core': '*' - babel-plugin-macros: '*' - react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' - peerDependenciesMeta: - '@babel/core': - optional: true - babel-plugin-macros: - optional: true - dependencies: - client-only: 0.0.1 - react: 18.3.1 - dev: false - - /styled-jsx@5.1.6(react@18.3.1): - resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} - engines: {node: '>= 12.0.0'} - peerDependencies: - '@babel/core': '*' - babel-plugin-macros: '*' - react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' - peerDependenciesMeta: - '@babel/core': - optional: true - babel-plugin-macros: - optional: true - dependencies: - client-only: 0.0.1 - react: 18.3.1 - dev: false - /stylis@4.3.2: resolution: {integrity: sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==} dev: false @@ -23185,25 +22606,6 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - /svelte@5.1.8: - resolution: {integrity: sha512-MCZVSNNqlgwKUSEZEsq2nILhzI70qv1jJy9fG9nf3I8CyJhJ2vxtPybDuP5HdB7Q9Az0WliFmqUeLEQdnY1j+g==} - engines: {node: '>=18'} - dependencies: - '@ampproject/remapping': 2.3.0 - '@jridgewell/sourcemap-codec': 1.5.0 - '@types/estree': 1.0.6 - acorn: 8.14.0 - acorn-typescript: 1.4.13(acorn@8.14.0) - aria-query: 5.3.2 - axobject-query: 4.1.0 - esm-env: 1.1.4 - esrap: 1.2.2 - is-reference: 3.0.2 - locate-character: 3.0.0 - magic-string: 0.30.12 - zimmerframe: 1.1.2 - dev: false - /svelte@5.1.9: resolution: {integrity: sha512-nzq+PPKGS2PoEWDjAcXSrKSbXmmmOAxd6dAz1IhRusUpVkFS6DMELWPyBPGwu6TpO/gsgtFXwX0M4+pAR5gzKw==} engines: {node: '>=18'} @@ -23441,7 +22843,7 @@ packages: supports-hyperlinks: 2.3.0 dev: false - /terser-webpack-plugin@5.3.10(@swc/core@1.3.101)(esbuild@0.19.11)(webpack@5.95.0): + /terser-webpack-plugin@5.3.10(@swc/core@1.3.101)(esbuild@0.19.11)(webpack@5.96.1): resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -23464,7 +22866,7 @@ packages: schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.36.0 - webpack: 5.95.0(@swc/core@1.3.101)(esbuild@0.19.11) + webpack: 5.96.1(@swc/core@1.3.101)(esbuild@0.19.11) dev: false /terser@5.36.0: @@ -23642,7 +23044,7 @@ packages: /ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - /ts-node@10.9.1(@types/node@22.8.6)(typescript@5.5.3): + /ts-node@10.9.1(@types/node@20.14.9)(typescript@5.5.3): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -23661,7 +23063,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.8.6 + '@types/node': 20.14.9 acorn: 8.14.0 acorn-walk: 8.3.4 arg: 4.1.3 @@ -23724,9 +23126,6 @@ packages: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} dev: false - /tslib@2.8.0: - resolution: {integrity: sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==} - /tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -23759,7 +23158,7 @@ packages: joycon: 3.1.1 postcss-load-config: 4.0.2(postcss@8.4.47)(ts-node@10.9.2) resolve-from: 5.0.0 - rollup: 4.24.2 + rollup: 4.24.4 source-map: 0.8.0-beta.0 sucrase: 3.35.0 tree-kill: 1.2.2 @@ -24246,8 +23645,8 @@ packages: engines: {node: '>= 0.8'} dev: true - /unplugin@1.14.1: - resolution: {integrity: sha512-lBlHbfSFPToDYp9pjXlUEFVxYLaue9f9T1HC+4OHlmj+HnMDdz9oZY+erXfoCe/5V/7gKUSY2jpXPb9S7f0f/w==} + /unplugin@1.15.0: + resolution: {integrity: sha512-jTPIs63W+DUEDW207ztbaoO7cQ4p5aVaB823LSlxpsFEU3Mykwxf3ZGC/wzxFJeZlASZYgVrWeo7LgOrqJZ8RA==} engines: {node: '>=14.0.0'} peerDependencies: webpack-sources: ^3 @@ -24294,7 +23693,7 @@ packages: dependencies: '@types/react': 18.3.11 react: 18.3.1 - tslib: 2.8.0 + tslib: 2.8.1 /use-sidecar@1.1.2(@types/react@18.3.11)(react@18.3.1): resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} @@ -24309,7 +23708,7 @@ packages: '@types/react': 18.3.11 detect-node-es: 1.1.0 react: 18.3.1 - tslib: 2.8.0 + tslib: 2.8.1 /use-sync-external-store@1.2.2(react@18.3.1): resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} @@ -24527,7 +23926,7 @@ packages: '@types/node': 20.14.9 esbuild: 0.21.5 postcss: 8.4.47 - rollup: 4.24.2 + rollup: 4.24.4 optionalDependencies: fsevents: 2.3.3 dev: true @@ -24712,7 +24111,7 @@ packages: '@peculiar/json-schema': 1.1.12 asn1js: 3.0.5 pvtsutils: 1.3.5 - tslib: 2.8.0 + tslib: 2.8.1 dev: false /webidl-conversions@3.0.1: @@ -24754,8 +24153,8 @@ packages: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} dev: true - /webpack@5.95.0(@swc/core@1.3.101)(esbuild@0.19.11): - resolution: {integrity: sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==} + /webpack@5.96.1(@swc/core@1.3.101)(esbuild@0.19.11): + resolution: {integrity: sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -24764,12 +24163,12 @@ packages: webpack-cli: optional: true dependencies: + '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.6 '@webassemblyjs/ast': 1.12.1 '@webassemblyjs/wasm-edit': 1.12.1 '@webassemblyjs/wasm-parser': 1.12.1 acorn: 8.14.0 - acorn-import-attributes: 1.9.5(acorn@8.14.0) browserslist: 4.24.2 chrome-trace-event: 1.0.4 enhanced-resolve: 5.17.1 @@ -24784,7 +24183,7 @@ packages: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(@swc/core@1.3.101)(esbuild@0.19.11)(webpack@5.95.0) + terser-webpack-plugin: 5.3.10(@swc/core@1.3.101)(esbuild@0.19.11)(webpack@5.96.1) watchpack: 2.4.2 webpack-sources: 3.2.3 transitivePeerDependencies: @@ -24855,6 +24254,14 @@ packages: dependencies: isexe: 2.0.0 + /which@4.0.0: + resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} + engines: {node: ^16.13.0 || >=18.0.0} + hasBin: true + dependencies: + isexe: 3.1.1 + dev: true + /why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} diff --git a/tools/migrate/auditlog-import.ts b/tools/migrate/auditlog-import.ts index 384d981123..a125cb1b48 100644 --- a/tools/migrate/auditlog-import.ts +++ b/tools/migrate/auditlog-import.ts @@ -1,27 +1,41 @@ -import { mysqlDrizzle, schema } from "@unkey/db"; +import type { AuditLog, AuditLogTarget } from "@unkey/db"; import { newId } from "@unkey/id"; -import mysql from "mysql2/promise"; +import ms from "ms"; -async function main() { - const conn = await mysql.createConnection( - `mysql://${process.env.DATABASE_USERNAME}:${process.env.DATABASE_PASSWORD}@${process.env.DATABASE_HOST}:3306/unkey`, - ); - - await conn.ping(); - const db = mysqlDrizzle(conn, { schema, mode: "default" }); +export type Row = { + workspaceId: string; + namespaceId: string; + requestId: string; + identifier: string; + time: string; + servicelatency: number; + success: number; +}; +async function main() { const decoder = new TextDecoder(); let buffer = ""; - const exportFile = Bun.file("./export_audit_log.json"); + const exportFile = Bun.file("./export_ratelimits.json"); + let lineCount = 0; const stream = exportFile.stream(); + const start = Date.now(); for await (const chunk of stream) { buffer += decoder.decode(chunk); + const auditLogs: AuditLog[] = []; + const auditLogTargets: AuditLogTarget[] = []; + const lines = buffer.split("\n"); while (lines.length > 1) { const line = lines.shift(); + lineCount++; + if (lineCount < 930583) { + continue; + } + const timePerRow = Math.round((Date.now() - start) / lineCount); + const _remainingTime = ms(timePerRow * (totalLines - lineCount)); const log = JSON.parse(line!) as { workspaceId: string; bucket: string; @@ -38,41 +52,51 @@ async function main() { location: string; meta: string | null; }; + + if (log.workspaceId.startsWith("test_") || log.time < Date.now() - 90 * 24 * 60 * 60 * 1000) { + continue; + } let bucketId = ""; - const bucket = await db.query.auditLogBucket.findFirst({ - where: (table, { eq, and }) => - and(eq(table.workspaceId, log.workspaceId), eq(table.name, "unkey_mutations")), - }); - if (bucket) { - bucketId = bucket.id; + const key: Key = `${log.workspaceId}::${log.bucket}`; + const cachedBucketId = bucketCache.get(key); + if (cachedBucketId) { + bucketId = cachedBucketId; } else { - bucketId = newId("auditLogBucket"); - await db.insert(schema.auditLogBucket).values({ - id: bucketId, - workspaceId: log.workspaceId, - name: "unkey_mutations", + const bucket = await db.query.auditLogBucket.findFirst({ + where: (table, { eq, and }) => + and(eq(table.workspaceId, log.workspaceId), eq(table.name, "unkey_mutations")), }); + if (bucket) { + bucketId = bucket.id; + } else { + bucketId = newId("auditLogBucket"); + await db.insert(schema.auditLogBucket).values({ + id: bucketId, + workspaceId: log.workspaceId, + name: "unkey_mutations", + }); + } } + bucketCache.set(key, bucketId); const auditLogId = newId("auditLog"); - await db - .insert(schema.auditLog) - .values({ - id: auditLogId, - workspaceId: log.workspaceId, - bucketId, - event: log.event, - time: log.time, - // A human readable description of the event - display: log.description, - remoteIp: log.location, - userAgent: log.userAgent, - actorType: log.actorType, - actorId: log.actorId, - actorName: log.actorName, - actorMeta: log.actorMeta ? JSON.parse(log.actorMeta) : null, - }) - .onDuplicateKeyUpdate({ set: { updatedAt: Date.now() } }); + auditLogs.push({ + id: auditLogId, + workspaceId: log.workspaceId, + bucketId, + event: log.event, + time: log.time, + // A human readable description of the event + display: log.description, + remoteIp: log.location, + userAgent: log.userAgent, + actorType: log.actorType, + actorId: log.actorId, + actorName: log.actorName, + actorMeta: log.actorMeta ? JSON.parse(log.actorMeta) : null, + createdAt: Date.now(), + updatedAt: null, + }); const resources: Array<{ type: string; @@ -81,8 +105,8 @@ async function main() { meta: unknown | null; }> = log.resources ? JSON.parse(log.resources) : []; - await db.insert(schema.auditLogTarget).values( - resources.map((r) => ({ + for (const r of resources) { + auditLogTargets.push({ workspaceId: log.workspaceId, bucketId, auditLogId, @@ -91,9 +115,29 @@ async function main() { id: r.id, name: r.name, meta: r.meta, - })), - ); + createdAt: Date.now(), + updatedAt: null, + }); + } } + + if (auditLogs.length > 0) { + await db + .insert(schema.auditLog) + .values(auditLogs) + .onDuplicateKeyUpdate({ set: { updatedAt: Date.now() } }); + } + if (auditLogTargets.length > 0) { + await db + .insert(schema.auditLogTarget) + .values(auditLogTargets) + .onDuplicateKeyUpdate({ + set: { + updatedAt: Date.now(), + }, + }); + } + buffer = lines[0]; } } diff --git a/tools/migrate/package.json b/tools/migrate/package.json index d572b0f189..c2c802dd1d 100644 --- a/tools/migrate/package.json +++ b/tools/migrate/package.json @@ -10,15 +10,18 @@ "dependencies": { "@axiomhq/js": "1.0.0-rc.2", "@chronark/zod-bird": "^0.3.9", + "@unkey/clickhouse": "workspace:^", "@unkey/db": "workspace:^", "@unkey/id": "workspace:^", "base-x": "^4.0.0", - "ts-semaphore": "^1.0.0", + "ms": "^2.1.3", "mysql2": "^3.10.3", + "ts-semaphore": "^1.0.0", "zod": "^3.23.5" }, "devDependencies": { "@planetscale/database": "^1.18.0", + "@types/ms": "^0.7.34", "@types/node": "20.14.9", "@unkey/tsconfig": "workspace:^", "dotenv-cli": "^7.4.1", diff --git a/tools/migrate/tinybird-export.ts b/tools/migrate/tinybird-export.ts index 82f25a6fa7..f680f6e7a7 100644 --- a/tools/migrate/tinybird-export.ts +++ b/tools/migrate/tinybird-export.ts @@ -1,23 +1,25 @@ async function main() { - const exportFile = Bun.file("./export_audit_log.json"); + const exportFile = Bun.file("./export_ratelimits.json"); const writer = exportFile.writer(); - let cursor = ""; + let cursor = 0; do { const res = await fetch( - `https://api.tinybird.co/v0/pipes/export_audit_log.json?cursor=${cursor}`, + `https://api.tinybird.co/v0/pipes/ratelimits_export_endpoint.json?cursor=${cursor}`, { headers: { Authorization: `Bearer ${process.env.TINYBIRD_TOKEN}`, }, }, ); - const body = (await res.json()) as { data: { auditLogId: string }[] }; - for (const row of body.data) { + const body = await res.text(); + // console.log(body); + const { data } = JSON.parse(body) as { data: { time: number }[] }; + for (const row of data) { writer.write(JSON.stringify(row)); writer.write("\n"); } - cursor = body.data.at(-1)?.auditLogId ?? ""; + cursor = data.at(-1)?.time ?? 0; } while (cursor); }