From 19d1246fe299f88cd01c2f10ddfd8c37836bcaee Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Tue, 11 Aug 2026 14:40:34 +0000 Subject: [PATCH] feat(db): move from SQLite Cloud to Turso MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Today's outage was a parked SQLite Cloud free node: every query and every new connection answered error 10010, and only a human restart from the dashboard could clear it. Turso sleeps too — a free group is archived after ten days idle — but it comes back through an API call, so the same failure becomes something a cron can heal instead of something that waits on someone noticing. The app talks to the database through exactly one surface, `db.sql`, so the swap lives in lib/db.ts. libSQL rows are array-like and object-like and serialize to plain named objects, which means all 77 call sites keep working untouched: `rows.length`, destructuring a single COUNT row, `NextResponse.json(rows)`, and the bounty claim's `RETURNING id` guard all behave as before. Verified by running the app against both a local file database and the real Turso database. One genuine incompatibility: libSQL refuses `undefined`, which the routes pass freely for absent optional fields. Coercing to NULL at the binding boundary keeps a missing image_url a NULL instead of a 500. The reconnect-and-retry dance is gone with the old driver. There is no long-lived websocket to go stale, so the failure that silently emptied /blog cannot recur in that form. Two pre-existing problems surfaced while recreating the schema, both fixed here: * lib/schema.sql stopped at blog_posts, omitting coupon_votes and bounties, so anyone rebuilding from it got a silently incomplete database. It is now dumped from a real migrated database. * idx_coupons_store, idx_stores_slug and idx_categories_slug were declared in schema.sql but never created by migrate.mjs, so no database built by the script has ever had them. Footer, privacy policy and llms.txt named SQLite Cloud as the database provider; the privacy page lists it as a third-party service, so leaving it would have been inaccurate about where user data lives. Not included: the data itself. The export has to come off the SQLite Cloud node, which is still parked, so the Turso database currently holds the schema and nothing else. Railway and vault variables are deliberately untouched — cutting over before the import would point production at an empty database. Co-Authored-By: Claude Opus 5 (1M context) --- .env.example | 5 +- .github/workflows/db-keepalive.yml | 12 +- apps/web/app/api/health/db/route.ts | 14 +- apps/web/app/privacy/page.tsx | 2 +- apps/web/components/Footer.tsx | 2 +- apps/web/lib/api-error.ts | 17 +- apps/web/lib/db.ts | 113 +++++----- apps/web/lib/schema.sql | 112 ++++++---- apps/web/package.json | 2 +- apps/web/public/llms.txt | 2 +- apps/web/scripts/migrate.mjs | 30 ++- pnpm-lock.yaml | 315 +++++++++++++++------------- turbo.json | 2 +- 13 files changed, 354 insertions(+), 274 deletions(-) diff --git a/.env.example b/.env.example index 66916da..37f0193 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,7 @@ -SQLITECLOUD_URL=sqlitecloud://your-project.g6.sqlite.cloud:8860/cloud.db?apikey=your-api-key +TURSO_DATABASE_URL=libsql://your-database-your-org.aws-us-west-2.turso.io +TURSO_AUTH_TOKEN=your-turso-auth-token +# For local development, point at a file instead and leave the token unset: +# TURSO_DATABASE_URL=file:./local.db NEXT_PUBLIC_BASE_URL=https://c0upons.com COINPAY_API_KEY=cp_live_your_api_key COINPAY_CLIENT_ID=cp_live_your_api_key diff --git a/.github/workflows/db-keepalive.yml b/.github/workflows/db-keepalive.yml index 772a4b7..319a2ed 100644 --- a/.github/workflows/db-keepalive.yml +++ b/.github/workflows/db-keepalive.yml @@ -1,10 +1,9 @@ name: db-keepalive -# SQLite Cloud parks a free node after a stretch of inactivity, and a parked -# node refuses new connections — so no amount of visitor traffic revives it, -# only a restart from https://dashboard.sqlitecloud.io. Prevention is the only -# automatable half: this keeps a query flowing so the node never goes idle -# long enough to be parked. +# Turso scales an idle free database to zero and wakes it automatically on the +# next request, but a group left idle for ten days is archived, and that state +# needs an explicit unarchive call. Keeping a query flowing means the ten-day +# clock never runs down. # # Two caveats worth knowing before trusting it as the sole guard: # * GitHub delays scheduled runs under load and drops them entirely when the @@ -47,5 +46,6 @@ jobs: sleep 10 done echo "::error::Database unreachable after 3 attempts (last status $status)." - echo "::error::If the body says the node is paused, restart it at https://dashboard.sqlitecloud.io" + echo "::error::If the group was archived for inactivity, unarchive it:" + echo "::error:: turso group unarchive default (or POST /v1/organizations/{org}/groups/{group}/unarchive)" exit 1 diff --git a/apps/web/app/api/health/db/route.ts b/apps/web/app/api/health/db/route.ts index 34d3e63..67c8a04 100644 --- a/apps/web/app/api/health/db/route.ts +++ b/apps/web/app/api/health/db/route.ts @@ -2,19 +2,19 @@ import { NextResponse } from 'next/server'; import { getDb } from '@/lib/db'; import { dbErrorResponse } from '@/lib/api-error'; -// Never cache: a cached 200 would keep reporting health after the node parks, -// and the keep-alive query has to actually reach SQLite Cloud to count as +// Never cache: a cached 200 would keep reporting health after the database goes +// away, and the keep-alive query has to actually reach Turso to count as // activity. export const dynamic = 'force-dynamic'; /** * Liveness probe for the database, and the query the keep-alive schedule runs. * - * SQLite Cloud parks a free node after a stretch with no queries, and a parked - * node cannot be woken by traffic — only by a restart from the dashboard. So - * the cheapest cure is to never go idle: `.github/workflows/db-keepalive.yml` - * calls this on a schedule, and the `SELECT 1` is the activity that keeps the - * node awake. It doubles as monitoring — a paused node answers 503 here loudly + * Turso archives a free group after ten days without activity, and an archived + * group needs an explicit unarchive call before it serves queries again. So the + * cheapest cure is to never go idle: `.github/workflows/db-keepalive.yml` calls + * this on a schedule, and the `SELECT 1` is the activity that resets the clock. + * It doubles as monitoring — an unreachable database answers 503 here loudly * instead of silently emptying the pages that swallow their own DB errors. */ export async function GET() { diff --git a/apps/web/app/privacy/page.tsx b/apps/web/app/privacy/page.tsx index 6b4c647..d69d109 100644 --- a/apps/web/app/privacy/page.tsx +++ b/apps/web/app/privacy/page.tsx @@ -56,7 +56,7 @@ export default function PrivacyPage() {
  • Railway — cloud hosting (privacy policy)
  • -
  • SQLite Cloud — database provider
  • +
  • Turso — database provider (privacy policy)
  • CoinPay — optional OAuth authentication for voting
  • CrawlProof — privacy-friendly analytics
diff --git a/apps/web/components/Footer.tsx b/apps/web/components/Footer.tsx index c07b578..b54d9bd 100644 --- a/apps/web/components/Footer.tsx +++ b/apps/web/components/Footer.tsx @@ -71,7 +71,7 @@ export default function Footer() {

© {new Date().getFullYear()} c0upons. Community-powered savings.

-

Built with Next.js · Powered by SQLite Cloud

+

Built with Next.js · Powered by Turso

diff --git a/apps/web/lib/api-error.ts b/apps/web/lib/api-error.ts index 2800e0b..8f93769 100644 --- a/apps/web/lib/api-error.ts +++ b/apps/web/lib/api-error.ts @@ -3,9 +3,9 @@ import { NextResponse } from 'next/server'; import { isDbPaused } from './db'; /** - * What a caller sees when the database node is parked. It names the cause - * rather than the fix: the dashboard restart is an operator action, and the - * person hitting the API can only wait. + * What a caller sees when the database cannot be reached. It names the cause + * rather than the fix: reviving an archived database is an operator action, and + * the person hitting the API can only wait. */ export const DB_PAUSED_MESSAGE = 'The coupon database is temporarily unavailable and should be back shortly.'; @@ -13,11 +13,12 @@ export const DB_PAUSED_MESSAGE = /** * Map an error caught in a route handler onto a response. * - * A paused database is a transient infrastructure state, not a bad request and - * not a bug, so it answers 503 + Retry-After instead of a blanket 500. Anything - * else keeps the route's own 500 and message, so a genuine defect still reads - * as a defect. `code` is stable for clients to branch on — the CLI prints its - * own wording for `database_paused` rather than echoing a raw HTTP status. + * An unreachable database is a transient infrastructure state, not a bad + * request and not a bug, so it answers 503 + Retry-After instead of a blanket + * 500. Anything else keeps the route's own 500 and message, so a genuine defect + * still reads as a defect. The `database_paused` code is the published wire + * contract for clients that want to branch on this rather than parse prose; it + * predates the move to Turso and is kept as-is so existing callers don't break. */ export function dbErrorResponse(err: unknown, fallback: string): NextResponse { if (isDbPaused(err)) { diff --git a/apps/web/lib/db.ts b/apps/web/lib/db.ts index fa89cec..a3d78f8 100644 --- a/apps/web/lib/db.ts +++ b/apps/web/lib/db.ts @@ -1,70 +1,67 @@ import 'server-only'; -import { Database } from '@sqlitecloud/drivers'; +import { createClient, type Client, type InValue } from '@libsql/client'; import { loadRootEnv } from './root-env'; -let client: Database | null = null; +let client: Client | null = null; -function createClient(): Database { +function getClient(): Client { + if (client) return client; loadRootEnv(); - const url = process.env.SQLITECLOUD_URL; - if (!url) throw new Error('SQLITECLOUD_URL is not set'); - return new Database({ connectionstring: url, usewebsocket: true }); + const url = process.env.TURSO_DATABASE_URL; + if (!url) throw new Error('TURSO_DATABASE_URL is not set'); + // A `file:` URL needs no token, which is what makes local runs and tests + // possible without production credentials. + client = createClient({ url, authToken: process.env.TURSO_AUTH_TOKEN }); + return client; } -// A cached client can hold a dead websocket after the SQLite Cloud node pauses -// and resumes (free-tier auto-pause). Without this, getDb() keeps returning the -// same broken connection and every query fails with "Connection unavailable" -// until the process is restarted — which is exactly how /blog silently emptied. -// Treat disconnect-class errors as recoverable: drop the client and retry once -// with a fresh connection. -function isDisconnect(err: unknown): boolean { - const msg = err instanceof Error ? err.message : String(err); - return /connection unavailable|got disconnected|disconnected|connection not been established|ERR_CONNECTION/i.test( - msg - ); -} - -// SQLite Cloud parks a free-tier node after a stretch of inactivity. Every -// query then fails with error 10010 until someone restarts it from the -// dashboard, and the node refuses new sockets too — so reconnecting cannot fix -// it. That is why this is deliberately NOT folded into isDisconnect() above: -// retrying a paused node just pays the connection cost twice before failing -// identically. Callers use it to tell "c0upons is down" (transient, 503) apart -// from "c0upons is broken" (a real 500). -export function isDbPaused(err: unknown): boolean { - const code = (err as { errorCode?: string | number } | null)?.errorCode; - if (code != null && String(code) === '10010') return true; - const msg = err instanceof Error ? err.message : String(err); - return /node has been paused|paused due to inactivity/i.test(msg); +// libSQL refuses `undefined` outright ("undefined cannot be passed as argument +// to the database"), while the routes hand it over freely for absent optional +// fields — an unscraped image_url, a webhook payload without a thumbnail. The +// old driver swallowed those; coercing here keeps a missing field a NULL +// instead of turning it into a 500. +function bind(value: unknown): InValue { + return value === undefined ? null : (value as InValue); } -async function runSql(args: unknown[]): Promise { - if (!client) client = createClient(); - try { - return await (client.sql as (...a: unknown[]) => Promise)(...args); - } catch (err) { - if (!isDisconnect(err)) throw err; - try { - (client as unknown as { close?: () => void }).close?.(); - } catch { - /* ignore close failures on an already-dead socket */ - } - client = createClient(); - return await (client.sql as (...a: unknown[]) => Promise)(...args); - } +/** + * The database handle. Only `db.sql` is used anywhere in the app, so this + * exposes exactly that: a tagged template that binds every interpolated value + * as a parameter and resolves to the rows. + * + * libSQL rows are array-like *and* object-like, and serialize to plain named + * objects, so callers keep working unchanged — `rows.length`, destructuring a + * single COUNT row, and `NextResponse.json(rows)` all behave as before. + * + * Unlike the SQLite Cloud driver this replaces, there is no long-lived + * websocket to go stale, so no reconnect dance is needed: the HTTP client + * establishes a connection per request and a dropped one cannot poison the + * cached handle the way it once silently emptied /blog. + */ +export function getDb() { + return { + // The app assigns results straight to its own row types + // (`const stores: StoreWithCount[] = await db.sql...`), which is why this + // stays `any` rather than forcing a cast at all 77 call sites. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + sql: async (strings: TemplateStringsArray, ...values: unknown[]): Promise => { + const rs = await getClient().execute({ + sql: strings.join('?'), + args: values.map(bind), + }); + return rs.rows; + }, + }; } -// Returns the shared client with its `sql` tagged-template wrapped so a stale -// connection self-heals. All call sites use only `db.sql`, so this is drop-in. -export function getDb(): Database { - if (!client) client = createClient(); - return new Proxy(client, { - get(target, prop, receiver) { - if (prop === 'sql') { - return (...args: unknown[]) => runSql(args); - } - const value = Reflect.get(target, prop, receiver); - return typeof value === 'function' ? value.bind(target) : value; - }, - }); +// Turso scales an idle free database to zero and wakes it automatically on the +// next request, so a brief stall is normal rather than an outage. A group left +// idle for ten days is archived instead, and that state does need an explicit +// unarchive call — the shapes below are the ones seen for a database that is +// gone or unreachable rather than merely asleep. Callers use this to tell +// "c0upons is down" (transient, 503) apart from "c0upons is broken" (a real +// 500). +export function isDbPaused(err: unknown): boolean { + const msg = err instanceof Error ? err.message : String(err); + return /archived|not found|unavailable|SERVER_ERROR|502|503/i.test(msg); } diff --git a/apps/web/lib/schema.sql b/apps/web/lib/schema.sql index 3ec19c6..785f375 100644 --- a/apps/web/lib/schema.sql +++ b/apps/web/lib/schema.sql @@ -1,56 +1,92 @@ +-- Reference copy of the c0upons schema. +-- +-- scripts/migrate.mjs is what actually creates and evolves the database; this +-- file documents the result. It is dumped from a database built by that script, +-- so the two agree — an earlier copy of this file stopped at blog_posts and +-- omitted coupon_votes and bounties entirely, which made rebuilding from it +-- silently incomplete. Regenerate with `turso db shell ".schema"` after +-- changing the migration rather than hand-editing. + CREATE TABLE IF NOT EXISTS categories ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL, - slug TEXT NOT NULL UNIQUE + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + slug TEXT NOT NULL UNIQUE, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS stores ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL, - slug TEXT NOT NULL UNIQUE, - logo_url TEXT, - website TEXT, + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + slug TEXT NOT NULL UNIQUE, + logo_url TEXT, + website TEXT, category_id INTEGER REFERENCES categories(id), - created_at TEXT DEFAULT (datetime('now')) + created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS coupons ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - store_id INTEGER NOT NULL REFERENCES stores(id), - code TEXT, - title TEXT NOT NULL, - description TEXT, - discount TEXT, - discount_type TEXT, + id INTEGER PRIMARY KEY AUTOINCREMENT, + store_id INTEGER NOT NULL REFERENCES stores(id) ON DELETE CASCADE, + code TEXT, + title TEXT NOT NULL, + description TEXT, + discount TEXT, + discount_type TEXT, discount_value REAL, - expiry_date TEXT, - verified INTEGER DEFAULT 0, - votes INTEGER DEFAULT 0, - url TEXT, - image_url TEXT, - created_at TEXT DEFAULT (datetime('now')) + expiry_date TEXT, + url TEXT, + image_url TEXT, + votes INTEGER NOT NULL DEFAULT 0, + verified INTEGER NOT NULL DEFAULT 0, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -CREATE INDEX IF NOT EXISTS idx_coupons_store ON coupons(store_id); -CREATE INDEX IF NOT EXISTS idx_stores_slug ON stores(slug); -CREATE INDEX IF NOT EXISTS idx_categories_slug ON categories(slug); +CREATE TABLE IF NOT EXISTS coupon_votes ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + coupon_id INTEGER NOT NULL REFERENCES coupons(id) ON DELETE CASCADE, + voter_did TEXT NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + UNIQUE(coupon_id, voter_did) +); CREATE TABLE IF NOT EXISTS blog_posts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - slug TEXT NOT NULL UNIQUE, - title TEXT NOT NULL, - excerpt TEXT, - content TEXT NOT NULL DEFAULT '', - cover_image TEXT, + id INTEGER PRIMARY KEY AUTOINCREMENT, + slug TEXT NOT NULL UNIQUE, + title TEXT NOT NULL, + excerpt TEXT, + content TEXT NOT NULL DEFAULT '', + cover_image TEXT, thumbnail_image TEXT, - banner_image TEXT, - author TEXT, - status TEXT NOT NULL DEFAULT 'published', - source TEXT, - source_id TEXT, - published_at TEXT DEFAULT (datetime('now')), - updated_at TEXT DEFAULT (datetime('now')) + banner_image TEXT, + author TEXT, + status TEXT NOT NULL DEFAULT 'published', + source TEXT, + source_id TEXT, + published_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); +CREATE TABLE IF NOT EXISTS bounties ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + public_id TEXT, + creator_did TEXT NOT NULL, + store_id INTEGER REFERENCES stores(id), + store_name TEXT, + title TEXT NOT NULL, + description TEXT, + url TEXT, + reward_usd REAL NOT NULL, + status TEXT NOT NULL DEFAULT 'open', + payment_id TEXT, + coupon_id INTEGER REFERENCES coupons(id), + claimer_did TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX IF NOT EXISTS idx_coupons_store ON coupons(store_id); +CREATE INDEX IF NOT EXISTS idx_stores_slug ON stores(slug); +CREATE INDEX IF NOT EXISTS idx_categories_slug ON categories(slug); CREATE INDEX IF NOT EXISTS idx_blog_posts_status_published ON blog_posts(status, published_at); CREATE UNIQUE INDEX IF NOT EXISTS idx_blog_posts_source_id ON blog_posts(source, source_id); +CREATE UNIQUE INDEX IF NOT EXISTS idx_bounties_public_id ON bounties(public_id); diff --git a/apps/web/package.json b/apps/web/package.json index bb7f68b..2ae1c09 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -15,9 +15,9 @@ }, "dependencies": { "@anthropic-ai/sdk": "^0.104.1", + "@libsql/client": "^0.17.4", "@profullstack/stack": "^0.1.3", "@serwist/next": "^9.5.0", - "@sqlitecloud/drivers": "^1.0.880", "next": "16.2.7", "posthog-js": "^1.381.0", "react": "19.2.4", diff --git a/apps/web/public/llms.txt b/apps/web/public/llms.txt index cd6e7ed..9a4f20c 100644 --- a/apps/web/public/llms.txt +++ b/apps/web/public/llms.txt @@ -30,4 +30,4 @@ c0upons is a free, open-source coupon aggregator where anyone can browse deals, - **Community-driven:** Anyone can submit codes; votes surface the best ones. - **Open source:** MIT licensed, source on GitHub. - **API:** `GET /api/coupons`, `GET /api/stores`, `GET /api/search?q=query` -- **Stack:** Next.js 16, SQLite Cloud, Tailwind CSS v4, deployed on Railway. +- **Stack:** Next.js 16, Turso (libSQL), Tailwind CSS v4, deployed on Railway. diff --git a/apps/web/scripts/migrate.mjs b/apps/web/scripts/migrate.mjs index 3ba80b3..c3b8d64 100644 --- a/apps/web/scripts/migrate.mjs +++ b/apps/web/scripts/migrate.mjs @@ -2,9 +2,11 @@ * Database migration — creates tables and optionally seeds sample data. * Run with: pnpm db:migrate (from apps/web) * - * Requires SQLITECLOUD_URL in the repo root .env or as an env var. + * Requires TURSO_DATABASE_URL (and TURSO_AUTH_TOKEN for a remote database) in + * the repo root .env or as env vars. A `file:` URL runs it against a local + * SQLite file, which is how the schema is exercised in tests. */ -import { Database } from '@sqlitecloud/drivers'; +import { createClient } from '@libsql/client'; import { readFileSync } from 'fs'; import { resolve, dirname } from 'path'; import { fileURLToPath } from 'url'; @@ -31,13 +33,24 @@ try { } } catch { /* no root .env — fall through to existing env */ } -const url = process.env.SQLITECLOUD_URL; +const url = process.env.TURSO_DATABASE_URL; if (!url) { - console.error('SQLITECLOUD_URL not set'); + console.error('TURSO_DATABASE_URL not set'); process.exit(1); } -const db = new Database({ connectionstring: url, usewebsocket: true }); +// Mirrors lib/db.ts: expose `db.sql` as a tagged template so the migration +// statements below read the same as the app's queries. +const client = createClient({ url, authToken: process.env.TURSO_AUTH_TOKEN }); +const db = { + sql: async (strings, ...values) => { + const rs = await client.execute({ + sql: strings.join('?'), + args: values.map((v) => (v === undefined ? null : v)), + }); + return rs.rows; + }, +}; console.log('Running migrations...'); @@ -86,6 +99,13 @@ await addColumn(() => db.sql`ALTER TABLE coupons ADD COLUMN discount_type TEXT`) await addColumn(() => db.sql`ALTER TABLE coupons ADD COLUMN discount_value REAL`); await addColumn(() => db.sql`ALTER TABLE coupons ADD COLUMN image_url TEXT`); await addColumn(() => db.sql`ALTER TABLE coupons ADD COLUMN url TEXT`); +// These three indexes were declared in lib/schema.sql but never created here, +// so every database built by this script — production included — has been +// running without them. Every listing page joins coupons to stores and looks +// stores up by slug, so they are worth having. +await db.sql`CREATE INDEX IF NOT EXISTS idx_coupons_store ON coupons(store_id)`; +await db.sql`CREATE INDEX IF NOT EXISTS idx_stores_slug ON stores(slug)`; +await db.sql`CREATE INDEX IF NOT EXISTS idx_categories_slug ON categories(slug)`; console.log(' coupons'); await db.sql` diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 86aa382..1d48652 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,15 +48,15 @@ importers: '@anthropic-ai/sdk': specifier: ^0.104.1 version: 0.104.1(zod@4.4.3) + '@libsql/client': + specifier: ^0.17.4 + version: 0.17.4 '@profullstack/stack': specifier: ^0.1.3 version: 0.1.3(next@16.2.7(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) '@serwist/next': specifier: ^9.5.0 version: 9.5.11(next@16.2.7(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@sqlitecloud/drivers': - specifier: ^1.0.880 - version: 1.0.880 next: specifier: 16.2.7 version: 16.2.7(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -566,12 +566,72 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@libsql/client@0.17.4': + resolution: {integrity: sha512-lYayFWasDV78A+TjlEhr6ubb3odBV6OHjb+wdp8VQcyWWAEIjuwbCHaraEUS4m4yWoo0BvZo96It4VdzZRmRWw==} + + '@libsql/core@0.17.4': + resolution: {integrity: sha512-LqF9gIvnJ38nmAH1y/ChizHqDO/MO1wLgA96XrraulEEbqXxLjleSH92YWTolbuJKgPUmGu4aJk9W3UnAcxLOQ==} + + '@libsql/darwin-arm64@0.5.29': + resolution: {integrity: sha512-K+2RIB1OGFPYQbfay48GakLhqf3ArcbHqPFu7EZiaUcRgFcdw8RoltsMyvbj5ix2fY0HV3Q3Ioa/ByvQdaSM0A==} + cpu: [arm64] + os: [darwin] + + '@libsql/darwin-x64@0.5.29': + resolution: {integrity: sha512-OtT+KFHsKFy1R5FVadr8FJ2Bb1mghtXTyJkxv0trocq7NuHntSki1eUbxpO5ezJesDvBlqFjnWaYYY516QNLhQ==} + cpu: [x64] + os: [darwin] + + '@libsql/hrana-client@0.10.0': + resolution: {integrity: sha512-OoA4EMqRAC7kn7V2P6EQqRcpZf2W+AjsNIyCizBg339Tq/aMC7sRnzs3SklderhmQWAqEzvv8A2vhxVmWpkVvw==} + + '@libsql/isomorphic-ws@0.1.5': + resolution: {integrity: sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg==} + + '@libsql/linux-arm-gnueabihf@0.5.29': + resolution: {integrity: sha512-CD4n4zj7SJTHso4nf5cuMoWoMSS7asn5hHygsDuhRl8jjjCTT3yE+xdUvI4J7zsyb53VO5ISh4cwwOtf6k2UhQ==} + cpu: [arm] + os: [linux] + + '@libsql/linux-arm-musleabihf@0.5.29': + resolution: {integrity: sha512-2Z9qBVpEJV7OeflzIR3+l5yAd4uTOLxklScYTwpZnkm2vDSGlC1PRlueLaufc4EFITkLKXK2MWBpexuNJfMVcg==} + cpu: [arm] + os: [linux] + + '@libsql/linux-arm64-gnu@0.5.29': + resolution: {integrity: sha512-gURBqaiXIGGwFNEaUj8Ldk7Hps4STtG+31aEidCk5evMMdtsdfL3HPCpvys+ZF/tkOs2MWlRWoSq7SOuCE9k3w==} + cpu: [arm64] + os: [linux] + + '@libsql/linux-arm64-musl@0.5.29': + resolution: {integrity: sha512-fwgYZ0H8mUkyVqXZHF3mT/92iIh1N94Owi/f66cPVNsk9BdGKq5gVpoKO+7UxaNzuEH1roJp2QEwsCZMvBLpqg==} + cpu: [arm64] + os: [linux] + + '@libsql/linux-x64-gnu@0.5.29': + resolution: {integrity: sha512-y14V0vY0nmMC6G0pHeJcEarcnGU2H6cm21ZceRkacWHvQAEhAG0latQkCtoS2njFOXiYIg+JYPfAoWKbi82rkg==} + cpu: [x64] + os: [linux] + + '@libsql/linux-x64-musl@0.5.29': + resolution: {integrity: sha512-gquqwA/39tH4pFl+J9n3SOMSymjX+6kZ3kWgY3b94nXFTwac9bnFNMffIomgvlFaC4ArVqMnOZD3nuJ3H3VO1w==} + cpu: [x64] + os: [linux] + + '@libsql/win32-x64-msvc@0.5.29': + resolution: {integrity: sha512-4/0CvEdhi6+KjMxMaVbFM2n2Z44escBRoEYpR+gZg64DdetzGnYm8mcNLcoySaDJZNaBd6wz5DNdgRmcI4hXcg==} + cpu: [x64] + os: [win32] + '@napi-rs/wasm-runtime@1.1.4': resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} peerDependencies: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 + '@neon-rs/load@0.0.4': + resolution: {integrity: sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==} + '@next/env@16.2.7': resolution: {integrity: sha512-tMJizPlj6ZYpBMMdK8S0LJufrP4QTdR6pcv9KQ/bVETPAmg0j1mlHE9G2c38UyGHxoBapgwuj7XjbGJ2RcDFOg==} @@ -729,27 +789,6 @@ packages: typescript: optional: true - '@socket.io/component-emitter@3.1.2': - resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} - - '@sqlitecloud/drivers@1.0.880': - resolution: {integrity: sha512-UtO2+KQ/aEo3FOyB5SAiN+J746JI/ZI20qzfp1j2C+COqFRr7ewkFE5My9vRp3/45slmQbt0lJjL/BBI6+D8xw==} - engines: {node: '>=18.0'} - peerDependencies: - '@craftzdog/react-native-buffer': '*' - react-native-quick-base64: '*' - react-native-tcp-socket: '*' - react-native-url-polyfill: '*' - peerDependenciesMeta: - '@craftzdog/react-native-buffer': - optional: true - react-native-quick-base64: - optional: true - react-native-tcp-socket: - optional: true - react-native-url-polyfill: - optional: true - '@stablelib/base64@1.0.1': resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} @@ -903,6 +942,9 @@ packages: '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + '@typescript-eslint/eslint-plugin@8.60.1': resolution: {integrity: sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1158,9 +1200,6 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.10.33: resolution: {integrity: sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==} engines: {node: '>=6.0.0'} @@ -1182,9 +1221,6 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -1304,6 +1340,10 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + detect-libc@2.0.2: + resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} + engines: {node: '>=8'} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -1345,13 +1385,6 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - engine.io-client@6.6.5: - resolution: {integrity: sha512-QCwxUDULPlXv8F6tqMMKx5dNkTe6OaBYRMPYeXKBlyOoKvAmE0ac6pW7fFhSscJ/5SI7666/U/B+MElbsrJlIg==} - - engine.io-parser@5.2.3: - resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} - engines: {node: '>=10.0.0'} - enhanced-resolve@5.22.2: resolution: {integrity: sha512-0rxICaFZ7NQho/sHely2bvOPRP0Eu2B0NZ9zM54YvRvWMn7jfz3DmnOZDR9LlXDdDcqntAVc6Hfy4gr/tdH/Ag==} engines: {node: '>=10.13.0'} @@ -1529,9 +1562,6 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - eventemitter3@5.0.4: - resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1698,9 +1728,6 @@ packages: idb@8.0.3: resolution: {integrity: sha512-LtwtVyVYO5BqRvcsKuB2iUMnHwPVByPCXFXOpuU96IZPPoPN6xjOGxZQ74pgSVVLQWtUOYgyeL4GE98BY5D3wg==} - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1854,6 +1881,9 @@ packages: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true + js-base64@3.9.2: + resolution: {integrity: sha512-6zayE8QlUdiweYI6cETD/XBSqFcoCUlufn/29PJR99r82x1yDnIprRca0YvAYpAW+ez0GuQkVBC6xG5QkD7OjA==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -1912,6 +1942,11 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + libsql@0.5.29: + resolution: {integrity: sha512-8lMP8iMgiBzzoNbAPQ59qdVcj6UaE/Vnm+fiwX4doX4Narook0a4GPKWBEv+CR8a1OwbfkgL18uBfBjWdF0Fzg==} + cpu: [x64, arm64, wasm32, arm] + os: [darwin, linux, win32] + lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} @@ -2007,9 +2042,6 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lz4js@0.2.0: - resolution: {integrity: sha512-gY2Ia9Lm7Ep8qMiuGRhvUq0Q7qUereeldZPP1PMEJxPtEWHJLqw9pgX68oHajBH0nzJK4MaZEA/YNV3jT8u8Bg==} - magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -2203,6 +2235,9 @@ packages: resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} engines: {node: ^14.13.1 || >=16.0.0} + promise-limit@2.7.0: + resolution: {integrity: sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -2343,14 +2378,6 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - socket.io-client@4.8.3: - resolution: {integrity: sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==} - engines: {node: '>=10.0.0'} - - socket.io-parser@4.2.6: - resolution: {integrity: sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==} - engines: {node: '>=10.0.0'} - source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -2456,10 +2483,6 @@ packages: tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} - tr46@5.1.1: - resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} - engines: {node: '>=18'} - ts-algebra@2.0.0: resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} @@ -2545,14 +2568,6 @@ packages: webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} - webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - - whatwg-url@14.2.0: - resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} - engines: {node: '>=18'} - whatwg-url@7.1.0: resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} @@ -2581,8 +2596,8 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - ws@8.20.1: - resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==} + ws@8.21.3: + resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -2593,10 +2608,6 @@ packages: utf-8-validate: optional: true - xmlhttprequest-ssl@2.1.2: - resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} - engines: {node: '>=0.4.0'} - yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -3001,6 +3012,64 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@libsql/client@0.17.4': + dependencies: + '@libsql/core': 0.17.4 + '@libsql/hrana-client': 0.10.0 + js-base64: 3.9.2 + libsql: 0.5.29 + promise-limit: 2.7.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@libsql/core@0.17.4': + dependencies: + js-base64: 3.9.2 + + '@libsql/darwin-arm64@0.5.29': + optional: true + + '@libsql/darwin-x64@0.5.29': + optional: true + + '@libsql/hrana-client@0.10.0': + dependencies: + '@libsql/isomorphic-ws': 0.1.5 + js-base64: 3.9.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@libsql/isomorphic-ws@0.1.5': + dependencies: + '@types/ws': 8.18.1 + ws: 8.21.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@libsql/linux-arm-gnueabihf@0.5.29': + optional: true + + '@libsql/linux-arm-musleabihf@0.5.29': + optional: true + + '@libsql/linux-arm64-gnu@0.5.29': + optional: true + + '@libsql/linux-arm64-musl@0.5.29': + optional: true + + '@libsql/linux-x64-gnu@0.5.29': + optional: true + + '@libsql/linux-x64-musl@0.5.29': + optional: true + + '@libsql/win32-x64-msvc@0.5.29': + optional: true + '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 @@ -3008,6 +3077,8 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true + '@neon-rs/load@0.0.4': {} + '@next/env@16.2.7': {} '@next/eslint-plugin-next@16.2.7': @@ -3131,21 +3202,6 @@ snapshots: transitivePeerDependencies: - browserslist - '@socket.io/component-emitter@3.1.2': {} - - '@sqlitecloud/drivers@1.0.880': - dependencies: - buffer: 6.0.3 - eventemitter3: 5.0.4 - lz4js: 0.2.0 - socket.io-client: 4.8.3 - socket.io-parser: 4.2.6 - whatwg-url: 14.2.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - '@stablelib/base64@1.0.1': {} '@swc/helpers@0.5.15': @@ -3268,6 +3324,10 @@ snapshots: '@types/trusted-types@2.0.7': {} + '@types/ws@8.18.1': + dependencies: + '@types/node': 22.19.19 + '@typescript-eslint/eslint-plugin@8.60.1(@typescript-eslint/parser@8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -3535,8 +3595,6 @@ snapshots: balanced-match@4.0.4: {} - base64-js@1.5.1: {} - baseline-browser-mapping@2.10.33: {} brace-expansion@1.1.15: @@ -3560,11 +3618,6 @@ snapshots: node-releases: 2.0.47 update-browserslist-db: 1.2.3(browserslist@4.28.2) - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -3671,6 +3724,8 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + detect-libc@2.0.2: {} + detect-libc@2.1.2: {} doctrine@2.1.0: @@ -3713,20 +3768,6 @@ snapshots: emoji-regex@9.2.2: {} - engine.io-client@6.6.5: - dependencies: - '@socket.io/component-emitter': 3.1.2 - debug: 4.4.3 - engine.io-parser: 5.2.3 - ws: 8.20.1 - xmlhttprequest-ssl: 2.1.2 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - engine.io-parser@5.2.3: {} - enhanced-resolve@5.22.2: dependencies: graceful-fs: 4.2.11 @@ -4075,8 +4116,6 @@ snapshots: esutils@2.0.3: {} - eventemitter3@5.0.4: {} - fast-deep-equal@3.1.3: {} fast-glob@3.3.1: @@ -4241,8 +4280,6 @@ snapshots: idb@8.0.3: {} - ieee754@1.2.1: {} - ignore@5.3.2: {} ignore@7.0.5: {} @@ -4395,6 +4432,8 @@ snapshots: jiti@2.7.0: {} + js-base64@3.9.2: {} + js-tokens@4.0.0: {} js-yaml@4.2.0: @@ -4448,6 +4487,21 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + libsql@0.5.29: + dependencies: + '@neon-rs/load': 0.0.4 + detect-libc: 2.0.2 + optionalDependencies: + '@libsql/darwin-arm64': 0.5.29 + '@libsql/darwin-x64': 0.5.29 + '@libsql/linux-arm-gnueabihf': 0.5.29 + '@libsql/linux-arm-musleabihf': 0.5.29 + '@libsql/linux-arm64-gnu': 0.5.29 + '@libsql/linux-arm64-musl': 0.5.29 + '@libsql/linux-x64-gnu': 0.5.29 + '@libsql/linux-x64-musl': 0.5.29 + '@libsql/win32-x64-msvc': 0.5.29 + lightningcss-android-arm64@1.32.0: optional: true @@ -4520,8 +4574,6 @@ snapshots: dependencies: yallist: 3.1.1 - lz4js@0.2.0: {} - magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -4725,6 +4777,8 @@ snapshots: pretty-bytes@6.1.1: {} + promise-limit@2.7.0: {} + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 @@ -4926,24 +4980,6 @@ snapshots: signal-exit@4.1.0: {} - socket.io-client@4.8.3: - dependencies: - '@socket.io/component-emitter': 3.1.2 - debug: 4.4.3 - engine.io-client: 6.6.5 - socket.io-parser: 4.2.6 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - socket.io-parser@4.2.6: - dependencies: - '@socket.io/component-emitter': 3.1.2 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - source-map-js@1.2.1: {} source-map@0.8.0-beta.0: @@ -5060,10 +5096,6 @@ snapshots: dependencies: punycode: 2.3.1 - tr46@5.1.1: - dependencies: - punycode: 2.3.1 - ts-algebra@2.0.0: {} ts-api-utils@2.5.0(typescript@5.9.3): @@ -5198,13 +5230,6 @@ snapshots: webidl-conversions@4.0.2: {} - webidl-conversions@7.0.0: {} - - whatwg-url@14.2.0: - dependencies: - tr46: 5.1.1 - webidl-conversions: 7.0.0 - whatwg-url@7.1.0: dependencies: lodash.sortby: 4.7.0 @@ -5258,9 +5283,7 @@ snapshots: word-wrap@1.2.5: {} - ws@8.20.1: {} - - xmlhttprequest-ssl@2.1.2: {} + ws@8.21.3: {} yallist@3.1.1: {} diff --git a/turbo.json b/turbo.json index b6e2e75..1ad2fcc 100644 --- a/turbo.json +++ b/turbo.json @@ -10,7 +10,7 @@ "build": { "dependsOn": ["^build"], "outputs": [".next/**", "!.next/cache/**", "dist/**"], - "env": ["SQLITECLOUD_URL"] + "env": ["TURSO_DATABASE_URL", "TURSO_AUTH_TOKEN"] }, "lint": { "outputs": [] }, "typecheck": { "outputs": [] },