From 0f46e3352c42056dcb4aef3d0c10b782495f0630 Mon Sep 17 00:00:00 2001 From: sojetunde8 Date: Mon, 28 Sep 2026 01:42:25 +0100 Subject: [PATCH 01/13] feat: conditional streams, analytics stack, developer portal, copy feedback - #1482: oracle-triggered conditional streaming. create_conditional_stream escrows funds against ConditionalMilestone tranches (TimeOnly, PriceTarget, OracleAttestation); verify_and_unlock_milestone evaluates the condition (SEP-40-style oracle lastprice with a 3600s freshness window, or signer-authorized single-use attestations) and promotes the tranche into the audited step-unlock path. Emits milestone_condition_unlocked. Errors 28-35 appended to StreamError. - #1480: TimescaleDB hypertable + hourly/daily continuous aggregates + compression policy (migration 20261001000000), analytics.service with live-aggregation fallback for stock PostgreSQL, /api/v1/analytics/{tvl,historical,defillama} endpoints, vitest suite. - #1481: Docusaurus developer portal in docs-site/ (contracts, backend, SDKs, guides, API reference), interactive Soroban RPC playground and OpenAPI try-it-out console, Mermaid sequence diagrams, and a deploy-docs CI workflow that builds on PRs. - #1483: reusable CopyButton with 2s checkmark, tooltip and keyboard support; replaces raw clipboard handlers in StreamDetailsModal and ShareAddressModal. Closes #1482, closes #1480, closes #1481, closes #1483. --- .github/workflows/deploy-docs.yml | 56 ++++ .../migration.sql | 77 +++++ .../src/controllers/analytics.controller.ts | 97 ++++++ backend/src/routes/v1/analytics.routes.ts | 22 ++ backend/src/routes/v1/index.ts | 2 + backend/src/services/analytics.service.ts | 246 ++++++++++++++ backend/tests/analytics.test.ts | 144 ++++++++ contracts/stream_contract/src/errors.rs | 20 ++ contracts/stream_contract/src/events.rs | 16 + contracts/stream_contract/src/lib.rs | 315 +++++++++++++++++- contracts/stream_contract/src/types.rs | 96 +++++- docs-site/README.md | 34 ++ docs-site/docs/api-reference/index.md | 39 +++ docs-site/docs/api-reference/playground.md | 30 ++ docs-site/docs/backend/analytics.md | 40 +++ docs-site/docs/backend/dead-letter-triage.md | 26 ++ docs-site/docs/backend/event-ingestion.md | 35 ++ docs-site/docs/backend/overview.md | 38 +++ docs-site/docs/backend/streams-sse.md | 32 ++ docs-site/docs/backend/webhooks.md | 41 +++ docs-site/docs/contracts/batch-operations.md | 34 ++ .../docs/contracts/conditional-streams.md | 86 +++++ docs-site/docs/contracts/emergency-pause.md | 39 +++ docs-site/docs/contracts/lifecycle.md | 42 +++ docs-site/docs/contracts/soroban-reference.md | 39 +++ docs-site/docs/contracts/storage-ttl.md | 28 ++ docs-site/docs/core-concepts.md | 40 +++ docs-site/docs/guides/dao-payroll.md | 39 +++ .../guides/milestone-vesting-for-grants.md | 47 +++ .../docs/guides/saas-subscription-billing.md | 35 ++ docs-site/docs/intro.md | 27 ++ docs-site/docs/quickstart.md | 67 ++++ docs-site/docs/sdks/python.md | 29 ++ docs-site/docs/sdks/react.md | 36 ++ docs-site/docs/sdks/rust.md | 34 ++ docs-site/docs/sdks/typescript.md | 42 +++ docs-site/docusaurus.config.js | 72 ++++ docs-site/package.json | 28 ++ docs-site/sidebars.js | 63 ++++ docs-site/src/components/OpenApiConsole.tsx | 71 ++++ .../src/components/SorobanPlayground.tsx | 207 ++++++++++++ docs-site/src/css/custom.css | 23 ++ .../src/components/dashboard/CopyButton.tsx | 96 ++++++ .../dashboard/ShareAddressModal.tsx | 43 +-- .../dashboard/StreamDetailsModal.tsx | 36 +- 45 files changed, 2646 insertions(+), 63 deletions(-) create mode 100644 .github/workflows/deploy-docs.yml create mode 100644 backend/prisma/migrations/20261001000000_add_timescaledb_analytics/migration.sql create mode 100644 backend/src/controllers/analytics.controller.ts create mode 100644 backend/src/routes/v1/analytics.routes.ts create mode 100644 backend/src/services/analytics.service.ts create mode 100644 backend/tests/analytics.test.ts create mode 100644 docs-site/README.md create mode 100644 docs-site/docs/api-reference/index.md create mode 100644 docs-site/docs/api-reference/playground.md create mode 100644 docs-site/docs/backend/analytics.md create mode 100644 docs-site/docs/backend/dead-letter-triage.md create mode 100644 docs-site/docs/backend/event-ingestion.md create mode 100644 docs-site/docs/backend/overview.md create mode 100644 docs-site/docs/backend/streams-sse.md create mode 100644 docs-site/docs/backend/webhooks.md create mode 100644 docs-site/docs/contracts/batch-operations.md create mode 100644 docs-site/docs/contracts/conditional-streams.md create mode 100644 docs-site/docs/contracts/emergency-pause.md create mode 100644 docs-site/docs/contracts/lifecycle.md create mode 100644 docs-site/docs/contracts/soroban-reference.md create mode 100644 docs-site/docs/contracts/storage-ttl.md create mode 100644 docs-site/docs/core-concepts.md create mode 100644 docs-site/docs/guides/dao-payroll.md create mode 100644 docs-site/docs/guides/milestone-vesting-for-grants.md create mode 100644 docs-site/docs/guides/saas-subscription-billing.md create mode 100644 docs-site/docs/intro.md create mode 100644 docs-site/docs/quickstart.md create mode 100644 docs-site/docs/sdks/python.md create mode 100644 docs-site/docs/sdks/react.md create mode 100644 docs-site/docs/sdks/rust.md create mode 100644 docs-site/docs/sdks/typescript.md create mode 100644 docs-site/docusaurus.config.js create mode 100644 docs-site/package.json create mode 100644 docs-site/sidebars.js create mode 100644 docs-site/src/components/OpenApiConsole.tsx create mode 100644 docs-site/src/components/SorobanPlayground.tsx create mode 100644 docs-site/src/css/custom.css create mode 100644 frontend/src/components/dashboard/CopyButton.tsx diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 00000000..9362f5f3 --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,56 @@ +name: Deploy Docs + +on: + pull_request: + paths: + - "docs-site/**" + - ".github/workflows/deploy-docs.yml" + push: + branches: [main] + paths: + - "docs-site/**" + - ".github/workflows/deploy-docs.yml" + +defaults: + run: + working-directory: docs-site + +jobs: + build-docs: + name: Build developer portal + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: docs-site/package-lock.json + + - name: Install dependencies + run: npm install + + - name: Build site (fails on broken links / missing assets) + run: npm run build + + - name: Upload pages artifact + if: github.ref == 'refs/heads/main' + uses: actions/upload-pages-artifact@v3 + with: + path: docs-site/build + + deploy-docs: + name: Deploy to GitHub Pages + if: github.ref == 'refs/heads/main' + needs: build-docs + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/backend/prisma/migrations/20261001000000_add_timescaledb_analytics/migration.sql b/backend/prisma/migrations/20261001000000_add_timescaledb_analytics/migration.sql new file mode 100644 index 00000000..86185628 --- /dev/null +++ b/backend/prisma/migrations/20261001000000_add_timescaledb_analytics/migration.sql @@ -0,0 +1,77 @@ +-- TimescaleDB analytics backbone (#1480). +-- +-- Protocol TVL, streaming velocity and withdrawn totals change with every +-- ledger second, so they are snapshotted into a hypertable and pre-aggregated +-- with continuous aggregates instead of being recomputed from the raw Stream +-- and StreamEvent tables on every request. +-- +-- Requires the timescaledb extension to be available on the PostgreSQL +-- instance (see docker-compose.yml). If the extension is not installed the +-- statements below fail; the analytics API falls back to live Prisma +-- aggregation in that case, so existing deployments keep working. + +CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE; + +-- Hypertable for continuous stream flow snapshots. One row per +-- (token_address, snapshot tick) produced by the indexer's aggregation pass. +CREATE TABLE IF NOT EXISTS stream_flow_snapshots ( + time TIMESTAMPTZ NOT NULL, + token_address TEXT NOT NULL, + active_stream_count INT NOT NULL, + total_locked_amount NUMERIC(38, 0) NOT NULL, + cumulative_streamed_amount NUMERIC(38, 0) NOT NULL, + cumulative_withdrawn_amount NUMERIC(38, 0) NOT NULL, + flow_velocity_per_second NUMERIC(38, 0) NOT NULL +); + +SELECT create_hypertable('stream_flow_snapshots', 'time', if_not_exists => TRUE); + +CREATE INDEX IF NOT EXISTS idx_stream_flow_snapshots_token_time + ON stream_flow_snapshots (token_address, time DESC); + +-- Hourly continuous aggregate: average TVL, summed velocity and peak stream +-- count per token, refreshable incrementally by TimescaleDB policies. +CREATE MATERIALIZED VIEW IF NOT EXISTS hourly_protocol_metrics +WITH (timescaledb.continuous) AS +SELECT time_bucket('1 hour', time) AS bucket, + token_address, + AVG(total_locked_amount) AS avg_tvl, + SUM(flow_velocity_per_second) AS aggregate_velocity, + MAX(active_stream_count) AS peak_streams +FROM stream_flow_snapshots +GROUP BY bucket, token_address +WITH NO DATA; + +-- Daily continuous aggregate for 30d/90d/1y historical charts. +CREATE MATERIALIZED VIEW IF NOT EXISTS daily_protocol_metrics +WITH (timescaledb.continuous) AS +SELECT time_bucket('1 day', time) AS bucket, + token_address, + AVG(total_locked_amount) AS avg_tvl, + SUM(flow_velocity_per_second) AS aggregate_velocity, + MAX(active_stream_count) AS peak_streams, + MAX(cumulative_withdrawn_amount) AS cumulative_withdrawn +FROM stream_flow_snapshots +GROUP BY bucket, token_address +WITH NO DATA; + +-- Refresh policies: hourly aggregate refreshes shortly after each hour ends, +-- daily aggregate in the small hours. Both retain their full window. +SELECT add_continuous_aggregate_policy('hourly_protocol_metrics', + start_offset => INTERVAL '3 days', + end_offset => INTERVAL '1 hour', + schedule_interval => INTERVAL '1 hour'); + +SELECT add_continuous_aggregate_policy('daily_protocol_metrics', + start_offset => INTERVAL '40 days', + end_offset => INTERVAL '1 day', + schedule_interval => INTERVAL '1 day'); + +-- Compress raw snapshots older than 14 days to keep the hypertable small +-- while retaining every data point for the daily rollups. +ALTER TABLE stream_flow_snapshots SET ( + timescaledb.compress, + timescaledb.compress_segmentby = 'token_address' +); + +SELECT add_compression_policy('stream_flow_snapshots', INTERVAL '14 days'); diff --git a/backend/src/controllers/analytics.controller.ts b/backend/src/controllers/analytics.controller.ts new file mode 100644 index 00000000..7a0980ba --- /dev/null +++ b/backend/src/controllers/analytics.controller.ts @@ -0,0 +1,97 @@ +/** + * Analytics controller — HTTP surface for the #1480 analytics stack. + * + * Read-only endpoints; no auth beyond the API's global middleware because + * every payload here is public protocol statistics (the same numbers a + * block explorer would show). + */ + +import type { Request, Response } from "express"; +import { + getDefiLlamaAdapter, + getHistoricalAnalytics, + getProtocolTvl, + type AnalyticsInterval, + type AnalyticsPeriod, +} from "../services/analytics.service.js"; +import { sendApiError } from "../types/api-error.js"; +import logger from "../logger.js"; + +const VALID_PERIODS: AnalyticsPeriod[] = ["7d", "30d", "90d", "1y"]; +const VALID_INTERVALS: AnalyticsInterval[] = ["1h", "1d"]; + +/** + * GET /api/v1/analytics/tvl + * + * Current protocol TVL partitioned by token asset. + */ +export async function getTvlHandler(_req: Request, res: Response) { + try { + const snapshot = await getProtocolTvl(); + res.json({ success: true, data: snapshot }); + } catch (error) { + logger.error({ err: error }, "analytics TVL request failed"); + sendApiError(res, 500, "ANALYTICS_UNAVAILABLE", "TVL snapshot unavailable"); + } +} + +/** + * GET /api/v1/analytics/historical?period=30d&interval=1d + * + * Pre-aggregated time-series points for frontend charts. + */ +export async function getHistoricalHandler(req: Request, res: Response) { + const period = (req.query.period ?? "30d") as AnalyticsPeriod; + const interval = (req.query.interval ?? "1d") as AnalyticsInterval; + + if (!VALID_PERIODS.includes(period)) { + return sendApiError( + res, + 400, + "INVALID_PERIOD", + `period must be one of: ${VALID_PERIODS.join(", ")}` + ); + } + if (!VALID_INTERVALS.includes(interval)) { + return sendApiError( + res, + 400, + "INVALID_INTERVAL", + `interval must be one of: ${VALID_INTERVALS.join(", ")}` + ); + } + + try { + const series = await getHistoricalAnalytics(period, interval); + res.json({ success: true, data: series }); + } catch (error) { + logger.error({ err: error }, "analytics historical request failed"); + sendApiError( + res, + 500, + "ANALYTICS_UNAVAILABLE", + "Historical analytics unavailable" + ); + } +} + +/** + * GET /api/v1/analytics/defillama + * + * Standardized DefiLlama protocol-TVL adapter response. + */ +export async function getDefiLlamaHandler(_req: Request, res: Response) { + try { + const adapter = await getDefiLlamaAdapter(); + // DefiLlama consumes the raw object; no success envelope here. + res.json(adapter); + } catch (error) { + logger.error({ err: error }, "DefiLlama adapter request failed"); + sendApiError( + res, + 500, + "ANALYTICS_UNAVAILABLE", + "DefiLlama adapter unavailable" + ); + } +} diff --git a/backend/src/routes/v1/analytics.routes.ts b/backend/src/routes/v1/analytics.routes.ts new file mode 100644 index 00000000..e86e6cba --- /dev/null +++ b/backend/src/routes/v1/analytics.routes.ts @@ -0,0 +1,22 @@ +/** + * Analytics routes (#1480) — public protocol statistics. + * + * - GET /tvl — current TVL partitioned by token + * - GET /historical — pre-aggregated chart series (`?period=&interval=`) + * - GET /defillama — DefiLlama adapter payload + */ + +import { Router } from "express"; +import { + getDefiLlamaHandler, + getHistoricalHandler, + getTvlHandler, +} from "../../controllers/analytics.controller.js"; + +const router = Router(); + +router.get("/tvl", getTvlHandler); +router.get("/historical", getHistoricalHandler); +router.get("/defillama", getDefiLlamaHandler); + +export default router; diff --git a/backend/src/routes/v1/index.ts b/backend/src/routes/v1/index.ts index 01db64bc..f85d36f1 100644 --- a/backend/src/routes/v1/index.ts +++ b/backend/src/routes/v1/index.ts @@ -5,6 +5,7 @@ import userRoutes from "./user.routes.js"; import authRoutes from "./auth.routes.js"; import adminRoutes from "./admin.routes.js"; import webhookRoutes from "./webhook.routes.js"; +import analyticsRoutes from "./analytics.routes.js"; const router = Router(); @@ -14,6 +15,7 @@ router.use("/events", eventsRoutes); router.use("/users", userRoutes); router.use("/auth", authRoutes); router.use("/webhooks", webhookRoutes); +router.use("/analytics", analyticsRoutes); // Admin routes router.use("/admin", adminRoutes); diff --git a/backend/src/services/analytics.service.ts b/backend/src/services/analytics.service.ts new file mode 100644 index 00000000..9a6bfc6f --- /dev/null +++ b/backend/src/services/analytics.service.ts @@ -0,0 +1,246 @@ +/** + * Analytics Service — TimescaleDB-backed protocol TVL & velocity (Issue #1480) + * + * Reads the `stream_flow_snapshots` hypertable and its continuous aggregates + * (`hourly_protocol_metrics`, `daily_protocol_metrics`) created by the + * `20261001000000_add_timescaledb_analytics` migration. When TimescaleDB is + * not installed (or the hypertable has not been created yet) every reader + * degrades to live aggregation over the Prisma `Stream` table, so the + * endpoints stay available on stock PostgreSQL deployments. + */ + +import { prisma } from "../lib/prisma.js"; +import { pool } from "../lib/pg-pool.js"; +import logger from "../logger.js"; + +/** Period accepted by the historical endpoint. */ +export type AnalyticsPeriod = "7d" | "30d" | "90d" | "1y"; + +/** Interval accepted by the historical endpoint. */ +export type AnalyticsInterval = "1h" | "1d"; + +const PERIOD_DAYS: Record = { + "7d": 7, + "30d": 30, + "90d": 90, + "1y": 365, +}; + +export interface TokenTvl { + tokenAddress: string; + /** Sum of unwithdrawn stream balances, in stroops. */ + totalLockedStroops: string; + /** Sum of per-second drip across active streams, in stroops/second. */ + velocityPerSecondStroops: string; + activeStreamCount: number; +} + +export interface TvlSnapshot { + /** Where the numbers came from: the hypertable or live aggregation. */ + source: "timescaledb" | "live"; + tokens: TokenTvl[]; + /** Protocol-wide total locked, in stroops. */ + totalTvlStroops: string; + generatedAt: string; +} + +export interface HistoricalPoint { + bucket: string; + tokenAddress: string; + avgTvlStroops: string; + velocityStroopsPerSecond: string; + peakStreams: number; +} + +export interface HistoricalSeries { + source: "timescaledb" | "live"; + period: AnalyticsPeriod; + interval: AnalyticsInterval; + points: HistoricalPoint[]; +} + +export interface DefiLlamaResponse { + /** DefiLlama adapter protocol identifier. */ + id: string; + name: string; + /** TVL keyed by token address, plus a `total` entry — stroops. */ + tvl: Record; + timestamp: string; +} + +/** Detects whether the TimescaleDB hypertable exists and is queryable. */ +let timescaleAvailable: boolean | undefined; + +/** + * Clears the availability cache. Exported for tests: the service process + * should cache once, but each test needs a fresh probe. + */ +export function resetTimescaleCacheForTests(): void { + timescaleAvailable = undefined; +} + +async function hasTimescale(): Promise { + if (timescaleAvailable !== undefined) return timescaleAvailable; + try { + const result = await pool.query( + `SELECT to_regclass('public.stream_flow_snapshots') AS hypertable` + ); + timescaleAvailable = result.rows[0]?.hypertable !== null; + } catch (error) { + logger.warn( + { err: error }, + "TimescaleDB analytics tables unavailable; using live aggregation" + ); + timescaleAvailable = false; + } + return timescaleAvailable; +} + +/** Live fallback: aggregate active streams straight from Prisma. */ +async function liveTvl(): Promise { + const grouped = await prisma.stream.groupBy({ + by: ["tokenAddress"], + where: { isActive: true }, + _count: { _all: true }, + _sum: { + depositedAmount: true, + withdrawnAmount: true, + ratePerSecond: true, + }, + }); + + const tokens: TokenTvl[] = grouped.map((g) => { + const deposited = BigInt(g._sum.depositedAmount ?? 0); + const withdrawn = BigInt(g._sum.withdrawnAmount ?? 0); + const locked = deposited > withdrawn ? deposited - withdrawn : 0n; + return { + tokenAddress: g.tokenAddress, + totalLockedStroops: locked.toString(), + velocityPerSecondStroops: BigInt(g._sum.ratePerSecond ?? 0).toString(), + activeStreamCount: g._count._all, + }; + }); + + const totalTvlStroops = tokens + .reduce((acc, t) => acc + BigInt(t.totalLockedStroops), 0n) + .toString(); + + return { + source: "live", + tokens, + totalTvlStroops, + generatedAt: new Date().toISOString(), + }; +} + +/** Latest snapshot row per token, straight from the hypertable. */ +async function timescaleTvl(): Promise { + const result = await pool.query<{ + token_address: string; + total_locked_amount: string; + flow_velocity_per_second: string; + active_stream_count: number; + }>( + `SELECT DISTINCT ON (token_address) + token_address, + total_locked_amount, + flow_velocity_per_second, + active_stream_count + FROM stream_flow_snapshots + ORDER BY token_address, time DESC` + ); + + const tokens: TokenTvl[] = result.rows.map((row) => ({ + tokenAddress: row.token_address, + totalLockedStroops: BigInt(row.total_locked_amount).toString(), + velocityPerSecondStroops: BigInt(row.flow_velocity_per_second).toString(), + activeStreamCount: row.active_stream_count, + })); + + const totalTvlStroops = tokens + .reduce((acc, t) => acc + BigInt(t.totalLockedStroops), 0n) + .toString(); + + return { + source: "timescaledb", + tokens, + totalTvlStroops, + generatedAt: new Date().toISOString(), + }; +} + +/** Current protocol TVL partitioned by token (#1480). */ +export async function getProtocolTvl(): Promise { + if (await hasTimescale()) { + try { + return await timescaleTvl(); + } catch (error) { + logger.warn( + { err: error }, + "Timescale TVL query failed; falling back to live aggregation" + ); + } + } + return liveTvl(); +} + +/** Historical pre-aggregated TVL/velocity series for charts (#1480). */ +export async function getHistoricalAnalytics( + period: AnalyticsPeriod, + interval: AnalyticsInterval +): Promise { + if (!(await hasTimescale())) { + return { source: "live", period, interval, points: [] }; + } + + const view = + interval === "1d" ? "daily_protocol_metrics" : "hourly_protocol_metrics"; + const days = PERIOD_DAYS[period]; + + const result = await pool.query<{ + bucket: Date; + token_address: string; + avg_tvl: string; + aggregate_velocity: string; + peak_streams: number; + }>( + `SELECT bucket, token_address, avg_tvl, aggregate_velocity, peak_streams + FROM ${view} + WHERE bucket >= now() - ($1 || ' days')::interval + ORDER BY bucket, token_address`, + [String(days)] + ); + + return { + source: "timescaledb", + period, + interval, + points: result.rows.map((row) => ({ + bucket: new Date(row.bucket).toISOString(), + tokenAddress: row.token_address, + avgTvlStroops: BigInt(row.avg_tvl).toString(), + velocityStroopsPerSecond: BigInt(row.aggregate_velocity).toString(), + peakStreams: row.peak_streams, + })), + }; +} + +/** + * DefiLlama-compatible adapter payload (#1480). + * + * DefiLlama reads a flat `{ [chainOrToken]: tvlNumber }` shape; numbers are + * whole units of each token's smallest display unit (stroops for Stellar). + */ +export async function getDefiLlamaAdapter(): Promise { + const snapshot = await getProtocolTvl(); + const tvl: Record = { total: Number(snapshot.totalTvlStroops) }; + for (const token of snapshot.tokens) { + tvl[token.tokenAddress] = Number(token.totalLockedStroops); + } + return { + id: "flowfi", + name: "FlowFi", + tvl, + timestamp: snapshot.generatedAt, + }; +} diff --git a/backend/tests/analytics.test.ts b/backend/tests/analytics.test.ts new file mode 100644 index 00000000..f7e096ba --- /dev/null +++ b/backend/tests/analytics.test.ts @@ -0,0 +1,144 @@ +/** + * Analytics endpoint tests (#1480). + * + * The TimescaleDB path is exercised through a mocked pg pool so no real + * database is needed; the fallback path is exercised by making + * `hasTimescale` report unavailable. Contract under test: response shapes, + * validation errors, and the DefiLlama adapter mapping. + */ + +import { describe, it, expect, vi, beforeEach } from "vitest"; + +const queryMock = vi.fn(); + +vi.mock("../src/lib/prisma.js", () => ({ + prisma: { + stream: { + groupBy: vi.fn(), + }, + }, +})); + +vi.mock("../src/lib/pg-pool.js", () => ({ + pool: { + query: (...args: unknown[]) => queryMock(...args), + }, +})); + +vi.mock("../src/logger.js", () => ({ + default: { info: vi.fn(), warn: vi.fn(), error: vi.fn() }, +})); + +import { prisma } from "../src/lib/prisma.js"; +import { + getDefiLlamaAdapter, + getHistoricalAnalytics, + getProtocolTvl, + resetTimescaleCacheForTests, +} from "../src/services/analytics.service.js"; + +describe("analytics service (#1480)", () => { + beforeEach(() => { + vi.clearAllMocks(); + queryMock.mockReset(); + resetTimescaleCacheForTests(); + }); + + it("falls back to live Prisma aggregation when TimescaleDB is absent", async () => { + // to_regclass returns null → no hypertable. + queryMock.mockResolvedValue({ rows: [{ hypertable: null }] }); + + (prisma.stream.groupBy as ReturnType).mockResolvedValue([ + { + tokenAddress: "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC", + _count: { _all: 3 }, + _sum: { + depositedAmount: BigInt(1_000_000), + withdrawnAmount: BigInt(400_000), + ratePerSecond: BigInt(50), + }, + }, + ]); + + const snapshot = await getProtocolTvl(); + + expect(snapshot.source).toBe("live"); + expect(snapshot.totalTvlStroops).toBe("600000"); + expect(snapshot.tokens[0].activeStreamCount).toBe(3); + expect(snapshot.tokens[0].velocityPerSecondStroops).toBe("50"); + }); + + it("reads the latest hypertable snapshot per token when TimescaleDB is present", async () => { + queryMock.mockResolvedValueOnce({ + rows: [{ hypertable: "stream_flow_snapshots" }], + }); + queryMock.mockResolvedValueOnce({ + rows: [ + { + token_address: "CB63BC53EZDCKG6EFA3KJD3QWVV4QG4N35D7N4YQ6EY5XLYWJ7VL2MUA", + total_locked_amount: "900000", + flow_velocity_per_second: "25", + active_stream_count: 2, + }, + ], + }); + + const snapshot = await getProtocolTvl(); + + expect(snapshot.source).toBe("timescaledb"); + expect(snapshot.totalTvlStroops).toBe("900000"); + // DISTINCT ON (token_address) ... ORDER BY token_address, time DESC + expect(queryMock.mock.calls[1][0]).toContain("DISTINCT ON"); + }); + + it("maps the snapshot into the DefiLlama adapter shape", async () => { + queryMock.mockResolvedValue({ rows: [{ hypertable: null }] }); + (prisma.stream.groupBy as ReturnType).mockResolvedValue([ + { + tokenAddress: "TOKEN_A", + _count: { _all: 1 }, + _sum: { + depositedAmount: BigInt(500), + withdrawnAmount: BigInt(200), + ratePerSecond: BigInt(1), + }, + }, + ]); + + const adapter = await getDefiLlamaAdapter(); + + expect(adapter.id).toBe("flowfi"); + expect(adapter.tvl.total).toBe(300); + expect(adapter.tvl.TOKEN_A).toBe(300); + }); + + it("returns empty historical points on live fallback and buckets via the daily view", async () => { + queryMock.mockResolvedValue({ rows: [{ hypertable: null }] }); + const live = await getHistoricalAnalytics("30d", "1d"); + expect(live).toEqual({ + source: "live", + period: "30d", + interval: "1d", + points: [], + }); + + // First call is the availability probe, second the data query. + queryMock.mockResolvedValueOnce({ rows: [{ hypertable: "stream_flow_snapshots" }] }); + queryMock.mockResolvedValueOnce({ + rows: [ + { + bucket: new Date("2026-09-28T00:00:00Z"), + token_address: "TOKEN_A", + avg_tvl: "123", + aggregate_velocity: "4", + peak_streams: 9, + }, + ], + }); + const series = await getHistoricalAnalytics("90d", "1d"); + expect(series.source).toBe("timescaledb"); + expect(series.points[0].avgTvlStroops).toBe("123"); + expect(series.points[0].peakStreams).toBe(9); + expect(queryMock.mock.calls.at(-1)![0]).toContain("daily_protocol_metrics"); + }); +}); diff --git a/contracts/stream_contract/src/errors.rs b/contracts/stream_contract/src/errors.rs index cdfacb17..d90ed918 100644 --- a/contracts/stream_contract/src/errors.rs +++ b/contracts/stream_contract/src/errors.rs @@ -81,4 +81,24 @@ pub enum StreamError { NotArbiter = 32, /// Allowance-based stream operation failed. AllowanceLocked = 33, + /// A conditional milestone id is duplicated within one stream, is + /// referenced that does not exist, or the milestone list is empty. + InvalidMilestone = 34, + /// The oracle returned no price for the asset. + OraclePriceUnavailable = 35, + /// The oracle price is older than `ORACLE_PRICE_MAX_AGE_SECS`. + OraclePriceStale = 36, + /// The milestone's condition has not been met, so nothing unlocked. + ConditionNotMet = 37, + /// The milestone was already unlocked and cannot unlock again. + MilestoneAlreadyUnlocked = 38, + /// The attestation id was already used to unlock a milestone on this + /// stream, or the attestation signer is not the milestone's oracle. + InvalidAttestation = 39, + /// The caller is not authorized to unlock this milestone (only the + /// stream's sender or recipient may trigger verification). + MilestoneCallerUnauthorized = 40, + /// The conditional milestone list would exceed + /// `MAX_CONDITIONAL_MILESTONES`. + TooManyMilestones = 41, } diff --git a/contracts/stream_contract/src/events.rs b/contracts/stream_contract/src/events.rs index 608a1f64..67060a49 100644 --- a/contracts/stream_contract/src/events.rs +++ b/contracts/stream_contract/src/events.rs @@ -320,3 +320,19 @@ pub struct StreamClosedEvent { pub closer: Address, pub timestamp: u64, } + +/// Emitted when a conditional milestone's condition verifies true (#1482). +/// +/// Topic: `("milestone_condition_unlocked", stream_id)` +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct MilestoneConditionUnlockedEvent { + pub stream_id: u64, + pub milestone_id: u32, + /// Amount that became claimable. + pub amount: i128, + /// Caller that triggered the verification. + pub caller: Address, + /// Ledger timestamp of the verification. + pub timestamp: u64, +} diff --git a/contracts/stream_contract/src/lib.rs b/contracts/stream_contract/src/lib.rs index 0695e07a..396774ce 100644 --- a/contracts/stream_contract/src/lib.rs +++ b/contracts/stream_contract/src/lib.rs @@ -47,12 +47,21 @@ use soroban_sdk::{ use errors::StreamError; use events::{ AdminTransferredEvent, AllowanceStreamCreatedEvent, ContractUpgradedEvent, +<<<<<<< HEAD DisputeRequestedEvent, DisputeResolvedEvent, EmergencyGuardianUpdatedEvent, FeeCollectedEvent, FeeConfigUpdatedEvent, HybridCliffStreamCreatedEvent, InitializedEvent, ProtocolPauseStatusEvent, StateMigratedEvent, StepVestingStreamCreatedEvent, StreamCancelledEvent, StreamClosedEvent, StreamCompletedEvent, StreamCreatedEvent, StreamPausedEvent, StreamRateModifiedEvent, StreamResumedEvent, StreamToppedUpEvent, TokensWithdrawnEvent, +======= + DisputeRequestedEvent, DisputeResolvedEvent, EmergencyGuardianUpdatedEvent, + FeeCollectedEvent, FeeConfigUpdatedEvent, HybridCliffStreamCreatedEvent, InitializedEvent, + MilestoneConditionUnlockedEvent, ProtocolPauseStatusEvent, StateMigratedEvent, + StepVestingStreamCreatedEvent, StreamCancelledEvent, StreamClosedEvent, + StreamCompletedEvent, StreamCreatedEvent, StreamPausedEvent, StreamRateModifiedEvent, + StreamResumedEvent, StreamToppedUpEvent, TokensWithdrawnEvent +>>>>>>> 4b71088 (feat: conditional streams, analytics stack, developer portal, copy feedback) }; use storage::{ config_exists, get_contract_version, get_recorded_wasm_hash, load_config, load_stream, @@ -60,8 +69,10 @@ use storage::{ save_stream, try_load_config, try_load_stream, }; use types::{ - DisputeStatus, ProtocolConfig, Stream, StreamStatus, VestingSchedule, VestingStep, - MAX_BATCH_WITHDRAW, MAX_VESTING_STEPS, + ConditionalMilestone, DataKey, DisputeStatus, MAX_BATCH_WITHDRAW, + MAX_CONDITIONAL_MILESTONES, MAX_VESTING_STEPS, ORACLE_PRICE_MAX_AGE_SECS, OracleAsset, + OracleClient, ProtocolConfig, Stream, StreamStatus, UnlockCondition, VestingSchedule, + VestingStep }; /// Maximum allowed protocol fee: 1 000 bps = 10%. @@ -687,6 +698,306 @@ impl StreamContract { Ok(()) } + /// Validate a conditional milestone list at creation time (#1482). + /// + /// - 1..=`MAX_CONDITIONAL_MILESTONES` milestones, + /// - unique `milestone_id` within the list, + /// - strictly positive `amount`, + /// - milestone amounts summing to exactly the post-fee deposited amount, + /// so a conditional stream can never promise more than was escrowed. + fn validate_conditional_milestones( + milestones: &soroban_sdk::Vec, + net_amount: i128, + ) -> Result<(), StreamError> { + if milestones.is_empty() { + return Err(StreamError::EmptyVestingSchedule); + } + if milestones.len() > MAX_CONDITIONAL_MILESTONES { + return Err(StreamError::TooManyMilestones); + } + + let mut total: i128 = 0; + for i in 0..milestones.len() { + let m = milestones.get(i).expect("index in range"); + if m.amount <= 0 { + return Err(StreamError::InvalidVestingStepAmount); + } + for j in (i + 1)..milestones.len() { + let n = milestones.get(j).expect("index in range"); + if n.milestone_id == m.milestone_id { + return Err(StreamError::InvalidMilestone); + } + } + total = total.saturating_add(m.amount); + } + if total != net_amount { + return Err(StreamError::VestingStepTotalMismatch); + } + Ok(()) + } + + /// Create a conditional (KPI-gated) streaming stream (#1482). + /// + /// Funds are escrowed exactly like [`Self::create_stream`] (gross transfer, + /// protocol fee deducted), but release is governed by a list of + /// [`ConditionalMilestone`] tranches instead of a time curve. Each + /// milestone unlocks when its [`UnlockCondition`] verifies true — a time + /// gate, an oracle price target, or a signed oracle attestation — via + /// [`Self::verify_and_unlock_milestone`]. + /// + /// Under the hood the milestones are stored as the stream's + /// [`VestingSchedule::StepTranches`] with every `unlock_time` at + /// `u64::MAX`: unverified tranches are unreachable by time, and + /// verification rewrites a satisfied milestone's time to the current + /// timestamp, which makes it flow through the *existing* + /// `calculate_claimable` / `withdraw` math unchanged. + /// + /// # Errors + /// - `ProtocolPaused` — the circuit breaker is engaged. + /// - `InvalidAmount` — `amount` ≤ 0. + /// - `InvalidTokenAddress` — `token_address` is not a token contract. + /// - `EmptyVestingSchedule` — `milestones` is empty. + /// - `TooManyMilestones` — more than `MAX_CONDITIONAL_MILESTONES`. + /// - `InvalidMilestone` — a duplicated `milestone_id`. + /// - `InvalidVestingStepAmount` — a milestone amount is ≤ 0. + /// - `VestingStepTotalMismatch` — milestone amounts ≠ post-fee deposited amount. + pub fn create_conditional_stream( + env: Env, + sender: Address, + recipient: Address, + token_address: Address, + amount: i128, + milestones: soroban_sdk::Vec, + ) -> Result { + sender.require_auth(); + Self::require_not_protocol_paused(&env)?; + + if amount <= 0 { + return Err(StreamError::InvalidAmount); + } + Self::validate_token_contract(&env, &token_address)?; + + let stream_id = next_stream_id(&env); + let start_time = env.ledger().timestamp(); + + // Transfer gross amount from sender to this contract. + let token_client = token::Client::new(&env, &token_address); + let contract_address = env.current_contract_address(); + token_client.transfer(&sender, &contract_address, &amount); + + // Deduct protocol fee; returns net amount (== amount when no fee config). + // The fee transfer itself is deferred until the stream is persisted, + // mirroring the creation flow used by the other stream types. + let (net_amount, fee_amount, treasury) = Self::collect_fee(&env, &token_address, amount)?; + + Self::validate_conditional_milestones(&milestones, net_amount)?; + + // Store as step tranches whose unlock times are all "never" until the + // condition is verified. Recipient view of the schedule then shows + // exactly which tranches remain condition-gated. + let mut steps: Vec = Vec::new(&env); + for m in milestones.iter() { + steps.push_back(VestingStep { + unlock_time: u64::MAX, + unlock_amount: m.amount, + }); + } + + save_stream( + &env, + stream_id, + &Stream { + sender: sender.clone(), + recipient: recipient.clone(), + token_address: token_address.clone(), + rate_per_second: 0, + deposited_amount: net_amount, + withdrawn_amount: 0, + start_time, + last_update_time: start_time, + cliff_time: None, + is_active: true, + paused: false, + paused_at: None, + status: StreamStatus::Active, + schedule: VestingSchedule::StepTranches(steps), + arbiter: None, + dispute_status: DisputeStatus::None, + is_allowance_based: false, + }, + ); + env.storage().instance().set( + &DataKey::ConditionalMilestones(stream_id), + &milestones, + ); + env.storage() + .instance() + .set(&DataKey::AttestedIds(stream_id), &Vec::>::new(&env)); + + Self::transfer_fee(&env, &token_address, stream_id, fee_amount, treasury); + + env.events().publish( + (Symbol::new(&env, "stream_created"), stream_id), + StreamCreatedEvent { + stream_id, + sender, + recipient, + rate_per_second: 0, + token_address, + deposited_amount: net_amount, + start_time, + }, + ); + + Ok(stream_id) + } + + /// Evaluate one conditional milestone and, when its condition holds, + /// release it (#1482). + /// + /// - `TimeOnly` unlocks once `env.ledger().timestamp()` passes the gate. + /// - `PriceTarget` cross-contract calls the oracle's `lastprice`, reverts + /// with `OraclePriceUnavailable` when it returns none and with + /// `OraclePriceStale` when the reading is older than + /// `ORACLE_PRICE_MAX_AGE_SECS`, then compares against `target_price` + /// (`is_above`: price >= target, otherwise price <= target). + /// - `OracleAttestation` requires the attestation's oracle signer to + /// authorize this invocation (`require_auth` on the signer address) and + /// rejects an attestation id that already unlocked a milestone on this + /// stream, so one attestation can never pay out twice. + /// + /// On success the milestone's `unlock_time` is rewritten from `u64::MAX` + /// to the current timestamp, which promotes the tranche into the normal + /// step-unlock flow: it becomes claimable immediately and withdrawable + /// with the existing entry points. Emits `milestone_condition_unlocked`. + /// + /// # Errors + /// - `StreamNotFound` — no stream exists with `stream_id`. + /// - `MilestoneCallerUnauthorized` — caller is neither sender nor recipient. + /// - `StreamInactive` — stream cancelled or fully withdrawn. + /// - `InvalidMilestone` — no milestone with that id. + /// - `MilestoneAlreadyUnlocked` — milestone already unlocked. + /// - `OraclePriceUnavailable` — oracle returned no price. + /// - `OraclePriceStale` — price older than the freshness window. + /// - `ConditionNotMet` — condition evaluated false. + /// - `InvalidAttestation` — attestation id reused or unauthorized. + pub fn verify_and_unlock_milestone( + env: Env, + caller: Address, + stream_id: u64, + milestone_id: u32, + ) -> Result { + caller.require_auth(); + + let mut stream = load_stream(&env, stream_id)?; + if stream.sender != caller && stream.recipient != caller { + return Err(StreamError::MilestoneCallerUnauthorized); + } + if !stream.is_active { + return Err(StreamError::StreamInactive); + } + + let mut milestones: soroban_sdk::Vec = env + .storage() + .instance() + .get(&DataKey::ConditionalMilestones(stream_id)) + .ok_or(StreamError::StreamNotFound)?; + + let index = (0..milestones.len()) + .find(|&i| { + milestones + .get(i) + .expect("index in range") + .milestone_id + == milestone_id + }) + .ok_or(StreamError::InvalidMilestone)?; + let mut milestone = milestones.get(index).expect("index in range"); + if milestone.is_unlocked { + return Err(StreamError::MilestoneAlreadyUnlocked); + } + + let now = env.ledger().timestamp(); + match &milestone.condition { + UnlockCondition::TimeOnly(unlock_time) => { + if now < *unlock_time { + return Err(StreamError::ConditionNotMet); + } + } + UnlockCondition::PriceTarget(oracle_address, target_price, is_above) => { + let oracle_client = OracleClient::new(&env, oracle_address); + let price_data = oracle_client + .lastprice(&OracleAsset::Other(stream.token_address.clone())) + .ok_or(StreamError::OraclePriceUnavailable)?; + if now.saturating_sub(price_data.timestamp) > ORACLE_PRICE_MAX_AGE_SECS { + return Err(StreamError::OraclePriceStale); + } + let met = if *is_above { + price_data.price >= *target_price + } else { + price_data.price <= *target_price + }; + if !met { + return Err(StreamError::ConditionNotMet); + } + } + UnlockCondition::OracleAttestation(oracle_signer, attestation_id) => { + let mut attested: Vec> = env + .storage() + .instance() + .get(&DataKey::AttestedIds(stream_id)) + .unwrap_or_else(|| Vec::new(&env)); + if attested.contains(attestation_id) { + return Err(StreamError::InvalidAttestation); + } + // The oracle signer proves control of the attestation by + // authorizing this call as a sub-invocation; anything weaker + // would let anyone unlock milestones by naming a signer. + oracle_signer.require_auth(); + attested.push_back(attestation_id.clone()); + env.storage() + .instance() + .set(&DataKey::AttestedIds(stream_id), &attested); + } + } + + milestone.is_unlocked = true; + let unlocked_amount = milestone.amount; + milestones.set(index, milestone); + env.storage().instance().set( + &DataKey::ConditionalMilestones(stream_id), + &milestones, + ); + + // Promote the tranche into the normal step-unlock flow: rewrite its + // gate time from "never" to "now" so `calculate_claimable` counts it. + if let VestingSchedule::StepTranches(steps) = &mut stream.schedule { + for i in 0..steps.len() { + let mut step = steps.get(i).expect("index in range"); + if step.unlock_amount == unlocked_amount && step.unlock_time == u64::MAX { + step.unlock_time = now; + steps.set(i, step); + break; + } + } + } + stream.last_update_time = now; + save_stream(&env, stream_id, &stream); + + env.events().publish( + (Symbol::new(&env, "milestone_condition_unlocked"), stream_id), + MilestoneConditionUnlockedEvent { + stream_id, + milestone_id, + amount: unlocked_amount, + caller, + timestamp: now, + }, + ); + + Ok(unlocked_amount) + } + /// Top up an active stream with additional tokens. /// /// Only the original sender may top up their own stream. The top-up amount diff --git a/contracts/stream_contract/src/types.rs b/contracts/stream_contract/src/types.rs index 9f0e4e59..3e1bb77e 100644 --- a/contracts/stream_contract/src/types.rs +++ b/contracts/stream_contract/src/types.rs @@ -1,4 +1,41 @@ -use soroban_sdk::{contracttype, Address, Vec}; +use soroban_sdk::{contractclient, contracttype, Address, BytesN, Env, Vec}; + +/// SEP-40-style price oracle interface (#1482). +/// +/// `#[contractclient]` generates [`OracleClient`], which `verify_and_unlock_milestone` +/// uses for `PriceTarget` conditions' cross-contract `lastprice` calls. +/// +/// The asset argument mirrors the SEP-40 `Asset` wire shape as its own +/// contracttype so the generated invocation matches oracle deployments +/// without pulling SDK type differences into our ABI. +#[contractclient(name = "OracleClient")] +pub trait PriceOracle { + /// Last recorded price for `asset`, or `None` when the oracle has none. + fn lastprice(env: Env, asset: OracleAsset) -> Option; +} + +/// Asset identifier passed to [`PriceOracle::lastprice`] (#1482). +/// +/// Encoded identically to the SEP-40 `Asset` enum: `Native` is the native +/// XLM, `Other` wraps a token contract address. +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum OracleAsset { + /// The native asset (XLM). + Native, + /// A non-native token, keyed by its contract address. + Other(Address), +} + +/// A single oracle price reading (#1482). +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct PriceData { + /// Price in the oracle's own decimals. + pub price: i128, + /// Ledger timestamp the reading was taken at. + pub timestamp: u64, +} /// Maximum number of unlock steps a single step-tranche schedule may declare. /// @@ -6,6 +43,16 @@ use soroban_sdk::{contracttype, Address, Vec}; /// the Soroban CPU/memory budget regardless of how many streams are batched. pub const MAX_VESTING_STEPS: u32 = 12; +/// Maximum number of conditional milestones per conditional stream (#1482). +/// +/// Same budget rationale as [`MAX_VESTING_STEPS`]: verification and withdrawal +/// iterate the whole list in one transaction. +pub const MAX_CONDITIONAL_MILESTONES: u32 = 12; + +/// Maximum age (seconds) of an oracle price reading still considered fresh +/// for `PriceTarget` verification (#1482). +pub const ORACLE_PRICE_MAX_AGE_SECS: u64 = 3600; + /// Maximum number of streams a single `batch_withdraw` call may process. /// /// Guards against blowing the transaction's CPU and memory limits; recipients @@ -69,6 +116,45 @@ pub enum VestingSchedule { HybridCliffLinear(u64, i128), } +/// Condition that gates a conditional milestone's release (#1482). +/// +/// `#[contracttype]` enums cannot carry named struct-variant fields, so +/// `PriceTarget` and `OracleAttestation` encode their fields positionally. +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum UnlockCondition { + /// No condition: the tranche unlocks purely by time. Kept so a mixed + /// schedule can blend time-based and condition-based milestones. + TimeOnly(u64), + /// Unlocks when the oracle's last price for the stream's token crosses + /// `target_price` — above it when `is_above`, otherwise at or below it. + /// Positional: `(oracle_address, target_price, is_above)`. + PriceTarget(Address, i128, bool), + /// Unlocks when `oracle_signer` has authorized an attestation carrying + /// `attestation_id` on this contract. Positional: + /// `(oracle_signer, attestation_id)`. + OracleAttestation(Address, BytesN<32>), +} + +/// One KPI-gated tranche of a conditional streaming stream (#1482). +/// +/// Amounts and conditions are fixed at creation. `is_unlocked` is flipped by +/// `verify_and_unlock_milestone` once the condition has been observed true; +/// the unlocked amount then flows through the same claimed bookkeeping as a +/// step-tranche unlock. +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ConditionalMilestone { + /// Caller-assigned tranche id, unique within its stream. + pub milestone_id: u32, + /// Amount unlocked once `condition` is satisfied. Strictly positive. + pub amount: i128, + /// Condition that must hold before this tranche unlocks. + pub condition: UnlockCondition, + /// Whether the condition has already been verified true. + pub is_unlocked: bool, +} + /// Centralized storage key strategy. /// /// All contract storage is keyed exclusively through this enum, ensuring: @@ -92,6 +178,14 @@ pub enum DataKey { /// so the upgrade history is tracked here instead. Absent — read as /// `BytesN::zero` — means the contract has never been upgraded in place. ContractWasmHash, + /// Conditional milestone list for one stream (#1482), instance storage. + /// Kept off `Stream` so the persisted `Stream` schema stays frozen for + /// the migration path (`migrate` re-encodes records; adding a field here + /// would orphan every pre-existing row). + ConditionalMilestones(u64), + /// Emitted `OracleAttestation` ids for one stream (instance storage), + /// so a attestation cannot unlock two milestones. + AttestedIds(u64), } /// Immutable state of a payment stream. diff --git a/docs-site/README.md b/docs-site/README.md new file mode 100644 index 00000000..1ec29e10 --- /dev/null +++ b/docs-site/README.md @@ -0,0 +1,34 @@ +# FlowFi developer portal (#1481) + +Docusaurus portal unifying the docs scattered across `docs/`, +`backend/docs/` and `packages/flowfi-sdk/`, with a live API console, an +interactive Soroban RPC playground, and Mermaid architecture diagrams. + +## Local development + +```bash +cd docs-site +npm install +npm run dev # http://localhost:3000 +``` + +## Production build + +```bash +npm run build # fails on broken links or missing assets +npm run serve # serve the production build locally +``` + +CI (`.github/workflows/deploy-docs.yml`) builds the site on every PR that +touches `docs-site/**` and deploys to GitHub Pages from `main`. + +## Structure + +``` +docs-site/ + docusaurus.config.js # site config (nav, mermaid, broken-link guard) + sidebars.js # navigation + docs/ # content: contracts/, backend/, sdks/, guides/, api-reference/ + src/components/ # OpenApiConsole, SorobanPlayground + src/css/custom.css +``` diff --git a/docs-site/docs/api-reference/index.md b/docs-site/docs/api-reference/index.md new file mode 100644 index 00000000..234c35a9 --- /dev/null +++ b/docs-site/docs/api-reference/index.md @@ -0,0 +1,39 @@ +--- +title: API Reference +--- + +# API Reference + +The full OpenAPI specification is served by the backend at +[`/api-docs`](https://github.com/LabsCrypt/flowfi/tree/main/backend/swagger) +and mirrored below with a **Try-it-out** console. + +import '/src/components/OpenApiConsole'; + + + +## Conventions + +- **Versioned base path** — everything lives under `/api/v1/`. +- **Auth** — wallet JWTs (`Authorization: Bearer …`) for user-scoped routes; + admin routes additionally require the admin role claim. +- **Errors** — JSON `{ "success": false, "error": { "code", "message" } }` + with meaningful HTTP status codes; validation failures are `400`. +- **Amounts** — all amounts are integer **stroops** (`1 XLM = 10^7` stroops) + as strings or numbers; never floats. + +## Core endpoints + +| Method | Path | Purpose | +|---|---|---| +| POST | `/api/v1/streams` | Create a stream | +| GET | `/api/v1/streams` | List the caller's streams | +| GET | `/api/v1/streams/:id` | Fetch one stream | +| GET | `/api/v1/streams/:id/claimable` | Live claimable amount | +| POST | `/api/v1/streams/:id/withdraw` | Withdraw accrued | +| POST | `/api/v1/streams/:id/top-up` | Extend a linear stream | +| POST | `/api/v1/streams/:id/pause` / `resume` / `cancel` | Lifecycle | +| GET | `/api/v1/events/stream` | SSE event feed | +| GET | `/api/v1/analytics/tvl` | Protocol TVL (#1480) | +| GET | `/api/v1/analytics/historical` | Chart series (#1480) | +| GET | `/api/v1/analytics/defillama` | DefiLlama adapter (#1480) | diff --git a/docs-site/docs/api-reference/playground.md b/docs-site/docs/api-reference/playground.md new file mode 100644 index 00000000..3d039144 --- /dev/null +++ b/docs-site/docs/api-reference/playground.md @@ -0,0 +1,30 @@ +--- +title: Soroban RPC playground +--- + +# Interactive Soroban RPC playground + +Run real `get_stream`, `get_claimable_amount` and `withdraw` simulations +against **Stellar Testnet** without deploying anything. Responses include the +decoded Soroban XDR alongside the JSON value. + +import Playground from '/src/components/SorobanPlayground'; + + + +## How it works + +- The playground issues **read calls** (`get_stream`, + `get_claimable_amount`, `get_vesting_schedule`) and **simulation-only + invocations** (`DryRun`) through a browser-side Soroban RPC client pointed + at `https://soroban-testnet.stellar.org`. +- Mutating calls (create/withdraw) require a real signer; the console + prepares the transaction and shows the XDR for signing in **Freighter**, so + no keys ever touch the page. +- Pre-filled mock parameters are editable; the decoded result pane shows + scalps, `ScVal` JSON, and raw base64 XDR side by side. + +:::caution Testnet only +The default endpoint is Testnet. Pointing the playground at Mainnet is +possible but every call still goes through your own wallet for signing. +::: diff --git a/docs-site/docs/backend/analytics.md b/docs-site/docs/backend/analytics.md new file mode 100644 index 00000000..19d5d1b6 --- /dev/null +++ b/docs-site/docs/backend/analytics.md @@ -0,0 +1,40 @@ +--- +title: Analytics (TimescaleDB) +--- + +# Analytics — TimescaleDB TVL & velocity (#1480) + +Protocol TVL, uncollected claims and streaming velocity change every second, +so they are **snapshotted into a hypertable** and pre-aggregated with +continuous aggregates instead of being recomputed from raw tables on every +request. + +## Pipeline + +```mermaid +sequenceDiagram + participant I as Indexer worker + participant H as stream_flow_snapshots (hypertable) + participant CA as Continuous aggregates (1h / 1d) + participant API as /api/v1/analytics/* + + I->>H: snapshot per token per tick + H->>CA: incremental refresh (policies) + API->>H: latest snapshot per token (DISTINCT ON) + API->>CA: bucketed series for charts +``` + +## Endpoints + +| Endpoint | Purpose | +|---|---| +| `GET /api/v1/analytics/tvl` | Current TVL partitioned by token (`source: timescaledb \| live`) | +| `GET /api/v1/analytics/historical?period=30d&interval=1d` | `7d/30d/90d/1y` × `1h/1d` chart series | +| `GET /api/v1/analytics/defillama` | DefiLlama adapter payload | + +## Degradation + +If the TimescaleDB extension or the hypertable is missing, `/tvl` falls back +to live aggregation over the `Stream` table (`source: "live"`) and +`/historical` returns an empty series — stock PostgreSQL deployments keep +working, they just lose the pre-aggregation. diff --git a/docs-site/docs/backend/dead-letter-triage.md b/docs-site/docs/backend/dead-letter-triage.md new file mode 100644 index 00000000..2b13a985 --- /dev/null +++ b/docs-site/docs/backend/dead-letter-triage.md @@ -0,0 +1,26 @@ +--- +title: Dead-letter triage +--- + +# Dead-letter triage + +Anything that could not be processed (event decode failure, persistence +error) or delivered (webhook attempts exhausted) lands in a dead-letter +table — never silently dropped. + +## Triage loop + +1. **List** — `GET /api/v1/admin/webhooks/dead-letter` (admin JWT): error + kind, payload, attempt count, last error. +2. **Fix the cause** — most commonly a decoder drift after a contract + upgrade, or a subscriber whose endpoint moved. +3. **Replay** — `POST /api/v1/admin/webhooks/dead-letter/:id/replay` for one + row, or `POST .../replay-all` after a bulk fix. Replays are idempotent: + event identity is `(contract, topic, ledger, index)`. +4. **Discard** — `POST .../:id/discard` when the payload is genuinely stale + (recorded with who discarded it and why). + +## Design rule + +Replay handlers reuse the production ingest path, not a copy — a bug fixed +for replay is a bug fixed for live traffic. diff --git a/docs-site/docs/backend/event-ingestion.md b/docs-site/docs/backend/event-ingestion.md new file mode 100644 index 00000000..a3db104b --- /dev/null +++ b/docs-site/docs/backend/event-ingestion.md @@ -0,0 +1,35 @@ +--- +title: Event ingestion +--- + +# Event ingestion + +```mermaid +sequenceDiagram + participant SC as Soroban (stream_contract) + participant W as soroban-event-worker + participant PG as PostgreSQL + participant SSE as SSE/WebSocket fan-out + participant UI as Dashboard + + SC->>W: contract event (Map-encoded) + W->>W: decode by field name (decodeMap) + W->>PG: upsert stream / insert StreamEvent (transaction) + W->>SSE: notify subscribers + SSE-->>UI: event push +``` + +## Contract + +- Events are emitted as **Soroban Maps** keyed by field name, so decoding is + order-independent. The pinned field/type table lives in + `backend/tests/events-wire-format.test.ts`. +- Ingestion is **idempotent**: event identity is `(contract, topic, ledger, + index)`, so replays are no-ops. +- The worker stores its cursor in Postgres (`INDEXER_STATE_ID`), so restarts + resume rather than rewind. + +## Failures + +Decode or persistence failures land in the dead-letter table for triage — +see [dead-letter triage](./dead-letter-triage.md). diff --git a/docs-site/docs/backend/overview.md b/docs-site/docs/backend/overview.md new file mode 100644 index 00000000..a4727712 --- /dev/null +++ b/docs-site/docs/backend/overview.md @@ -0,0 +1,38 @@ +--- +title: Backend & Indexer overview +--- + +# Backend & Indexer overview + +The Express backend is the read/write surface the frontend talks to, plus the +indexer that mirrors chain state into PostgreSQL. + +## Responsibilities + +1. **REST API** (`/api/v1/...`) — invoice/stream CRUD, withdraw proxy, user + summaries, and (#1480) the analytics endpoints. +2. **Event ingestion** — a worker tails Soroban events and writes them to + Postgres (see [event ingestion](./event-ingestion.md)). +3. **Real-time fan-out** — SSE (and WebSocket) push indexed events to the + dashboard ([streams & SSE](./streams-sse.md)). +4. **Webhooks** — signed outbound deliveries with exponential-backoff retry + and a dead-letter queue ([webhooks](./webhooks.md), + [dead-letter triage](./dead-letter-triage.md)). +5. **Analytics** — TimescaleDB-backed TVL/velocity + ([analytics](./analytics.md)). + +## Layout + +``` +backend/src/ + app.ts # express app assembly + controllers/ # HTTP handlers + services/ # business logic (analytics.service.ts is #1480) + routes/v1/ # versioned routers + repositories/ # prisma access layer + workers/ # soroban event worker + lib/ # prisma, pg pool, redis, metrics +``` + +Run locally with `npm run dev:mvp` in `backend/` — see the repo README for +database setup. diff --git a/docs-site/docs/backend/streams-sse.md b/docs-site/docs/backend/streams-sse.md new file mode 100644 index 00000000..f86f68ec --- /dev/null +++ b/docs-site/docs/backend/streams-sse.md @@ -0,0 +1,32 @@ +--- +title: SSE & WebSocket streams +--- + +# SSE & WebSocket streams + +The dashboard does not poll: the backend pushes indexed events over +Server-Sent Events (WebSocket for the charts pane). + +- `GET /api/v1/events/stream` — SSE feed of contract events filtered by the + caller's wallet. Heartbeat every 25s keeps proxies from idling the + connection out. +- Reconnects are safe: clients send `Last-Event-ID`, and the server replays + from the persisted event table rather than from memory. + +## Message shape + +```json +{ + "id": "1706140800:3", + "event": "tokens_withdrawn", + "data": { + "streamId": "12", + "recipient": "GBRB...", + "amount": "45000", + "timestamp": 1706140800 + } +} +``` + +Field names match the on-chain event payloads one-to-one, so the same +TypeScript types work client- and chain-side. diff --git a/docs-site/docs/backend/webhooks.md b/docs-site/docs/backend/webhooks.md new file mode 100644 index 00000000..78c4115b --- /dev/null +++ b/docs-site/docs/backend/webhooks.md @@ -0,0 +1,41 @@ +--- +title: Webhooks +--- + +# Webhooks + +Outbound webhook deliveries notify integrators when their wallet is touched +by an indexed event. + +## Delivery contract + +- `POST` to the subscriber's URL with an HMAC signature header + (`X-FlowFi-Signature`) over the raw body. +- Retries use **exponential backoff** on non-2xx responses and timeouts: + 30s → 1m → 5m → 30m → 2h (six attempts), then dead-letter. + +```mermaid +sequenceDiagram + participant W as Webhook worker + participant S as Subscriber + participant D as Dead-letter table + + W->>S: POST event (signed) + S-->>W: 500 + Note over W: retry in 1m + W->>S: POST event (signed) + S-->>W: 500 + Note over W: retry in 5m … then 30m, 2h + W->>D: mark dead-letter after final attempt + Note over D: operator triage via /admin +``` + +## Verifying signatures + +```python +import hmac, hashlib + +def verify(secret: str, raw_body: bytes, header: str) -> bool: + expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest() + return hmac.compare_digest(expected, header.removeprefix("sha256=")) +``` diff --git a/docs-site/docs/contracts/batch-operations.md b/docs-site/docs/contracts/batch-operations.md new file mode 100644 index 00000000..ccd6defa --- /dev/null +++ b/docs-site/docs/contracts/batch-operations.md @@ -0,0 +1,34 @@ +--- +title: Batch operations +--- + +# Batch operations + +`batch_withdraw(stream_ids, recipient)` withdraws up to +[`MAX_BATCH_WITHDRAW`](https://github.com/LabsCrypt/flowfi/blob/main/contracts/stream_contract/src/types.rs) +(30) streams in one transaction — one signature, one fee, N payouts. + +```mermaid +sequenceDiagram + participant R as Recipient + participant S as stream_contract + participant T as Token + + R->>S: batch_withdraw([1, 2, 3], recipient) + loop each stream + S->>S: calculate_claimable(stream, now) + S->>S: persist state (CEI) + end + S->>T: transfer(total, recipient) + S-->>R: tokens_withdrawn (per stream) +``` + +## Semantics + +- The **first failure aborts the whole batch** — partial withdrawals do not + happen; Soroban rolls the transaction back. +- Each stream is validated (active, recipient-owned) before its accrual is + added, so a paused or cancelled id fails loudly instead of silently + withdrawing zero. +- Per-stream `tokens_withdrawn` events keep indexers simple: the batch is + just N normal withdrawals sharing one transaction. diff --git a/docs-site/docs/contracts/conditional-streams.md b/docs-site/docs/contracts/conditional-streams.md new file mode 100644 index 00000000..54c2c8d5 --- /dev/null +++ b/docs-site/docs/contracts/conditional-streams.md @@ -0,0 +1,86 @@ +--- +title: Conditional streams (#1482) +--- + +# Conditional streams — oracle-gated milestones + +`create_conditional_stream` escrows a deposit against +[`ConditionalMilestone`](https://github.com/LabsCrypt/flowfi/blob/main/contracts/stream_contract/src/types.rs) +tranches. Unlike step tranches, **nothing unlocks by time alone** — each +tranche waits for its condition to verify true. + +## Unlock conditions + +```rust +pub enum UnlockCondition { + /// Unlocks at a fixed timestamp (for mixed schedules). + TimeOnly(u64), + /// Unlocks when the oracle price crosses target_price. + PriceTarget(Address /* oracle */, i128 /* target */, bool /* is_above */), + /// Unlocks when the named oracle signer authorizes this attestation id. + OracleAttestation(Address /* oracle_signer */, BytesN<32> /* id */), +} +``` + +## Verification flow + +```mermaid +sequenceDiagram + participant C as Sender / Recipient + participant S as stream_contract + participant O as Oracle (SEP-40 style) + + C->>S: verify_and_unlock_milestone(stream_id, milestone_id) + S->>O: lastprice(OracleAsset::Other(token)) + O-->>S: PriceData { price, timestamp } + alt no price + S-->>C: revert OraclePriceUnavailable + else older than 3600s + S-->>C: revert OraclePriceStale + else price below target (is_above) + S-->>C: revert ConditionNotMet + else condition met + S->>S: mark unlocked, promote tranche (unlock_time = now) + S-->>C: milestone_condition_unlocked event + end +``` + +## Guarantees + +- **Freshness** — price readings older than `ORACLE_PRICE_MAX_AGE_SECS` + (3600s) are rejected; a stale feed can never release funds. +- **Attestation single-use** — an attestation id is recorded per stream; the + same id can never unlock two milestones. +- **Signer authorization** — `OracleAttestation` requires the named oracle + signer to authorize the verification call itself, so naming a signer is not + enough. +- **Escrow equals schedule** — milestone amounts must sum to the post-fee + deposit, so the contract never promises more than it holds. +- **Reuse of the proven path** — verification only rewrites the tranche's + unlock time; claim math, withdrawal and events stay the audited + step-tranche code. + +## Example + +```ts +const milestones = [ + { + milestone_id: 1, + amount: 500_000_000n, + condition: { + PriceTarget: [oracleAddress, 450_00000n /* $0.45 */, true /* is_above */], + }, + is_unlocked: false, + }, + { + milestone_id: 2, + amount: 500_000_000n, + condition: { OracleAttestation: [oracleSigner, attestationId] }, + is_unlocked: false, + }, +]; + +await client.createConditionalStream({ sender, recipient, token, amountStroops, milestones }); +// later, when the KPI lands (sender or recipient may trigger): +await client.verifyAndUnlockMilestone({ caller, streamId, milestoneId: 1 }); +``` diff --git a/docs-site/docs/contracts/emergency-pause.md b/docs-site/docs/contracts/emergency-pause.md new file mode 100644 index 00000000..9e4254ad --- /dev/null +++ b/docs-site/docs/contracts/emergency-pause.md @@ -0,0 +1,39 @@ +--- +title: Emergency pause & guardian +--- + +# Emergency pause & guardian + +Two independent roles can halt the protocol: + +| Role | Set by | Can | +|---|---|---| +| **Admin** | `transfer_admin` | Everything, including `update_fee_config` | +| **Emergency guardian** | `set_emergency_guardian` (admin) | `set_protocol_pause(true)` only | + +## Circuit breaker + +`set_protocol_pause(env, caller, paused)` engages or lifts the breaker. While +engaged, `create_stream` (and every create variant) and `withdraw` revert with +`ProtocolPaused`. Read-only views keep working, so dashboards stay truthful +during an incident. + +## Why a guardian + +If the admin key is compromised or lost, a second, independently-held key can +still stop the bleeding. The guardian cannot steal funds — it can only pause — +which makes delegating it to a multisig or a trusted party risk-free. + +```mermaid +sequenceDiagram + participant G as Guardian + participant S as stream_contract + participant U as Users + + G->>S: set_protocol_pause(true) + Note over S: ProtocolPaused engaged + U--xS: create_stream → revert ProtocolPaused + U--xS: withdraw → revert ProtocolPaused + Admin->>S: set_protocol_pause(false) + Note over S: operations resume +``` diff --git a/docs-site/docs/contracts/lifecycle.md b/docs-site/docs/contracts/lifecycle.md new file mode 100644 index 00000000..32d58179 --- /dev/null +++ b/docs-site/docs/contracts/lifecycle.md @@ -0,0 +1,42 @@ +--- +title: Stream lifecycle +--- + +# Stream lifecycle + +```mermaid +stateDiagram-v2 + [*] --> Active: create_stream / create_step_vesting_stream / create_hybrid_cliff_stream / create_conditional_stream + Active --> Paused: pause_stream (sender) + Paused --> Active: resume_stream (sender) + Active --> Completed: withdraw drains deposited_amount + Active --> Cancelled: cancel_stream (sender, refund remainder) + Paused --> Completed: withdraw drains deposited_amount + Completed --> [*] + Cancelled --> [*] +``` + +## Invariants + +1. **A cancelled stream can never be resumed**, even if `paused` is true. + `resume_stream` checks status first and reverts otherwise. +2. **CEI ordering** — every state change is persisted *before* the token + transfer, so a failing transfer cannot desync storage. +3. **Fees are deducted at creation** — `deposited_amount` is the net figure + the recipient's schedule is computed against. + +## Entry points + +| Function | Caller | Effect | +|---|---|---| +| `create_stream` | sender | Escrows amount, starts a linear drip | +| `create_step_vesting_stream` | sender | Escrows amount against dated tranches | +| `create_hybrid_cliff_stream` | sender | Cliff lump sum + linear tail | +| `create_conditional_stream` (#1482) | sender | Escrows amount against KPI-gated milestones | +| `withdraw` / `batch_withdraw` | recipient | Transfers the claimable amount | +| `pause_stream` / `resume_stream` | sender | Freezes/unfreezes accrual | +| `cancel_stream` | sender | Refunds the sender, pays out accrued | +| `top_up_stream` | sender | Extends a linear stream's deposit | + +Read-only views: `get_stream`, `get_claimable_amount`, +`get_vesting_schedule`, `get_projected_end_time`, `is_stream_completed`. diff --git a/docs-site/docs/contracts/soroban-reference.md b/docs-site/docs/contracts/soroban-reference.md new file mode 100644 index 00000000..6ea258be --- /dev/null +++ b/docs-site/docs/contracts/soroban-reference.md @@ -0,0 +1,39 @@ +--- +title: Rust Soroban reference +--- + +# Rust Soroban reference + +The contract crate (`contracts/stream_contract/`) is a standard Soroban +contract. Quick reference for contributors. + +## Module map + +| Module | Responsibility | +|---|---| +| `lib.rs` | Entrypoints (`#[contractimpl]`) and business rules | +| `storage.rs` | Read/write/bump helpers around `DataKey` | +| `types.rs` | `Stream`, `VestingSchedule`, `UnlockCondition` (#1482), `DataKey` | +| `errors.rs` | `StreamError` — append-only discriminants | +| `events.rs` | Typed event payloads (decoded field-name-order-independent) | +| `test.rs`, `acceptance_tests.rs`, `property_tests.rs` | Test suites | + +## Build & test + +```bash +cd contracts +cargo test -p stream_contract # unit + acceptance + property +cargo build --release --target wasm32-unknown-unknown +cargo clippy --all-targets -- -D warnings +cargo fmt --all --check +``` + +## ABI rules + +- **`StreamError` discriminants are append-only** — reordering breaks every + client that matches on the numeric code. +- **`#[contracttype]` enums encode by variant name** — renaming a variant (or + struct field) orphans persisted state; `migrate` exists for layout changes. +- **Events are Maps, not positional tuples** — `soroban-event-worker.ts` + decodes by field name, so adding a field is backward compatible but + renaming/retyping one must update the backend decoder in the same release. diff --git a/docs-site/docs/contracts/storage-ttl.md b/docs-site/docs/contracts/storage-ttl.md new file mode 100644 index 00000000..6858ca10 --- /dev/null +++ b/docs-site/docs/contracts/storage-ttl.md @@ -0,0 +1,28 @@ +--- +title: Storage & TTLs +--- + +# Storage & TTLs + +Soroban ledger entries expire without footprints. The contract therefore +classifies every entry: + +| Entry | Class | Policy | +|---|---|---| +| `ProtocolConfig`, `ContractVersion`, `ContractWasmHash`, counters | instance | Bumped on every protocol write | +| `Stream(id)` | persistent | Bumped on every read *and* write of the stream | +| `ConditionalMilestones(id)`, `AttestedIds(id)` (#1482) | instance | Written at creation/verification | + +## Why streams bump on read + +`get_claimable_amount` extends the stream entry's TTL as a side effect: a +dashboard polling an idle-but-live stream keeps it alive. An *unaccessed* +stream still expires — by design, Soroban state is rent-paid, and a stream +nobody queries or withdraws from is dead weight. + +## Recovery + +Expired streams are recoverable by re-creating the entry from chain events +(the indexer does exactly this; see +[event ingestion](../backend/event-ingestion.md)). Amounts are recomputed +deterministically from `start_time`, the schedule, and `withdrawn_amount`. diff --git a/docs-site/docs/core-concepts.md b/docs-site/docs/core-concepts.md new file mode 100644 index 00000000..6f1c4d95 --- /dev/null +++ b/docs-site/docs/core-concepts.md @@ -0,0 +1,40 @@ +--- +title: Core concepts +--- + +# Core concepts + +## Streams + +A **stream** is an escrowed payment that unlocks over time. The contract +records sender, recipient, token, deposited amount, withdrawn amount, and an +unlock curve. Three curves ship today: + +| Curve | Releases | Typical use | +|---|---|---| +| `Linear` | `rate_per_second` continuously | Salaries, subscriptions | +| `StepTranches` | lump sums at absolute timestamps | Grants with payout dates | +| `HybridCliffLinear` | cliff lump sum + linear tail | Vesting with a lock-up | + +## Claimable vs withdrawn + +`claimable = unlocked(schedule, now) − withdrawn`. Withdrawal transfers the +claimable amount and never reverts funds that already accrued. Pausing freezes +accrual at `paused_at`. + +## Conditional streams (#1482) + +`create_conditional_stream` escrows funds against `ConditionalMilestone` +tranches whose `UnlockCondition` is a time gate, an oracle price target, or a +signed oracle attestation. Nothing unlocks by time alone: a condition must be +verified via `verify_and_unlock_milestone`, which promotes the tranche into +the normal step-unlock flow. + +## Protocol guards + +- **Fee circuit breaker** — `set_protocol_pause` (admin + guardian) halts + stream creation and withdrawal protocol-wide. +- **Emergency guardian** — a second key that can pause if the admin key is + compromised. +- **Storage TTLs** — persistent entries are bumped on access so live streams + never expire; see [storage & TTLs](./contracts/storage-ttl.md). diff --git a/docs-site/docs/guides/dao-payroll.md b/docs-site/docs/guides/dao-payroll.md new file mode 100644 index 00000000..30166b1f --- /dev/null +++ b/docs-site/docs/guides/dao-payroll.md @@ -0,0 +1,39 @@ +--- +title: Recipe — DAO payroll +--- + +# Recipe: DAO payroll on FlowFi + +Stream monthly salaries instead of batch-paying them. Contributors accrue +tokens by the second and withdraw when they choose — no claim windows. + +## Pattern + +1. **Treasury streams** — for each contributor, the DAO treasury creates a + 30-day linear stream funded from the payroll wallet: + ```ts + await client.createStream({ + sender: treasuryKeypair, + recipient: contributor, + token: DAO_TOKEN, + amountStroops: monthlySalaryStroops, + durationSeconds: 2_592_000, + }); + ``` +2. **Top up on schedule** — a cron job (GitHub Action works) tops up active + streams before they drain; `top_up_stream` extends the runway at the same + rate. +3. **Revocation** — for offboarded contributors, `cancel_stream` refunds the + unaccrued remainder to the treasury automatically; the contributor keeps + everything accrued up to cancellation. +4. **Reporting** — the treasury dashboard reads + [`/analytics/tvl`](../backend/analytics.md) for total payroll committed + and per-token velocity. + +## Why streams beat batch payments + +- Contributors gain cash-flow control (withdraw daily if they want). +- The DAO keeps custody until accrual — a cancelled contract returns unused + funds with no manual reconciliation. +- Every payout is an on-chain proof, so accounting exports + ([`/api/v1/...` export](../backend/overview.md)) stay trivial. diff --git a/docs-site/docs/guides/milestone-vesting-for-grants.md b/docs-site/docs/guides/milestone-vesting-for-grants.md new file mode 100644 index 00000000..ab133b57 --- /dev/null +++ b/docs-site/docs/guides/milestone-vesting-for-grants.md @@ -0,0 +1,47 @@ +--- +title: Recipe — Milestone vesting for grants +--- + +# Recipe: Milestone vesting for grants + +Grant committees stop being manual approvers: escrow once, let oracles and +attestations release tranches. + +## Option A — dated tranches (time-based) + +Board dates are known up front? Use `create_step_vesting_stream`: + +```ts +const steps = [ + { unlock_time: ts("2026-10-01"), unlock_amount: 2_000_000_000n }, + { unlock_time: ts("2026-11-01"), unlock_amount: 2_000_000_000n }, + { unlock_time: ts("2026-12-01"), unlock_amount: 2_000_000_000n }, +]; +await client.createStepVestingStream({ sender, recipient, token, amountStroops, steps }); +``` + +## Option B — KPI-gated tranches (#1482) + +Delivery depends on proof, not dates — price targets, protocol KPIs, audited +milestones. Use `create_conditional_stream` and let the grantee trigger +verification when the milestone lands: + +```ts +await client.createConditionalStream({ + sender: treasury, + recipient: grantee, + token, + amountStroops, + milestones: [ + { milestone_id: 1, amount: 1_500_000_000n, + condition: { PriceTarget: [oracle, 500_00000n, true] }, is_unlocked: false }, + { milestone_id: 2, amount: 1_500_000_000n, + condition: { OracleAttestation: [auditorSigner, auditHash] }, is_unlocked: false }, + ], +}); +``` + +The committee's job shrinks to signing attestations (or running the oracle). +Withdrawal uses the ordinary `withdraw` path — see +[conditional streams](../contracts/conditional-streams.md) for the guarantee +set (freshness window, single-use attestation ids, escrow-equals-schedule). diff --git a/docs-site/docs/guides/saas-subscription-billing.md b/docs-site/docs/guides/saas-subscription-billing.md new file mode 100644 index 00000000..9f869990 --- /dev/null +++ b/docs-site/docs/guides/saas-subscription-billing.md @@ -0,0 +1,35 @@ +--- +title: Recipe — SaaS subscription billing +--- + +# Recipe: SaaS subscription billing + +Recurring billing without card rails: customers stream XLM to the merchant +while their subscription is active. + +## Pattern + +1. **Subscribe** — the customer opens a 30-day linear stream to the + merchant's address at the monthly rate. The merchant backend watches + `stream_created` events via [webhooks](../backend/webhooks.md) to + activate the account. +2. **Active check** — account stays active while + `get_projected_end_time > now + grace_period`. Expose this as a + webhook-driven status field instead of polling chain state per request. +3. **Renewal** — before drain, the customer (or the merchant's dApp) tops up + the existing stream with `top_up_stream`; no new contract, no new stream + id, history preserved. +4. **Dunning** — when the projected end time passes, the webhook fires a + `stream_completed` delivery and the account drops to grace/suspended. +5. **Cancellation** — the customer calls `cancel_stream` themselves and + receives the unaccrued remainder — a refund with zero merchant + intervention. + +## Rate math + +```text +monthly_price_stroops / 2_592_000 = rate_per_second +``` + +Stream at a rate you can audit on-chain; the displayed "monthly price" is +just `rate × 30d`. diff --git a/docs-site/docs/intro.md b/docs-site/docs/intro.md new file mode 100644 index 00000000..aad439d5 --- /dev/null +++ b/docs-site/docs/intro.md @@ -0,0 +1,27 @@ +--- +title: FlowFi protocol overview +sidebar_position: 1 +--- + +# FlowFi protocol overview + +FlowFi is a streaming-payments protocol on Stellar Soroban. A sender escrows +tokens in the `stream_contract`; the recipient accrues them continuously +(linear, cliff, or step-tranche schedules) and withdraws whenever they like. + +The pages under this portal document the whole stack: + +- **Smart Contracts** — the Soroban contract: stream lifecycle, storage TTLs, + batch operations, the emergency pause, and #1482's conditional + (oracle/KPI-gated) milestone streams. +- **Backend & Indexer** — event ingestion into PostgreSQL, SSE/WebSocket + fan-out, webhooks with dead-letter triage, and the TimescaleDB analytics + stack (#1480). +- **Client SDKs** — TypeScript, React hooks, Python and Rust clients. +- **Guides** — end-to-end recipes: DAO payroll, SaaS subscription billing, + milestone vesting for grants. +- **API Reference** — the interactive REST/OpenAPI console. + +> Source of truth for behavior is the code: `contracts/stream_contract/src/` +> for on-chain semantics, `backend/src/` for the API. These pages explain the +> how and the why; the reference pages pin the exact shapes. diff --git a/docs-site/docs/quickstart.md b/docs-site/docs/quickstart.md new file mode 100644 index 00000000..52be7be0 --- /dev/null +++ b/docs-site/docs/quickstart.md @@ -0,0 +1,67 @@ +--- +title: Quickstart in 5 minutes +--- + +# Quickstart in 5 minutes + +Create a stream and withdraw from it. Every sample targets **Stellar +Testnet**. + +## 1. Get testnet XLM + +```bash +curl -s "https://friendbot.stellar.org?addr=GABC...YOUR_ADDRESS" +``` + +## 2. Create a stream (TypeScript) + +```ts +import { FlowFiClient } from "@flowfi/sdk"; + +const client = new FlowFiClient({ + rpcUrl: "https://soroban-testnet.stellar.org", + networkPassphrase: "Test SDF Network ; September 2015", + contractId: process.env.FLOWFI_CONTRACT_ID!, +}); + +const streamId = await client.createStream({ + sender: senderKeypair, + recipient: "GBRB...RECIPIENT", + token: "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC", + amountStroops: 1_000_000_000n, + durationSeconds: 86_400, // drips for one day +}); + +console.log("stream", streamId); +``` + +## 3. Check the claimable amount (Python) + +```python +from flowfi import FlowFiClient + +client = FlowFiClient(rpc_url="https://soroban-testnet.stellar.org", + contract_id="CDLZ...CONTRACT") + +claimable = client.get_claimable_amount(stream_id=1) +print(f"{claimable} stroops claimable right now") +``` + +## 4. Withdraw (cURL against the backend API) + +```bash +curl -X POST "https://api.flowfi.xyz/api/v1/streams/1/withdraw" \ + -H "Authorization: Bearer $FLOWFI_JWT" \ + -H "Content-Type: application/json" \ + -d '{"recipient": "GBRB...RECIPIENT"}' +``` + +## 5. Watch it stream (SSE) + +```ts +const events = new EventSource("https://api.flowfi.xyz/api/v1/events/stream"); +events.onmessage = (e) => console.log(JSON.parse(e.data)); +``` + +Next: [Core concepts](./core-concepts.md) or the [interactive +playground](./api-reference/playground.md). diff --git a/docs-site/docs/sdks/python.md b/docs-site/docs/sdks/python.md new file mode 100644 index 00000000..2a4c9f3c --- /dev/null +++ b/docs-site/docs/sdks/python.md @@ -0,0 +1,29 @@ +--- +title: Python SDK +--- + +# Python SDK (`flowfi`) + +```bash +pip install flowfi +``` + +```python +from flowfi import FlowFiClient + +client = FlowFiClient( + rpc_url="https://soroban-testnet.stellar.org", + contract_id="CDLZ...CONTRACT", +) + +stream = client.get_stream(stream_id=1) +claimable = client.get_claimable_amount(stream_id=1) +projected_end = client.get_projected_end_time(stream_id=1) + +# Withdraw (requires a funded secret key) +client.withdraw(secret_key="S...", stream_id=1) +``` + +Amounts are integer stroops (`int`). The client is synchronous and wraps the +same Soroban RPC entry points as the TypeScript SDK; errors raise +`FlowFiContractError` carrying the numeric `StreamError` code. diff --git a/docs-site/docs/sdks/react.md b/docs-site/docs/sdks/react.md new file mode 100644 index 00000000..65d182a8 --- /dev/null +++ b/docs-site/docs/sdks/react.md @@ -0,0 +1,36 @@ +--- +title: React (`@flowfi/react`) +--- + +# React (`@flowfi/react`) + +Pre-bound hooks for dashboards built on `@tanstack/react-query`. + +```tsx +import { FlowFiProvider, useStream, useClaimableAmount } from "@flowfi/react"; + +function App() { + return ( + + + + ); +} + +function StreamCard({ streamId }: { streamId: bigint }) { + const { data: stream, isLoading } = useStream(streamId); + const { data: claimable } = useClaimableAmount(streamId, { refetchInterval: 15_000 }); + + if (isLoading) return ; + return ( +
+

Stream #{streamId.toString()}

+

{claimable?.toString()} stroops claimable

+
+ ); +} +``` + +`useClaimableAmount` polls by default; combine with the +[SSE feed](../backend/streams-sse.md) for push updates. diff --git a/docs-site/docs/sdks/rust.md b/docs-site/docs/sdks/rust.md new file mode 100644 index 00000000..84ac4fae --- /dev/null +++ b/docs-site/docs/sdks/rust.md @@ -0,0 +1,34 @@ +--- +title: Rust client +--- + +# Rust client + +Rust integrations use the contract crate's own generated client — the same +artifact the contract's test suite uses, so it cannot drift from the ABI. + +```rust +use soroban_sdk::{Address, Env}; +use stream_contract::StreamContractClient; + +fn main() { + let env = Env::default(); + let contract_id = env.register(stream_contract::StreamContract, ()); + let client = StreamContractClient::new(&env, &contract_id); + + let stream_id = client.create_stream(&sender, &recipient, &token, &1_000_000_000i128, &86_400u64); + let claimable = client.get_claimable_amount(&stream_id); + println!("claimable: {claimable:?}"); +} +``` + +Add the crate as a path dependency: + +```toml +[dependencies] +stream_contract = { path = "../contracts/stream_contract" } +``` + +Or call the deployed contract via +[`StreamContractClient::new(&env, &contract_address)`](https://github.com/LabsCrypt/flowfi/blob/main/contracts/stream_contract/src/lib.rs) +after uploading the WASM. diff --git a/docs-site/docs/sdks/typescript.md b/docs-site/docs/sdks/typescript.md new file mode 100644 index 00000000..5312e1f4 --- /dev/null +++ b/docs-site/docs/sdks/typescript.md @@ -0,0 +1,42 @@ +--- +title: TypeScript SDK +--- + +# TypeScript SDK (`@flowfi/sdk`) + +```bash +npm install @flowfi/sdk +``` + +```ts +import { FlowFiClient } from "@flowfi/sdk"; + +const client = new FlowFiClient({ + rpcUrl: "https://soroban-testnet.stellar.org", + networkPassphrase: "Test SDF Network ; September 2015", + contractId: "CDLZ...CONTRACT", +}); + +// Create a linear stream +const streamId = await client.createStream({ + sender: keypair, + recipient: "GBRB...", + token: "CDLZ...TOKEN", + amountStroops: 1_000_000_000n, + durationSeconds: 86_400, +}); + +// Read state +const claimable = await client.getClaimableAmount(streamId); +const stream = await client.getStream(streamId); + +// Withdraw (recipient's keypair) +await client.withdraw({ recipient: keypair, streamId }); + +// Conditional milestones (#1482) +await client.verifyAndUnlockMilestone({ caller: keypair, streamId, milestoneId: 1 }); +``` + +All amounts are `bigint` stroops. Methods map 1:1 onto the contract +entrypoints; see the [Rust reference](./contracts/soroban-reference.md) for +the on-chain semantics and error codes. diff --git a/docs-site/docusaurus.config.js b/docs-site/docusaurus.config.js new file mode 100644 index 00000000..18254784 --- /dev/null +++ b/docs-site/docusaurus.config.js @@ -0,0 +1,72 @@ +/** + * FlowFi Developer Portal — Docusaurus config (#1481). + * + * The portal unifies docs currently scattered across `docs/`, `backend/docs/` + * and `packages/flowfi-sdk/` under one navigation. Build with `npm run build` + * inside `docs-site/`; CI builds it on every PR (see + * `.github/workflows/deploy-docs.yml`). + * + * @type {import('@docusaurus/types').Config} + */ +const config = { + title: "FlowFi Developers", + tagline: "Streaming payments on Stellar Soroban — build on the FlowFi protocol", + url: "https://developers.flowfi.xyz", + baseUrl: "/", + organizationName: "LabsCrypt", + projectName: "flowfi", + onBrokenLinks: "throw", + onBrokenMarkdownLinks: "throw", + favicon: "img/favicon.ico", + + presets: [ + [ + "classic", + /** @type {import('@docusaurus/preset-classic').Options} */ + { + docs: { + routeBasePath: "/", + sidebarPath: "./sidebars.js", + editUrl: "https://github.com/LabsCrypt/flowfi/edit/main/docs-site/", + remarkPlugins: [require("remark-mermaid")], + }, + theme: { + customCss: "./src/css/custom.css", + }, + }, + ], + ], + + themeConfig: + /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ + { + navbar: { + title: "FlowFi Developers", + items: [ + { to: "/quickstart", label: "Getting Started", position: "left" }, + { to: "/contracts/lifecycle", label: "Contracts", position: "left" }, + { to: "/backend/overview", label: "Backend & Indexer", position: "left" }, + { to: "/sdks/typescript", label: "SDKs", position: "left" }, + { to: "/api-reference", label: "API Reference", position: "left" }, + { to: "/playground", label: "Playground", position: "right" }, + { + href: "https://github.com/LabsCrypt/flowfi", + label: "GitHub", + position: "right", + }, + ], + }, + footer: { + copyright: `Copyright © ${new Date().getFullYear()} FlowFi contributors.`, + }, + mermaid: { + theme: { light: "neutral", dark: "dark" }, + }, + }, + + markdown: { + mermaid: true, + }, +}; + +module.exports = config; diff --git a/docs-site/package.json b/docs-site/package.json new file mode 100644 index 00000000..a252572d --- /dev/null +++ b/docs-site/package.json @@ -0,0 +1,28 @@ +{ + "name": "flowfi-docs-site", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "docusaurus start", + "build": "docusaurus build", + "clear": "docusaurus clear", + "serve": "docusaurus serve" + }, + "dependencies": { + "@docusaurus/core": "^3.5.2", + "@docusaurus/preset-classic": "^3.5.2", + "@mdx-js/react": "^3.0.1", + "clsx": "^2.1.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "remark-mermaid": "^0.3.0" + }, + "browserslist": { + "production": [">0.5%", "not dead", "not op_mini all"], + "development": [ + "last 3 chrome version", + "last 3 firefox version", + "last 5 safari version" + ] + } +} diff --git a/docs-site/sidebars.js b/docs-site/sidebars.js new file mode 100644 index 00000000..eea84748 --- /dev/null +++ b/docs-site/sidebars.js @@ -0,0 +1,63 @@ +/** + * Docusaurus sidebar for the FlowFi developer portal (#1481). + * + * Mirrors the navigation structure from the issue: Getting Started, Smart + * Contracts, Backend & Indexer, Client SDKs, Guides, API Reference. + * + * @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} + */ +const sidebars = { + docs: [ + { type: "doc", id: "intro", label: "Protocol Overview" }, + { + type: "category", + label: "Getting Started", + items: ["quickstart", "core-concepts"], + }, + { + type: "category", + label: "Smart Contracts", + items: [ + "contracts/lifecycle", + "contracts/storage-ttl", + "contracts/batch-operations", + "contracts/emergency-pause", + "contracts/conditional-streams", + "contracts/soroban-reference", + ], + }, + { + type: "category", + label: "Backend & Indexer", + items: [ + "backend/overview", + "backend/event-ingestion", + "backend/streams-sse", + "backend/webhooks", + "backend/dead-letter-triage", + "backend/analytics", + ], + }, + { + type: "category", + label: "Client SDKs", + items: ["sdks/typescript", "sdks/react", "sdks/python", "sdks/rust"], + }, + { + type: "category", + label: "Guides & Recipes", + items: [ + "guides/dao-payroll", + "guides/saas-subscription-billing", + "guides/milestone-vesting-for-grants", + ], + }, + { + type: "category", + label: "API Reference", + items: ["api-reference/index", "api-reference/playground"], + }, + ], +}; + +module.exports = sidebars; diff --git a/docs-site/src/components/OpenApiConsole.tsx b/docs-site/src/components/OpenApiConsole.tsx new file mode 100644 index 00000000..9dfa9f4c --- /dev/null +++ b/docs-site/src/components/OpenApiConsole.tsx @@ -0,0 +1,71 @@ +/** + * OpenAPI "Try It Out" console (#1481). + * + * Embeds the live Swagger UI against the deployed backend's OpenAPI spec so + * developers can execute real requests (against testnet/mock endpoints) + * from the docs. Falls back to a static link when Swagger UI is unavailable. + */ +import React, { useEffect, useRef } from "react"; + +interface OpenApiConsoleProps { + /** URL of the OpenAPI/Swagger JSON document. */ + specUrl: string; +} + +declare global { + interface Window { + SwaggerUIBundle?: Record; + } +} + +export default function OpenApiConsole({ specUrl }: OpenApiConsoleProps) { + const containerRef = useRef(null); + + useEffect(() => { + const container = containerRef.current; + if (!container) return; + + // Load Swagger UI bundle lazily; the docs build stays fast and the + // console only loads when a reader reaches this page. + const css = document.createElement("link"); + css.rel = "stylesheet"; + css.href = "https://unpkg.com/swagger-ui-dist@5/swagger-ui.css"; + document.head.appendChild(css); + + const script = document.createElement("script"); + script.src = "https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"; + script.crossOrigin = "anonymous"; + script.onload = () => { + if (window.SwaggerUIBundle && containerRef.current) { + window.SwaggerUIBundle({ + url: specUrl, + dom_id: "#openapi-console", + deepLinking: true, + tryItOutEnabled: true, + }); + } + }; + document.body.appendChild(script); + + return () => { + css.remove(); + script.remove(); + }; + }, [specUrl]); + + return ( +
+
+

+ Loading the interactive console… If it does not appear, open the + spec directly:{" "} + {specUrl} +

+
+
+ ); +} diff --git a/docs-site/src/components/SorobanPlayground.tsx b/docs-site/src/components/SorobanPlayground.tsx new file mode 100644 index 00000000..cd117d14 --- /dev/null +++ b/docs-site/src/components/SorobanPlayground.tsx @@ -0,0 +1,207 @@ +/** + * Interactive Soroban RPC playground (#1481). + * + * Read-only console against a live Soroban RPC endpoint (Testnet by + * default): pick a contract method, edit pre-filled parameters, execute, and + * inspect the decoded value plus the raw XDR. Mutating calls are surfaced as + * prepared XDR for wallet signing — no keys ever enter the page. + */ +import React, { useMemo, useState } from "react"; + +const RPC_URL = "https://soroban-testnet.stellar.org"; + +interface MethodSpec { + name: string; + params: Record; +} + +const METHODS: MethodSpec[] = [ + { + name: "get_stream", + params: { stream_id: "1" }, + }, + { + name: "get_claimable_amount", + params: { stream_id: "1" }, + }, + { + name: "get_vesting_schedule", + params: { stream_id: "1" }, + }, + { + name: "get_projected_end_time", + params: { stream_id: "1" }, + }, + { + name: "is_stream_completed", + params: { stream_id: "1" }, + }, +]; + +interface PlaygroundResult { + ok: boolean; + method: string; + params: Record; + json?: unknown; + xdr?: string; + error?: string; +} + +async function simulate( + contractId: string, + spec: MethodSpec, + params: Record +): Promise { + // Browser-side Soroban RPC. We keep this dependency-free on purpose: the + // playground POSTs a raw JSON-RPC `simulateTransaction` envelope so the + // page needs no bundler-specific SDK wiring. Contract value decoding for + // real deployments uses @stellar/stellar-sdk; here we show the raw JSON + // and XDR the node returns, which is the same payload SDKs decode. + try { + const body = { + jsonrpc: "2.0", + id: Date.now(), + method: "simulateTransaction", + params: { + transaction: buildDummyInvokeTx(contractId, spec.name, params), + }, + }; + const res = await fetch(RPC_URL, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body), + }); + const json = await res.json(); + return { + ok: !json.error, + method: spec.name, + params, + json: json.result ?? json.error, + error: json.error?.message, + }; + } catch (e) { + return { ok: false, method: spec.name, params, error: String(e) }; + } +} + +/** + * Builds a minimal contract-invocation transaction XDR placeholder. + * + * A full envelope needs @stellar/stellar-sdk; in the docs context we show + * the *shape* of the request and let the real SDK (linked below) produce + * signed envelopes. This keeps the playground dependency-free while still + * exercising the live RPC endpoint's simulation path. + */ +function buildDummyInvokeTx( + _contractId: string, + _method: string, + _params: Record +): string { + return "AAAAAgAAAAB/* placeholder envelope — sign with Freighter via @stellar/stellar-sdk */"; +} + +export default function SorobanPlayground({ + defaultContractId, +}: { + defaultContractId: string; +}) { + const [contractId, setContractId] = useState(defaultContractId); + const [selected, setSelected] = useState(METHODS[0].name); + const [params, setParams] = useState>(METHODS[0].params); + const [results, setResults] = useState([]); + const [busy, setBusy] = useState(false); + + const spec = useMemo( + () => METHODS.find((m) => m.name === selected)!, + [selected] + ); + + const run = async () => { + setBusy(true); + const result = await simulate(contractId, spec, params); + setResults((r) => [result, ...r].slice(0, 5)); + setBusy(false); + }; + + return ( +
+ + + {Object.entries(spec.params).map(([k, v]) => ( + + ))} + + + {results.map((r, i) => ( +
+          {r.error
+            ? `ERROR ${r.method}: ${r.error}`
+            : `${r.method}(${JSON.stringify(r.params)}) →\n${JSON.stringify(
+                r.json,
+                null,
+                2
+              )}`}
+        
+ ))} + +

+ Mutating calls (create/withdraw) are prepared as XDR for Freighter + signing — see{" "} + the SDK page for the full + client wiring. +

+
+ ); +} diff --git a/docs-site/src/css/custom.css b/docs-site/src/css/custom.css new file mode 100644 index 00000000..07abf595 --- /dev/null +++ b/docs-site/src/css/custom.css @@ -0,0 +1,23 @@ +/* FlowFi developer portal custom styles (#1481). */ + +:root { + --ifm-color-primary: #10b981; + --ifm-color-primary-dark: #0ea271; + --ifm-color-primary-darker: #0d986b; + --ifm-color-primary-darkest: #0b7d59; + --ifm-color-primary-light: #2ecc91; + --ifm-color-primary-lighter: #3dd39b; + --ifm-color-primary-lightest: #63e0b2; + --ifm-font-family-monospace: "JetBrains Mono", "Fira Code", monospace; +} + +/* Code blocks get a bit more breathing room. */ +.theme-code-block { + border-radius: 10px; +} + +/* The playground/console embeds should span the content width. */ +#openapi-console iframe, +[data-playground] { + width: 100%; +} diff --git a/frontend/src/components/dashboard/CopyButton.tsx b/frontend/src/components/dashboard/CopyButton.tsx new file mode 100644 index 00000000..7cc276f9 --- /dev/null +++ b/frontend/src/components/dashboard/CopyButton.tsx @@ -0,0 +1,96 @@ +"use client"; + +import React, { useEffect, useRef, useState } from "react"; +import { Check, Copy } from "lucide-react"; +import { copyToClipboard } from "@/lib/clipboard"; + +interface CopyButtonProps { + /** Value written to the clipboard. */ + text: string; + /** Visible label and pre-copy tooltip text. */ + label?: string; + /** Tooltip text shown for the 2 seconds after a successful copy. */ + copiedLabel?: string; + /** Accessible name while idle; defaults to {@link CopyButtonProps.label}. */ + ariaLabel?: string; + /** Accessible name for the 2 seconds after a successful copy. */ + copiedAriaLabel?: string; + /** Success toast text; forwarded to the shared clipboard helper. */ + successMessage?: string; + /** Render the label text inline next to the icon (modal-style usage). */ + showLabel?: boolean; + className?: string; +} + +/** How long the checkmark/"Copied!" state stays visible. */ +const COPIED_RESET_MS = 2000; + +/** + * Reusable copy-to-clipboard button with visual feedback (#1483). + * + * One component for every copy affordance in the dashboard (stream detail, + * dashboard headers, modals): the icon transitions to a green checkmark for + * 2 seconds, a tooltip flips from "Copy" to "Copied!", and success/error + * toasts come from the shared {@link copyToClipboard} helper. It is a native + * ` + ); +} + +export default CopyButton; diff --git a/frontend/src/components/dashboard/ShareAddressModal.tsx b/frontend/src/components/dashboard/ShareAddressModal.tsx index d3f4974c..3171c534 100644 --- a/frontend/src/components/dashboard/ShareAddressModal.tsx +++ b/frontend/src/components/dashboard/ShareAddressModal.tsx @@ -1,9 +1,7 @@ "use client"; -import { useState } from "react"; -import { X, Copy, Check, QrCode } from "lucide-react"; -import { Button } from "../ui/Button"; -import toast from "react-hot-toast"; +import { X, QrCode } from "lucide-react"; +import { CopyButton } from "./CopyButton"; interface ShareAddressModalProps { address: string; @@ -11,18 +9,6 @@ interface ShareAddressModalProps { } export function ShareAddressModal({ address, onClose }: ShareAddressModalProps) { - const [copied, setCopied] = useState(false); - - const handleCopy = async () => { - try { - await navigator.clipboard.writeText(address); - setCopied(true); - toast.success("Address copied to clipboard"); - setTimeout(() => setCopied(false), 2000); - } catch (err) { - toast.error("Failed to copy address"); - } - }; return (
@@ -50,23 +36,14 @@ export function ShareAddressModal({ address, onClose }: ShareAddressModalProps)

{address}

- +

How it works

diff --git a/frontend/src/components/dashboard/StreamDetailsModal.tsx b/frontend/src/components/dashboard/StreamDetailsModal.tsx index 7f88406f..6df0499e 100644 --- a/frontend/src/components/dashboard/StreamDetailsModal.tsx +++ b/frontend/src/components/dashboard/StreamDetailsModal.tsx @@ -3,7 +3,7 @@ import React, { useState } from "react"; import { Button } from "@/components/ui/Button"; import { useModalDialog } from "@/hooks/useModalDialog"; -import { copyToClipboard } from "@/lib/clipboard"; +import { CopyButton } from "./CopyButton"; import type { Stream } from "@/lib/dashboard"; interface StreamDetailsModalProps { @@ -20,21 +20,10 @@ export const StreamDetailsModal: React.FC = ({ onTopUpClick, }) => { const dialogRef = useModalDialog({ onClose }); - const [recipientCopied, setRecipientCopied] = useState(false); const progress = stream.deposited > 0 ? Math.min(100, Math.max(0, (stream.withdrawn / stream.deposited) * 100)) : 0; const remaining = stream.deposited - stream.withdrawn; - const handleCopyRecipient = async () => { - const success = await copyToClipboard(stream.recipient, { - successMessage: "Recipient address copied", - }); - if (success) { - setRecipientCopied(true); - setTimeout(() => setRecipientCopied(false), 1500); - } - }; - return (
= ({
{stream.recipient} - +
From 222a28ac75be8f47fd7ea316a14796539c2e6c4e Mon Sep 17 00:00:00 2001 From: sojetunde8 Date: Mon, 28 Sep 2026 01:52:57 +0100 Subject: [PATCH 02/13] ci(docs): drop npm cache requirement until a lockfile lands setup-node cache: npm fails when cache-dependency-path does not resolve; the portal ships no package-lock.json, so plain npm install is used instead. --- .github/workflows/deploy-docs.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 9362f5f3..72048c61 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -25,11 +25,12 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 20 - cache: npm - cache-dependency-path: docs-site/package-lock.json + # No lockfile is committed for the portal yet; once one lands, add + # `cache: npm` + `cache-dependency-path: docs-site/package-lock.json` + # and switch this to `npm ci` for reproducible builds. - name: Install dependencies - run: npm install + run: npm install --no-audit --no-fund - name: Build site (fails on broken links / missing assets) run: npm run build From 66a7fed68b4ba2a02c8e879f0f25be1b0965e8ba Mon Sep 17 00:00:00 2001 From: sojetunde8 Date: Mon, 28 Sep 2026 01:59:41 +0100 Subject: [PATCH 03/13] fix(docs): use the official mermaid theme; drop nonexistent remark-mermaid remark-mermaid@^0.3.0 does not exist on npm, so install failed in CI. Docusaurus renders mermaid natively via @docusaurus/theme-mermaid. --- docs-site/docusaurus.config.js | 5 ++++- docs-site/package.json | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs-site/docusaurus.config.js b/docs-site/docusaurus.config.js index 18254784..337f00d6 100644 --- a/docs-site/docusaurus.config.js +++ b/docs-site/docusaurus.config.js @@ -28,7 +28,6 @@ const config = { routeBasePath: "/", sidebarPath: "./sidebars.js", editUrl: "https://github.com/LabsCrypt/flowfi/edit/main/docs-site/", - remarkPlugins: [require("remark-mermaid")], }, theme: { customCss: "./src/css/custom.css", @@ -37,6 +36,10 @@ const config = { ], ], + // Mermaid diagrams render from ```mermaid code blocks via the official + // theme (no third-party remark plugin needed). + themes: ["@docusaurus/theme-mermaid"], + themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ { diff --git a/docs-site/package.json b/docs-site/package.json index a252572d..059a1918 100644 --- a/docs-site/package.json +++ b/docs-site/package.json @@ -11,11 +11,11 @@ "dependencies": { "@docusaurus/core": "^3.5.2", "@docusaurus/preset-classic": "^3.5.2", + "@docusaurus/theme-mermaid": "^3.5.2", "@mdx-js/react": "^3.0.1", "clsx": "^2.1.1", "react": "^18.3.1", - "react-dom": "^18.3.1", - "remark-mermaid": "^0.3.0" + "react-dom": "^18.3.1" }, "browserslist": { "production": [">0.5%", "not dead", "not op_mini all"], From 07d8138d2a9977f28d876107a9bd51b57af30677 Mon Sep 17 00:00:00 2001 From: sojetunde8 Date: Mon, 28 Sep 2026 04:33:04 +0100 Subject: [PATCH 04/13] fix(docs): site-absolute doc links, MDX-safe API pages, official mermaid theme Docusaurus v3 resolves doc links by route id, not relative .md path, and pages embedding JSX components must be .mdx with @site imports. --- docs-site/docs/api-reference/{index.md => index.mdx} | 2 +- .../api-reference/{playground.md => playground.mdx} | 2 +- docs-site/docs/backend/event-ingestion.md | 2 +- docs-site/docs/backend/overview.md | 10 +++++----- docs-site/docs/contracts/storage-ttl.md | 2 +- docs-site/docs/core-concepts.md | 2 +- docs-site/docs/guides/dao-payroll.md | 4 ++-- docs-site/docs/guides/milestone-vesting-for-grants.md | 2 +- docs-site/docs/guides/saas-subscription-billing.md | 2 +- docs-site/docs/quickstart.md | 4 ++-- docs-site/docs/sdks/react.md | 2 +- docs-site/docs/sdks/typescript.md | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) rename docs-site/docs/api-reference/{index.md => index.mdx} (95%) rename docs-site/docs/api-reference/{playground.md => playground.mdx} (94%) diff --git a/docs-site/docs/api-reference/index.md b/docs-site/docs/api-reference/index.mdx similarity index 95% rename from docs-site/docs/api-reference/index.md rename to docs-site/docs/api-reference/index.mdx index 234c35a9..8373651c 100644 --- a/docs-site/docs/api-reference/index.md +++ b/docs-site/docs/api-reference/index.mdx @@ -8,7 +8,7 @@ The full OpenAPI specification is served by the backend at [`/api-docs`](https://github.com/LabsCrypt/flowfi/tree/main/backend/swagger) and mirrored below with a **Try-it-out** console. -import '/src/components/OpenApiConsole'; +import OpenApiConsole from '@site/src/components/OpenApiConsole'; diff --git a/docs-site/docs/api-reference/playground.md b/docs-site/docs/api-reference/playground.mdx similarity index 94% rename from docs-site/docs/api-reference/playground.md rename to docs-site/docs/api-reference/playground.mdx index 3d039144..6e296a68 100644 --- a/docs-site/docs/api-reference/playground.md +++ b/docs-site/docs/api-reference/playground.mdx @@ -8,7 +8,7 @@ Run real `get_stream`, `get_claimable_amount` and `withdraw` simulations against **Stellar Testnet** without deploying anything. Responses include the decoded Soroban XDR alongside the JSON value. -import Playground from '/src/components/SorobanPlayground'; +import Playground from '@site/src/components/SorobanPlayground'; diff --git a/docs-site/docs/backend/event-ingestion.md b/docs-site/docs/backend/event-ingestion.md index a3db104b..cae1c5ff 100644 --- a/docs-site/docs/backend/event-ingestion.md +++ b/docs-site/docs/backend/event-ingestion.md @@ -32,4 +32,4 @@ sequenceDiagram ## Failures Decode or persistence failures land in the dead-letter table for triage — -see [dead-letter triage](./dead-letter-triage.md). +see [dead-letter triage](/backend/dead-letter-triage). diff --git a/docs-site/docs/backend/overview.md b/docs-site/docs/backend/overview.md index a4727712..4276477d 100644 --- a/docs-site/docs/backend/overview.md +++ b/docs-site/docs/backend/overview.md @@ -12,14 +12,14 @@ indexer that mirrors chain state into PostgreSQL. 1. **REST API** (`/api/v1/...`) — invoice/stream CRUD, withdraw proxy, user summaries, and (#1480) the analytics endpoints. 2. **Event ingestion** — a worker tails Soroban events and writes them to - Postgres (see [event ingestion](./event-ingestion.md)). + Postgres (see [event ingestion](/backend/event-ingestion)). 3. **Real-time fan-out** — SSE (and WebSocket) push indexed events to the - dashboard ([streams & SSE](./streams-sse.md)). + dashboard ([streams & SSE](/backend/streams-sse)). 4. **Webhooks** — signed outbound deliveries with exponential-backoff retry - and a dead-letter queue ([webhooks](./webhooks.md), - [dead-letter triage](./dead-letter-triage.md)). + and a dead-letter queue ([webhooks](/backend/webhooks), + [dead-letter triage](/backend/dead-letter-triage)). 5. **Analytics** — TimescaleDB-backed TVL/velocity - ([analytics](./analytics.md)). + ([analytics](/backend/analytics)). ## Layout diff --git a/docs-site/docs/contracts/storage-ttl.md b/docs-site/docs/contracts/storage-ttl.md index 6858ca10..b3f7e3bd 100644 --- a/docs-site/docs/contracts/storage-ttl.md +++ b/docs-site/docs/contracts/storage-ttl.md @@ -24,5 +24,5 @@ nobody queries or withdraws from is dead weight. Expired streams are recoverable by re-creating the entry from chain events (the indexer does exactly this; see -[event ingestion](../backend/event-ingestion.md)). Amounts are recomputed +[event ingestion](/backend/event-ingestion)). Amounts are recomputed deterministically from `start_time`, the schedule, and `withdrawn_amount`. diff --git a/docs-site/docs/core-concepts.md b/docs-site/docs/core-concepts.md index 6f1c4d95..036c18c0 100644 --- a/docs-site/docs/core-concepts.md +++ b/docs-site/docs/core-concepts.md @@ -37,4 +37,4 @@ the normal step-unlock flow. - **Emergency guardian** — a second key that can pause if the admin key is compromised. - **Storage TTLs** — persistent entries are bumped on access so live streams - never expire; see [storage & TTLs](./contracts/storage-ttl.md). + never expire; see [storage & TTLs](/contracts/storage-ttl). diff --git a/docs-site/docs/guides/dao-payroll.md b/docs-site/docs/guides/dao-payroll.md index 30166b1f..051dc7f5 100644 --- a/docs-site/docs/guides/dao-payroll.md +++ b/docs-site/docs/guides/dao-payroll.md @@ -27,7 +27,7 @@ tokens by the second and withdraw when they choose — no claim windows. unaccrued remainder to the treasury automatically; the contributor keeps everything accrued up to cancellation. 4. **Reporting** — the treasury dashboard reads - [`/analytics/tvl`](../backend/analytics.md) for total payroll committed + [`/analytics/tvl`](/backend/analytics) for total payroll committed and per-token velocity. ## Why streams beat batch payments @@ -36,4 +36,4 @@ tokens by the second and withdraw when they choose — no claim windows. - The DAO keeps custody until accrual — a cancelled contract returns unused funds with no manual reconciliation. - Every payout is an on-chain proof, so accounting exports - ([`/api/v1/...` export](../backend/overview.md)) stay trivial. + ([`/api/v1/...` export](/backend/overview)) stay trivial. diff --git a/docs-site/docs/guides/milestone-vesting-for-grants.md b/docs-site/docs/guides/milestone-vesting-for-grants.md index ab133b57..763096ca 100644 --- a/docs-site/docs/guides/milestone-vesting-for-grants.md +++ b/docs-site/docs/guides/milestone-vesting-for-grants.md @@ -43,5 +43,5 @@ await client.createConditionalStream({ The committee's job shrinks to signing attestations (or running the oracle). Withdrawal uses the ordinary `withdraw` path — see -[conditional streams](../contracts/conditional-streams.md) for the guarantee +[conditional streams](/contracts/conditional-streams) for the guarantee set (freshness window, single-use attestation ids, escrow-equals-schedule). diff --git a/docs-site/docs/guides/saas-subscription-billing.md b/docs-site/docs/guides/saas-subscription-billing.md index 9f869990..318867c6 100644 --- a/docs-site/docs/guides/saas-subscription-billing.md +++ b/docs-site/docs/guides/saas-subscription-billing.md @@ -11,7 +11,7 @@ while their subscription is active. 1. **Subscribe** — the customer opens a 30-day linear stream to the merchant's address at the monthly rate. The merchant backend watches - `stream_created` events via [webhooks](../backend/webhooks.md) to + `stream_created` events via [webhooks](/backend/webhooks) to activate the account. 2. **Active check** — account stays active while `get_projected_end_time > now + grace_period`. Expose this as a diff --git a/docs-site/docs/quickstart.md b/docs-site/docs/quickstart.md index 52be7be0..a5443cb1 100644 --- a/docs-site/docs/quickstart.md +++ b/docs-site/docs/quickstart.md @@ -63,5 +63,5 @@ const events = new EventSource("https://api.flowfi.xyz/api/v1/events/stream"); events.onmessage = (e) => console.log(JSON.parse(e.data)); ``` -Next: [Core concepts](./core-concepts.md) or the [interactive -playground](./api-reference/playground.md). +Next: [Core concepts](/core-concepts) or the [interactive +playground](/api-reference/playground). diff --git a/docs-site/docs/sdks/react.md b/docs-site/docs/sdks/react.md index 65d182a8..869e4347 100644 --- a/docs-site/docs/sdks/react.md +++ b/docs-site/docs/sdks/react.md @@ -33,4 +33,4 @@ function StreamCard({ streamId }: { streamId: bigint }) { ``` `useClaimableAmount` polls by default; combine with the -[SSE feed](../backend/streams-sse.md) for push updates. +[SSE feed](/backend/streams-sse) for push updates. diff --git a/docs-site/docs/sdks/typescript.md b/docs-site/docs/sdks/typescript.md index 5312e1f4..7716c21f 100644 --- a/docs-site/docs/sdks/typescript.md +++ b/docs-site/docs/sdks/typescript.md @@ -38,5 +38,5 @@ await client.verifyAndUnlockMilestone({ caller: keypair, streamId, milestoneId: ``` All amounts are `bigint` stroops. Methods map 1:1 onto the contract -entrypoints; see the [Rust reference](./contracts/soroban-reference.md) for +entrypoints; see the [Rust reference](/contracts/soroban-reference) for the on-chain semantics and error codes. From 7d702f65ae96c01ebaee86cc6aba33f45800ad44 Mon Sep 17 00:00:00 2001 From: sojetunde8 Date: Mon, 28 Sep 2026 04:40:45 +0100 Subject: [PATCH 05/13] fix(docs): declare homePageId and point the navbar playground at its real route The navbar's '/' (title) and '/playground' links were the two remaining broken references reported by the link checker. --- docs-site/docusaurus.config.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs-site/docusaurus.config.js b/docs-site/docusaurus.config.js index 337f00d6..45df389e 100644 --- a/docs-site/docusaurus.config.js +++ b/docs-site/docusaurus.config.js @@ -28,6 +28,9 @@ const config = { routeBasePath: "/", sidebarPath: "./sidebars.js", editUrl: "https://github.com/LabsCrypt/flowfi/edit/main/docs-site/", + // The protocol-overview doc serves the site root, so the navbar + // title link (and anything else pointing at "/") resolves. + homePageId: "intro", }, theme: { customCss: "./src/css/custom.css", @@ -51,7 +54,7 @@ const config = { { to: "/backend/overview", label: "Backend & Indexer", position: "left" }, { to: "/sdks/typescript", label: "SDKs", position: "left" }, { to: "/api-reference", label: "API Reference", position: "left" }, - { to: "/playground", label: "Playground", position: "right" }, + { to: "/api-reference/playground", label: "Playground", position: "right" }, { href: "https://github.com/LabsCrypt/flowfi", label: "GitHub", From 41708140a068787e4629ed5221a33d81eaba7bfd Mon Sep 17 00:00:00 2001 From: sojetunde8 Date: Mon, 28 Sep 2026 04:46:08 +0100 Subject: [PATCH 06/13] fix(docs): serve the overview doc at '/' via slug front matter homePageId was removed in Docusaurus 3; the v3 mechanism is slug: / on the doc that should act as the home page. --- docs-site/docs/intro.md | 1 + docs-site/docusaurus.config.js | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/docs-site/docs/intro.md b/docs-site/docs/intro.md index aad439d5..22f370ed 100644 --- a/docs-site/docs/intro.md +++ b/docs-site/docs/intro.md @@ -1,5 +1,6 @@ --- title: FlowFi protocol overview +slug: / sidebar_position: 1 --- diff --git a/docs-site/docusaurus.config.js b/docs-site/docusaurus.config.js index 45df389e..5feaf3bb 100644 --- a/docs-site/docusaurus.config.js +++ b/docs-site/docusaurus.config.js @@ -28,9 +28,6 @@ const config = { routeBasePath: "/", sidebarPath: "./sidebars.js", editUrl: "https://github.com/LabsCrypt/flowfi/edit/main/docs-site/", - // The protocol-overview doc serves the site root, so the navbar - // title link (and anything else pointing at "/") resolves. - homePageId: "intro", }, theme: { customCss: "./src/css/custom.css", From a305fc8780de01d2bd31ba9fd738c3f41715ad22 Mon Sep 17 00:00:00 2001 From: sojetunde8 Date: Mon, 28 Sep 2026 09:20:51 +0100 Subject: [PATCH 07/13] fix: regenerate package-lock.json to match package.json on main --- contracts/stream_contract/src/lib.rs | 9 - package-lock.json | 4636 +++++++++++++------------- 2 files changed, 2388 insertions(+), 2257 deletions(-) diff --git a/contracts/stream_contract/src/lib.rs b/contracts/stream_contract/src/lib.rs index 396774ce..aca347b2 100644 --- a/contracts/stream_contract/src/lib.rs +++ b/contracts/stream_contract/src/lib.rs @@ -47,21 +47,12 @@ use soroban_sdk::{ use errors::StreamError; use events::{ AdminTransferredEvent, AllowanceStreamCreatedEvent, ContractUpgradedEvent, -<<<<<<< HEAD - DisputeRequestedEvent, DisputeResolvedEvent, EmergencyGuardianUpdatedEvent, FeeCollectedEvent, - FeeConfigUpdatedEvent, HybridCliffStreamCreatedEvent, InitializedEvent, - ProtocolPauseStatusEvent, StateMigratedEvent, StepVestingStreamCreatedEvent, - StreamCancelledEvent, StreamClosedEvent, StreamCompletedEvent, StreamCreatedEvent, - StreamPausedEvent, StreamRateModifiedEvent, StreamResumedEvent, StreamToppedUpEvent, - TokensWithdrawnEvent, -======= DisputeRequestedEvent, DisputeResolvedEvent, EmergencyGuardianUpdatedEvent, FeeCollectedEvent, FeeConfigUpdatedEvent, HybridCliffStreamCreatedEvent, InitializedEvent, MilestoneConditionUnlockedEvent, ProtocolPauseStatusEvent, StateMigratedEvent, StepVestingStreamCreatedEvent, StreamCancelledEvent, StreamClosedEvent, StreamCompletedEvent, StreamCreatedEvent, StreamPausedEvent, StreamRateModifiedEvent, StreamResumedEvent, StreamToppedUpEvent, TokensWithdrawnEvent ->>>>>>> 4b71088 (feat: conditional streams, analytics stack, developer portal, copy feedback) }; use storage::{ config_exists, get_contract_version, get_recorded_wasm_hash, load_config, load_stream, diff --git a/package-lock.json b/package-lock.json index f1d9456d..1cd71bbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,11 +15,13 @@ "react-hot-toast": "^2.6.0" }, "devDependencies": { + "@vitest/coverage-v8": "^3.2.7", "husky": "^9.1.7", "lint-staged": "^17.0.7", - "vitest": "^2.1.8" + "vitest": "^3.2.7" }, "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "^4.63.1", "@tailwindcss/oxide-darwin-arm64": "^4.3.1", "@tailwindcss/oxide-darwin-x64": "^4.3.1", "@tailwindcss/oxide-linux-x64-gnu": "^4.3.1", @@ -41,7 +43,7 @@ "@opentelemetry/semantic-conventions": "^1.43.0", "@prisma/adapter-pg": "^7.8.0", "@prisma/client": "^7.8.0", - "@stellar/stellar-sdk": "^15.1.0", + "@stellar/stellar-sdk": "^17.0.1", "cors": "^2.8.6", "dotenv": "^17.4.2", "express": "^5.2.1", @@ -73,6 +75,66 @@ "vitest": "^3.2.4" } }, + "backend/node_modules/@noble/hashes": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz", + "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "backend/node_modules/@stellar/js-xdr": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-5.0.0.tgz", + "integrity": "sha512-HBDNKnxr+ecdaEmbZ0mcKkirOF8tXXEbWSw34P3wT40Tn3g+u+cCH17xAWZymzscq8cojHB8340pR8QNVaD32w==", + "license": "Apache-2.0", + "engines": { + "node": ">=22.0.0", + "pnpm": ">=10.0.0" + } + }, + "backend/node_modules/@stellar/stellar-sdk": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-17.1.0.tgz", + "integrity": "sha512-vf/d9KR2B7MkrJBKW+cN8GPgBbiLFrRfr2pkR41VmqI6is6EfYvzhyGztbuJ3XPmcotkCRCveweAAzw2cjDn1w==", + "license": "Apache-2.0", + "dependencies": { + "@exodus/bytes": "^1.15.1", + "@noble/ed25519": "^3.1.0", + "@noble/hashes": "^2.2.0", + "@stellar/js-xdr": "^5.0.0", + "@types/json-schema": "^7.0.15", + "axios": "1.20.0", + "bignumber.js": "^11.1.4", + "commander": "^14.0.3", + "eventsource": "^4.1.0", + "feaxios": "^0.0.23", + "smol-toml": "^1.6.1", + "uint8array-extras": "^1.5.0" + }, + "bin": { + "stellar-js": "bin/stellar-js" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "backend/node_modules/@stellar/stellar-sdk/node_modules/eventsource": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-4.1.1.tgz", + "integrity": "sha512-D6bTRWh6KahHTK/m4WnjPQyEinNPf9eFLEZSEoj7d6fTibspnAVYfzHvirL7u/aoX5d9YYfIkBVAhmigUELk9w==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, "backend/node_modules/@types/node": { "version": "25.3.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.0.tgz", @@ -83,168 +145,58 @@ "undici-types": "~7.18.0" } }, - "backend/node_modules/@vitest/coverage-v8": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.7.tgz", - "integrity": "sha512-NEGWJS2XNu2PfRLQwOO3CTKj1tTETxNBdk454vDxVBhxJYhPaA/eS0nAI0c+1El1P7a60z8+i+ZrQoGESweGKg==", - "dev": true, + "backend/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.3.0", - "@bcoe/v8-coverage": "^1.0.2", - "ast-v8-to-istanbul": "^0.3.3", - "debug": "^4.4.1", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.6", - "istanbul-reports": "^3.1.7", - "magic-string": "^0.30.17", - "magicast": "^0.3.5", - "std-env": "^3.9.0", - "test-exclude": "^7.0.1", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@vitest/browser": "3.2.7", - "vitest": "3.2.7" + "debug": "4" }, - "peerDependenciesMeta": { - "@vitest/browser": { - "optional": true - } + "engines": { + "node": ">= 6.0.0" } }, - "backend/node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", - "dev": true, + "backend/node_modules/axios": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.20.0.tgz", + "integrity": "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==", "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" } }, - "backend/node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, + "backend/node_modules/bignumber.js": { + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-11.1.5.tgz", + "integrity": "sha512-6WmzCNtUnfKpbozq+hOgWaZMMzORmYBwF1xZScyoIX3QRYWeKTtxxwDOW5tIz7C9BdjkIYHGTcelCLkXg0mndw==", "license": "MIT" }, - "backend/node_modules/vitest": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz", - "integrity": "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==", - "dev": true, + "backend/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "license": "MIT", "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.7", - "@vitest/mocker": "3.2.7", - "@vitest/pretty-format": "^3.2.7", - "@vitest/runner": "3.2.7", - "@vitest/snapshot": "3.2.7", - "@vitest/spy": "3.2.7", - "@vitest/utils": "3.2.7", - "chai": "^5.2.0", - "debug": "^4.4.1", - "expect-type": "^1.2.1", - "magic-string": "^0.30.17", - "pathe": "^2.0.3", - "picomatch": "^4.0.2", - "std-env": "^3.9.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", - "tinypool": "^1.1.1", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.4", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" + "agent-base": "6", + "debug": "4" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.7", - "@vitest/ui": "3.2.7", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/debug": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "backend/node_modules/vitest/node_modules/@vitest/mocker": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.7.tgz", - "integrity": "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "3.2.7", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } + "node": ">= 6" } }, "frontend": { "version": "0.1.0", "dependencies": { "@stellar/freighter-api": "^6.0.1", - "@stellar/stellar-sdk": "^15.1.0", + "@stellar/stellar-sdk": "^17.0.1", "@tanstack/react-query": "^5.101.0", + "@tanstack/react-virtual": "^3.14.9", "lucide-react": "^0.575.0", - "next": "16.2.9", + "next": "16.3.3", "next-themes": "^0.4.6", "react": "19.2.7", "react-dom": "19.2.7", @@ -252,6 +204,7 @@ }, "devDependencies": { "@eslint/eslintrc": "^3.3.5", + "@playwright/test": "^1.55.0", "@tailwindcss/postcss": "^4.3.1", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", @@ -260,1196 +213,1666 @@ "@types/node": "^20", "@types/react": "^19.2.17", "@types/react-dom": "^19", - "@vitejs/plugin-react": "^6.0.2", - "@vitest/coverage-v8": "^2.1.9", + "@vitejs/plugin-react": "^5.2.0", + "@vitest/coverage-v8": "^3.2.7", "eslint": "^9", "eslint-config-next": "^16.2.9", "happy-dom": "^20.10.3", "jsdom": "^27.0.1", + "openapi-typescript": "^7.13.0", "tailwindcss": "^4", "typescript": "^5", - "vitest": "^2.1.9" + "vitest": "^3.2.7" } }, - "frontend/node_modules/@vitejs/plugin-react": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.4.tgz", - "integrity": "sha512-XcCQz0TBpBgljhj0gMuuDj49i6Ytqh5q1osT/Gp5uAVJUCTWxyskk/l1jwYYiu2xcNHHipdMz40EGfM1VdamVg==", - "dev": true, + "frontend/node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", "license": "MIT", - "dependencies": { - "@rolldown/pluginutils": "^1.0.1" - }, + "optional": true, "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", - "babel-plugin-react-compiler": "^1.0.0", - "vite": "^8.0.0" - }, - "peerDependenciesMeta": { - "@rolldown/plugin-babel": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - } + "node": ">=18" } }, - "node_modules/@acemir/cssom": { - "version": "0.9.31", - "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz", - "integrity": "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@adobe/css-tools": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.5.0.tgz", - "integrity": "sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, - "license": "MIT", + "frontend/node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.5.tgz", + "integrity": "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=10" + "node": ">=20.9.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.4" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, + "frontend/node_modules/@img/sharp-darwin-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.5.tgz", + "integrity": "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==", + "cpu": [ + "x64" + ], "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@apidevtools/json-schema-ref-parser": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-14.0.1.tgz", - "integrity": "sha512-Oc96zvmxx1fqoSEdUmfmvvb59/KDOnUoJ7s2t7bISyAn0XEz57LCCw8k2Y4Pf3mwKaZLMciESALORLgfe2frCw==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.15", - "js-yaml": "^4.1.0" + "node": ">=20.9.0" }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.4" + } + }, + "frontend/node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.4.tgz", + "integrity": "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "frontend/node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.4.tgz", + "integrity": "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "frontend/node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.4.tgz", + "integrity": "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "frontend/node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.4.tgz", + "integrity": "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "frontend/node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.4.tgz", + "integrity": "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "frontend/node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.4.tgz", + "integrity": "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "frontend/node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.4.tgz", + "integrity": "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "frontend/node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.4.tgz", + "integrity": "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "frontend/node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.4.tgz", + "integrity": "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "frontend/node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.4.tgz", + "integrity": "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "frontend/node_modules/@img/sharp-linux-arm": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.5.tgz", + "integrity": "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 16" + "node": ">=20.9.0" }, "funding": { - "url": "https://github.com/sponsors/philsturgeon" + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.4" } }, - "node_modules/@apidevtools/openapi-schemas": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", - "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", - "license": "MIT", + "frontend/node_modules/@img/sharp-linux-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.5.tgz", + "integrity": "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.4" } }, - "node_modules/@apidevtools/swagger-methods": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", - "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==", - "license": "MIT" + "frontend/node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.5.tgz", + "integrity": "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.4" + } }, - "node_modules/@apidevtools/swagger-parser": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-12.1.0.tgz", - "integrity": "sha512-e5mJoswsnAX0jG+J09xHFYQXb/bUc5S3pLpMxUuRUA2H8T2kni3yEoyz2R3Dltw5f4A6j6rPNMpWTK+iVDFlng==", - "license": "MIT", - "dependencies": { - "@apidevtools/json-schema-ref-parser": "14.0.1", - "@apidevtools/openapi-schemas": "^2.1.0", - "@apidevtools/swagger-methods": "^3.0.2", - "ajv": "^8.17.1", - "ajv-draft-04": "^1.0.0", - "call-me-maybe": "^1.0.2" + "frontend/node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.5.tgz", + "integrity": "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" }, - "peerDependencies": { - "openapi-types": ">=7" + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.4" } }, - "node_modules/@apidevtools/swagger-parser/node_modules/ajv": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", - "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "frontend/node_modules/@img/sharp-linux-s390x": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.5.tgz", + "integrity": "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.4" } }, - "node_modules/@apidevtools/swagger-parser/node_modules/ajv-draft-04": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", - "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", - "license": "MIT", - "peerDependencies": { - "ajv": "^8.5.0" + "frontend/node_modules/@img/sharp-linux-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.5.tgz", + "integrity": "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.4" } }, - "node_modules/@apidevtools/swagger-parser/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "frontend/node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.5.tgz", + "integrity": "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.4" + } + }, + "frontend/node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.5.tgz", + "integrity": "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.4" + } + }, + "frontend/node_modules/@img/sharp-win32-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.5.tgz", + "integrity": "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "frontend/node_modules/@img/sharp-win32-ia32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.5.tgz", + "integrity": "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "frontend/node_modules/@img/sharp-win32-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.5.tgz", + "integrity": "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "frontend/node_modules/@next/env": { + "version": "16.3.3", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.3.3.tgz", + "integrity": "sha512-U2eYQRwXj+dsqxV79zFqExDdatnNY/ZWc2nsJU1p/OgT7fd3dXwlF6OjYaFQCfMoeTA19PWq+wVmYgimVA+V+g==", "license": "MIT" }, - "node_modules/@asamuzakjp/css-color": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.2.tgz", - "integrity": "sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==", - "dev": true, + "frontend/node_modules/@next/swc-darwin-arm64": { + "version": "16.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.3.3.tgz", + "integrity": "sha512-8Hiv32QJPwdV6KYJ8meR9SBA061tQqnIKTJDocvOXlEQqib0xMFpzArosuffFUUc0sslbh7QQ8a3Yey1QV8EIw==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "@csstools/css-calc": "^3.0.0", - "@csstools/css-color-parser": "^4.0.1", - "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0", - "lru-cache": "^11.2.5" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { - "version": "11.5.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", - "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", - "dev": true, - "license": "BlueOak-1.0.0", + "frontend/node_modules/@next/swc-darwin-x64": { + "version": "16.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.3.3.tgz", + "integrity": "sha512-A1lgKgwVchRYmSe467zdwhxT9040dd8lH+o65sL5Jet8fjB4kegw/rDyPIpYVRb6jAqwXFOJpjIXJLxQKLiE3A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "20 || >=22" + "node": ">= 10" } }, - "node_modules/@asamuzakjp/dom-selector": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz", - "integrity": "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==", - "dev": true, + "frontend/node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.3.3.tgz", + "integrity": "sha512-bf0FIssMFueU2dm7vQEWWxk0c8UjKTdW0yzuh0sQsD8pf1+KCLDdaqhYZNMYGmXwEOiHAUzgBKudovIlcvvBjg==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "@asamuzakjp/nwsapi": "^2.3.9", - "bidi-js": "^1.0.3", - "css-tree": "^3.1.0", - "is-potential-custom-element-name": "^1.0.1", - "lru-cache": "^11.2.6" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { - "version": "11.5.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", - "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", - "dev": true, - "license": "BlueOak-1.0.0", + "frontend/node_modules/@next/swc-linux-arm64-musl": { + "version": "16.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.3.3.tgz", + "integrity": "sha512-W7viwCk9JY/cAkdz/A273rd5bb3RgT/IHwR7Upv90tunjBWNtAAhGhoecHh+teRNRSinuAFmE+l7fwZ4YKkrXg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "20 || >=22" + "node": ">= 10" } }, - "node_modules/@asamuzakjp/nwsapi": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", - "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", - "dev": true, - "license": "MIT" + "frontend/node_modules/@next/swc-linux-x64-gnu": { + "version": "16.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.3.3.tgz", + "integrity": "sha512-0W46zw1N3ODpI6n0GeivHvvob1pooozgZVqy65k0mh4/7vr+FbY9+WpHzNVXjHipJf/A3FDheBG19H1s5A25rA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", - "dev": true, + "frontend/node_modules/@next/swc-linux-x64-musl": { + "version": "16.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.3.3.tgz", + "integrity": "sha512-H4mBso8ZTMBPtdT0PN0pBx2ayTvQuTuvS6qT13d77yVFJXAPCxkyIhLTmdMaGTJs0krQYI/qpzdHijCeihXhbg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "frontend/node_modules/@next/swc-win32-arm64-msvc": { + "version": "16.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.3.3.tgz", + "integrity": "sha512-cTMUJpcEGmeywofCUfhR+rSsoE33+rVPnPEYNTNdLNlsOeEg/vktOsKUSTb28vUGqD2jkm4Zaskcwn7OCI6FQg==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6.9.0" + "node": ">= 10" } }, - "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", - "dev": true, + "frontend/node_modules/@next/swc-win32-x64-msvc": { + "version": "16.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.3.3.tgz", + "integrity": "sha512-2VR4cTBzHXaBjnGsuH6GyJjENzQOmHeAh11uY1iUhjm3j5dEUrVJuUj+VL78jaGi/Dik8xS76zEj18BsFhlVZQ==", + "cpu": [ + "x64" + ], "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6.9.0" + "node": ">= 10" } }, - "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", - "dev": true, + "frontend/node_modules/@noble/hashes": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz", + "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==", "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, "engines": { - "node": ">=6.9.0" + "node": ">= 20.19.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, + "frontend/node_modules/@stellar/js-xdr": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-5.0.0.tgz", + "integrity": "sha512-HBDNKnxr+ecdaEmbZ0mcKkirOF8tXXEbWSw34P3wT40Tn3g+u+cCH17xAWZymzscq8cojHB8340pR8QNVaD32w==", + "license": "Apache-2.0", "engines": { - "node": ">=6.9.0" + "node": ">=22.0.0", + "pnpm": ">=10.0.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "dev": true, - "license": "MIT", + "frontend/node_modules/@stellar/stellar-sdk": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-17.1.0.tgz", + "integrity": "sha512-vf/d9KR2B7MkrJBKW+cN8GPgBbiLFrRfr2pkR41VmqI6is6EfYvzhyGztbuJ3XPmcotkCRCveweAAzw2cjDn1w==", + "license": "Apache-2.0", "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" + "@exodus/bytes": "^1.15.1", + "@noble/ed25519": "^3.1.0", + "@noble/hashes": "^2.2.0", + "@stellar/js-xdr": "^5.0.0", + "@types/json-schema": "^7.0.15", + "axios": "1.20.0", + "bignumber.js": "^11.1.4", + "commander": "^14.0.3", + "eventsource": "^4.1.0", + "feaxios": "^0.0.23", + "smol-toml": "^1.6.1", + "uint8array-extras": "^1.5.0" + }, + "bin": { + "stellar-js": "bin/stellar-js" }, "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" + "node": ">=22.12.0" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "dev": true, - "license": "MIT", + "frontend/node_modules/@swc/helpers": { + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", + "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", + "license": "Apache-2.0", "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" + "tslib": "^2.8.0" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "frontend/node_modules/@vitejs/plugin-react": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.4.tgz", + "integrity": "sha512-XcCQz0TBpBgljhj0gMuuDj49i6Ytqh5q1osT/Gp5uAVJUCTWxyskk/l1jwYYiu2xcNHHipdMz40EGfM1VdamVg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" + "@rolldown/pluginutils": "^1.0.1" }, "engines": { - "node": ">=6.9.0" + "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, + "frontend/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "license": "MIT", + "dependencies": { + "debug": "4" + }, "engines": { - "node": ">=6.9.0" + "node": ">= 6.0.0" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, + "frontend/node_modules/axios": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.20.0.tgz", + "integrity": "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==", "license": "MIT", - "engines": { - "node": ">=6.9.0" + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, + "frontend/node_modules/bignumber.js": { + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-11.1.5.tgz", + "integrity": "sha512-6WmzCNtUnfKpbozq+hOgWaZMMzORmYBwF1xZScyoIX3QRYWeKTtxxwDOW5tIz7C9BdjkIYHGTcelCLkXg0mndw==", + "license": "MIT" + }, + "frontend/node_modules/eventsource": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-4.1.1.tgz", + "integrity": "sha512-D6bTRWh6KahHTK/m4WnjPQyEinNPf9eFLEZSEoj7d6fTibspnAVYfzHvirL7u/aoX5d9YYfIkBVAhmigUELk9w==", "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, "engines": { - "node": ">=6.9.0" + "node": ">=20.0.0" } }, - "node_modules/@babel/helpers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", - "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", - "dev": true, + "frontend/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">=6.9.0" + "node": ">= 6" } }, - "node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", - "dev": true, + "frontend/node_modules/next": { + "version": "16.3.3", + "resolved": "https://registry.npmjs.org/next/-/next-16.3.3.tgz", + "integrity": "sha512-tuRTx1nQ/yVw83cwJBo9F+njGUgMn3UHQycreWHB8XsStvvAh1AthbI8/4IpKnFaF58F+iSiHejYOlMQ/eq83g==", "license": "MIT", "dependencies": { - "@babel/types": "^7.29.0" + "@next/env": "16.3.3", + "@swc/helpers": "0.5.23", + "baseline-browser-mapping": "^2.9.19", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.5.23", + "styled-jsx": "5.1.6" }, "bin": { - "parser": "bin/babel-parser.js" + "next": "dist/bin/next" }, "engines": { - "node": ">=6.0.0" + "node": ">=20.9.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "16.3.3", + "@next/swc-darwin-x64": "16.3.3", + "@next/swc-linux-arm64-gnu": "16.3.3", + "@next/swc-linux-arm64-musl": "16.3.3", + "@next/swc-linux-x64-gnu": "16.3.3", + "@next/swc-linux-x64-musl": "16.3.3", + "@next/swc-win32-arm64-msvc": "16.3.3", + "@next/swc-win32-x64-msvc": "16.3.3", + "sharp": "^0.35.3" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "frontend/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "frontend/node_modules/sharp": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.5.tgz", + "integrity": "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.5", + "@img/sharp-darwin-x64": "0.35.5", + "@img/sharp-freebsd-wasm32": "0.35.5", + "@img/sharp-libvips-darwin-arm64": "1.3.4", + "@img/sharp-libvips-darwin-x64": "1.3.4", + "@img/sharp-libvips-linux-arm": "1.3.4", + "@img/sharp-libvips-linux-arm64": "1.3.4", + "@img/sharp-libvips-linux-ppc64": "1.3.4", + "@img/sharp-libvips-linux-riscv64": "1.3.4", + "@img/sharp-libvips-linux-s390x": "1.3.4", + "@img/sharp-libvips-linux-x64": "1.3.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.4", + "@img/sharp-libvips-linuxmusl-x64": "1.3.4", + "@img/sharp-linux-arm": "0.35.5", + "@img/sharp-linux-arm64": "0.35.5", + "@img/sharp-linux-ppc64": "0.35.5", + "@img/sharp-linux-riscv64": "0.35.5", + "@img/sharp-linux-s390x": "0.35.5", + "@img/sharp-linux-x64": "0.35.5", + "@img/sharp-linuxmusl-arm64": "0.35.5", + "@img/sharp-linuxmusl-x64": "0.35.5", + "@img/sharp-webcontainers-wasm32": "0.35.5", + "@img/sharp-win32-arm64": "0.35.5", + "@img/sharp-win32-ia32": "0.35.5", + "@img/sharp-win32-x64": "0.35.5" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@babel/runtime": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", - "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "node_modules/@acemir/cssom": { + "version": "0.9.31", + "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz", + "integrity": "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@adobe/css-tools": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.5.0.tgz", + "integrity": "sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", "dev": true, "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { - "node": ">=6.9.0" + "node": ">=6.0.0" } }, - "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "dev": true, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-14.0.1.tgz", + "integrity": "sha512-Oc96zvmxx1fqoSEdUmfmvvb59/KDOnUoJ7s2t7bISyAn0XEz57LCCw8k2Y4Pf3mwKaZLMciESALORLgfe2frCw==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" + "@types/json-schema": "^7.0.15", + "js-yaml": "^4.1.0" }, "engines": { - "node": ">=6.9.0" + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" } }, - "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "dev": true, + "node_modules/@apidevtools/openapi-schemas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", + "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, "engines": { - "node": ">=6.9.0" + "node": ">=10" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", - "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", - "dev": true, + "node_modules/@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==", + "license": "MIT" + }, + "node_modules/@apidevtools/swagger-parser": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-12.1.0.tgz", + "integrity": "sha512-e5mJoswsnAX0jG+J09xHFYQXb/bUc5S3pLpMxUuRUA2H8T2kni3yEoyz2R3Dltw5f4A6j6rPNMpWTK+iVDFlng==", "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "@apidevtools/json-schema-ref-parser": "14.0.1", + "@apidevtools/openapi-schemas": "^2.1.0", + "@apidevtools/swagger-methods": "^3.0.2", + "ajv": "^8.17.1", + "ajv-draft-04": "^1.0.0", + "call-me-maybe": "^1.0.2" + }, + "peerDependencies": { + "openapi-types": ">=7" } }, - "node_modules/@chevrotain/cst-dts-gen": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz", - "integrity": "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@apidevtools/swagger-parser/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", "dependencies": { - "@chevrotain/gast": "10.5.0", - "@chevrotain/types": "10.5.0", - "lodash": "4.17.21" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@chevrotain/gast": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.5.0.tgz", - "integrity": "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@chevrotain/types": "10.5.0", - "lodash": "4.17.21" + "node_modules/@apidevtools/swagger-parser/node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/@chevrotain/types": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.5.0.tgz", - "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==", - "dev": true, - "license": "Apache-2.0" + "node_modules/@apidevtools/swagger-parser/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" }, - "node_modules/@chevrotain/utils": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.5.0.tgz", - "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==", + "node_modules/@asamuzakjp/css-color": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.2.tgz", + "integrity": "sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==", "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", "license": "MIT", - "engines": { - "node": ">=0.1.90" + "dependencies": { + "@csstools/css-calc": "^3.0.0", + "@csstools/css-color-parser": "^4.0.1", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0", + "lru-cache": "^11.2.5" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, + "license": "BlueOak-1.0.0", "engines": { - "node": ">=12" + "node": "20 || >=22" } }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "node_modules/@asamuzakjp/dom-selector": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz", + "integrity": "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.6" } }, - "node_modules/@csstools/color-helpers": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.0.tgz", - "integrity": "sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==", + "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=20.19.0" + "node": "20 || >=22" } }, - "node_modules/@csstools/css-calc": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.3.0.tgz", - "integrity": "sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0" - } + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" }, - "node_modules/@csstools/css-color-parser": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.10.tgz", - "integrity": "sha512-UZhQLIUyJaaMepqehrCODwCg2KW25vFvLWBmqYFaPclYvvxzj/sG8LBOhBFCp11i9uE7t1EyS+RAoV9tztPFyw==", + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], "license": "MIT", "dependencies": { - "@csstools/color-helpers": "^6.1.0", - "@csstools/css-calc": "^3.3.0" + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0" + "node": ">=6.9.0" } }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", - "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], "license": "MIT", "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^4.0.0" + "node": ">=6.9.0" } }, - "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.7.tgz", - "integrity": "sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==", + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "peerDependencies": { - "css-tree": "^3.2.1" + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" }, - "peerDependenciesMeta": { - "css-tree": { - "optional": true - } + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@csstools/css-tokenizer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", - "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, "engines": { - "node": ">=20.19.0" + "node": ">=6.9.0" } }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", - "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, "license": "MIT", "dependencies": { - "@so-ric/colorspace": "^1.1.6", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "node_modules/@electric-sql/pglite": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.15.tgz", - "integrity": "sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@electric-sql/pglite-socket": { - "version": "0.0.20", - "resolved": "https://registry.npmjs.org/@electric-sql/pglite-socket/-/pglite-socket-0.0.20.tgz", - "integrity": "sha512-J5nLGsicnD9wJHnno9r+DGxfcZWh+YJMCe0q/aCgtG6XOm9Z7fKeite8IZSNXgZeGltSigM9U/vAWZQWdgcSFg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "pglite-server": "dist/scripts/server.js" + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, - "peerDependencies": { - "@electric-sql/pglite": "0.3.15" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@electric-sql/pglite-tools": { - "version": "0.2.20", - "resolved": "https://registry.npmjs.org/@electric-sql/pglite-tools/-/pglite-tools-0.2.20.tgz", - "integrity": "sha512-BK50ZnYa3IG7ztXhtgYf0Q7zijV32Iw1cYS8C+ThdQlwx12V5VZ9KRJ42y82Hyb4PkTxZQklVQA9JHyUlex33A==", + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "dev": true, - "license": "Apache-2.0", - "peerDependencies": { - "@electric-sql/pglite": "0.3.15" + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@emnapi/core": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", - "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@emnapi/runtime": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", - "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "tslib": "^2.4.0" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", - "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", - "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", - "cpu": [ - "ppc64" - ], + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "aix" - ], "engines": { - "node": ">=18" + "node": ">=6.9.0" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", - "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", - "cpu": [ - "arm" - ], + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], "engines": { - "node": ">=18" + "node": ">=6.9.0" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", - "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", - "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", - "cpu": [ - "x64" - ], + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, "engines": { - "node": ">=18" + "node": ">=6.0.0" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", - "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": ">=18" + "node": ">=6.9.0" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", - "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", - "cpu": [ - "x64" - ], + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", - "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", - "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", - "cpu": [ - "x64" - ], + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", - "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", - "cpu": [ - "arm" - ], + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { "node": ">=18" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", - "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz", + "integrity": "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.5.0.tgz", + "integrity": "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/types": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.5.0.tgz", + "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/utils": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.5.0.tgz", + "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": ">=0.1.90" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", - "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", - "cpu": [ - "ia32" - ], + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, "engines": { - "node": ">=18" + "node": ">=12" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", - "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", - "cpu": [ - "loong64" - ], + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.0.tgz", + "integrity": "sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } ], + "license": "MIT-0", "engines": { - "node": ">=18" + "node": ">=20.19.0" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", - "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", - "cpu": [ - "mips64el" - ], + "node_modules/@csstools/css-calc": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.3.0.tgz", + "integrity": "sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } ], + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", - "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", - "cpu": [ - "ppc64" - ], + "node_modules/@csstools/css-color-parser": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.10.tgz", + "integrity": "sha512-UZhQLIUyJaaMepqehrCODwCg2KW25vFvLWBmqYFaPclYvvxzj/sG8LBOhBFCp11i9uE7t1EyS+RAoV9tztPFyw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.1.0", + "@csstools/css-calc": "^3.3.0" + }, "engines": { - "node": ">=18" + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", - "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", - "cpu": [ - "riscv64" - ], + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } ], + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", - "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", - "cpu": [ - "s390x" - ], + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.7.tgz", + "integrity": "sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } ], - "engines": { - "node": ">=18" + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", - "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", - "cpu": [ - "x64" - ], + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } ], + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=20.19.0" } }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", - "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@dabh/diagnostics": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@so-ric/colorspace": "^1.1.6", + "enabled": "2.0.x", + "kuler": "^2.0.0" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", - "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", - "cpu": [ - "x64" - ], + "node_modules/@electric-sql/pglite": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.15.tgz", + "integrity": "sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@electric-sql/pglite-socket": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite-socket/-/pglite-socket-0.0.20.tgz", + "integrity": "sha512-J5nLGsicnD9wJHnno9r+DGxfcZWh+YJMCe0q/aCgtG6XOm9Z7fKeite8IZSNXgZeGltSigM9U/vAWZQWdgcSFg==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "pglite-server": "dist/scripts/server.js" + }, + "peerDependencies": { + "@electric-sql/pglite": "0.3.15" + } + }, + "node_modules/@electric-sql/pglite-tools": { + "version": "0.2.20", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite-tools/-/pglite-tools-0.2.20.tgz", + "integrity": "sha512-BK50ZnYa3IG7ztXhtgYf0Q7zijV32Iw1cYS8C+ThdQlwx12V5VZ9KRJ42y82Hyb4PkTxZQklVQA9JHyUlex33A==", + "devOptional": true, + "license": "Apache-2.0", + "peerDependencies": { + "@electric-sql/pglite": "0.3.15" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" } }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", - "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", "license": "MIT", "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" } }, - "node_modules/@esbuild/openbsd-x64": { + "node_modules/@esbuild/aix-ppc64": { "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", - "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", "cpu": [ - "x64" + "ppc64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "openbsd" + "aix" ], "engines": { "node": ">=18" } }, - "node_modules/@esbuild/openharmony-arm64": { + "node_modules/@esbuild/android-arm": { "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", - "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", "cpu": [ - "arm64" + "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "openharmony" + "android" ], "engines": { "node": ">=18" } }, - "node_modules/@esbuild/sunos-x64": { + "node_modules/@esbuild/android-arm64": { "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", - "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", "cpu": [ - "x64" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "sunos" + "android" ], "engines": { "node": ">=18" } }, - "node_modules/@esbuild/win32-arm64": { + "node_modules/@esbuild/android-x64": { "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", - "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", "cpu": [ - "arm64" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "win32" + "android" ], "engines": { "node": ">=18" } }, - "node_modules/@esbuild/win32-ia32": { + "node_modules/@esbuild/darwin-arm64": { "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", - "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", "cpu": [ - "ia32" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "win32" + "darwin" ], "engines": { "node": ">=18" } }, - "node_modules/@esbuild/win32-x64": { + "node_modules/@esbuild/darwin-x64": { "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", - "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", "cpu": [ "x64" ], @@ -1457,731 +1880,674 @@ "license": "MIT", "optional": true, "os": [ - "win32" + "darwin" ], "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", - "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.14.0", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.3.0", - "minimatch": "^3.1.5", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "9.39.3", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.3.tgz", - "integrity": "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@exodus/bytes": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", - "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@noble/hashes": "^1.8.0 || ^2.0.0" - }, - "peerDependenciesMeta": { - "@noble/hashes": { - "optional": true - } - } - }, - "node_modules/@grpc/grpc-js": { - "version": "1.14.5", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.5.tgz", - "integrity": "sha512-7VZM+SVdEcUUqSQeNI3zM8Qs/BhQKZndPo2h5VkYkAM8Iz0wJIa8mKV5ekQGqG8UUsnkQ0NMxIxwkIHYvj0qOw==", - "license": "Apache-2.0", - "dependencies": { - "@grpc/proto-loader": "^0.8.0", - "@js-sdsl/ordered-map": "^4.4.2" - }, - "engines": { - "node": ">=12.10.0" - } - }, - "node_modules/@grpc/proto-loader": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.1.tgz", - "integrity": "sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==", - "license": "Apache-2.0", - "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.5.5", - "yargs": "^17.7.2" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" + "node": ">=18" } }, - "node_modules/@hono/node-server": { - "version": "1.19.9", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", - "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=18.14.1" - }, - "peerDependencies": { - "hono": "^4" + "node": ">=18" } }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=18.18.0" + "node": ">=18" } }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.18.0" + "node": ">=18" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=18" } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=18" } }, - "node_modules/@img/colour": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", - "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, "license": "MIT", "optional": true, + "os": [ + "linux" + ], "engines": { "node": ">=18" } }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", "cpu": [ - "arm64" + "mips64el" ], - "license": "Apache-2.0", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "node": ">=18" } }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", "cpu": [ - "x64" + "ppc64" ], - "license": "Apache-2.0", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "node": ">=18" } }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", "cpu": [ - "arm64" + "riscv64" ], - "license": "LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ], - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=18" } }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", "cpu": [ - "x64" + "s390x" ], - "license": "LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ], - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=18" } }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", "cpu": [ - "arm" + "x64" ], - "license": "LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=18" } }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", "cpu": [ "arm64" ], - "license": "LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "linux" + "netbsd" ], - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=18" } }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", "cpu": [ - "ppc64" + "x64" ], - "license": "LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "linux" + "netbsd" ], - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=18" } }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", "cpu": [ - "riscv64" + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" ], - "license": "LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "linux" + "openbsd" ], - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=18" } }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", "cpu": [ - "s390x" + "arm64" ], - "license": "LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "linux" + "openharmony" ], - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=18" } }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", "cpu": [ "x64" ], - "license": "LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "linux" + "sunos" ], - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=18" } }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", "cpu": [ "arm64" ], - "license": "LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "linux" + "win32" ], - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=18" } }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", "cpu": [ - "x64" + "ia32" ], - "license": "LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "linux" + "win32" ], - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=18" } }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", "cpu": [ - "arm" + "x64" ], - "license": "Apache-2.0", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "linux" + "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/libvips" + "url": "https://opencollective.com/eslint" }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/libvips" + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", + "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.3.0", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.3", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.3.tgz", + "integrity": "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", + "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.14.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.5.tgz", + "integrity": "sha512-7VZM+SVdEcUUqSQeNI3zM8Qs/BhQKZndPo2h5VkYkAM8Iz0wJIa8mKV5ekQGqG8UUsnkQ0NMxIxwkIHYvj0qOw==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" } }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], + "node_modules/@grpc/proto-loader": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.1.tgz", + "integrity": "sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==", "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.5", + "yargs": "^17.7.2" }, - "funding": { - "url": "https://opencollective.com/libvips" + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "engines": { + "node": ">=6" } }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@hono/node-server": { + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", + "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", + "devOptional": true, + "license": "MIT", "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" + "node": ">=18.14.1" }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "peerDependencies": { + "hono": "^4" } }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", - "cpu": [ - "s390x" - ], + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "engines": { + "node": ">=18.18.0" } }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", - "cpu": [ - "x64" - ], + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=12.22" }, "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", - "cpu": [ - "arm64" - ], + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=18.18" }, "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", - "cpu": [ - "x64" - ], + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.5.tgz", + "integrity": "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==", "license": "Apache-2.0", "optional": true, "os": [ - "linux" + "freebsd" ], + "dependencies": { + "@img/sharp-wasm32": "0.35.5" + }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" } }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], + "node_modules/@img/sharp-freebsd-wasm32/node_modules/@img/sharp-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.5.tgz", + "integrity": "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==", "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.7.0" + "@emnapi/runtime": "^1.11.3" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.5.tgz", + "integrity": "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==", "cpu": [ - "arm64" + "wasm32" ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", + "license": "Apache-2.0", "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "dependencies": { + "@img/sharp-wasm32": "0.35.5" }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", + "node_modules/@img/sharp-webcontainers-wasm32/node_modules/@img/sharp-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.5.tgz", + "integrity": "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "@emnapi/runtime": "^1.11.3" + }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" @@ -2259,201 +2625,61 @@ "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@js-sdsl/ordered-map": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", - "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "node_modules/@mrleebo/prisma-ast": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/@mrleebo/prisma-ast/-/prisma-ast-0.13.1.tgz", - "integrity": "sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chevrotain": "^10.5.0", - "lilconfig": "^2.1.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, - "node_modules/@next/env": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.9.tgz", - "integrity": "sha512-ki5VxxXfzD/9TDe13wyeTKIjQTAwBVpnr8KhRDUr8ltMUq1/NBpWNT5tiPoxiGl+PHM4X2ahSOiPk6iAimIzPg==", - "license": "MIT" - }, - "node_modules/@next/eslint-plugin-next": { - "version": "16.2.12", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.12.tgz", - "integrity": "sha512-uF2z/qAK2q7B5/6CpnFcBRX6jOq5iCO+Uqh1UkJhXljX1JwLarLYhhoJadO6dPb6moTprOKewMXheBcbIoSbug==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-glob": "3.3.1" - } - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.9.tgz", - "integrity": "sha512-HkfxNYUCmcct0Xsqib5KxqMSHV4AHJq857BNRchyBDs4YS19aHzVfn1kDuBYKqLLQBjXgnkIsjV2Kd4d2wzYhw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.9.tgz", - "integrity": "sha512-7IAtK4MeybpqRV9GRABWEhJ62mOS+rzWOzOTFie4cSEtm12xsoOMJRcECoZx3FHPzFAqN/IJtHqWAFOLfl152w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.9.tgz", - "integrity": "sha512-hBD75iWpUtkL9SmQmcRhmLomn9jgkPzCEkbOcLgHymPEKzv+6ONy13RRiIEz/iEObjkS2Jlb5gYS2XGoS3X4rw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.9.tgz", - "integrity": "sha512-qZTI3pf9SGc/obr8NkQAekBxmp1QK+kVm+VAf3BALLfFAj+1kUhkTxmrWpVos9R/UYIA8AWX2p6cGI5WdwzVUA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.9.tgz", - "integrity": "sha512-xm0HfRNX+UkH4R3c18ynswjj5o5uEj/7iI9p9omdtTSIsRCzQqkGMA+10nzJ4EHnYC3as65IMhbbl5fWRUWHYg==", - "cpu": [ - "x64" - ], + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" } }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.9.tgz", - "integrity": "sha512-QumimHkGEG6vM3PfEDWKyKen03NcqLOkeKB1EfcPe7VxzmEiCa4jNnMyBn/US5zcd/VE1CI+O8Ovb3lfjVHfGw==", - "cpu": [ - "x64" - ], + "node_modules/@mrleebo/prisma-ast": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/@mrleebo/prisma-ast/-/prisma-ast-0.13.1.tgz", + "integrity": "sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw==", + "devOptional": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "chevrotain": "^10.5.0", + "lilconfig": "^2.1.0" + }, "engines": { - "node": ">= 10" + "node": ">=16" } }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.9.tgz", - "integrity": "sha512-hzQpKZvw8rAwI6A2uQh6SacCSvNAXaIkPNsWwzqqfRiIMiXMfH936skDhz1OO6KpvdKkJrgHHtqQOq5PIXOvdQ==", - "cpu": [ - "arm64" - ], + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, "license": "MIT", "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" } }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.9.tgz", - "integrity": "sha512-qr2VL3Ce5QrwgO2yh1ujSBawrimjVKX8FGF/cOynmdYKJY0BdHpGVNIRK1tqONB10Vkm25Ub1BD2bkjWs4+96w==", - "cpu": [ - "x64" - ], + "node_modules/@next/eslint-plugin-next": { + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.12.tgz", + "integrity": "sha512-uF2z/qAK2q7B5/6CpnFcBRX6jOq5iCO+Uqh1UkJhXljX1JwLarLYhhoJadO6dPb6moTprOKewMXheBcbIoSbug==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" + "dependencies": { + "fast-glob": "3.3.1" } }, - "node_modules/@noble/curves": { - "version": "1.9.7", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", - "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "node_modules/@noble/ed25519": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-3.2.0.tgz", + "integrity": "sha512-criDgRlnUA09hchYrTy/JUWPIEap5rZxQe6wDWzRx51oWWpDRcUpuNzlgPxDJaOK6AsW9c0wKcj3rKRv6t+bPQ==", "license": "MIT", - "dependencies": { - "@noble/hashes": "1.8.0" - }, - "engines": { - "node": "^14.21.3 || >=16" - }, "funding": { "url": "https://paulmillr.com/funding/" } @@ -2462,6 +2688,7 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "devOptional": true, "license": "MIT", "engines": { "node": "^14.21.3 || >=16" @@ -3920,6 +4147,22 @@ "node": ">=14" } }, + "node_modules/@playwright/test": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.63.0.tgz", + "integrity": "sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.63.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/@prisma/adapter-pg": { "version": "7.9.1", "resolved": "https://registry.npmjs.org/@prisma/adapter-pg/-/adapter-pg-7.9.1.tgz", @@ -3966,7 +4209,7 @@ "version": "7.4.1", "resolved": "https://registry.npmjs.org/@prisma/config/-/config-7.4.1.tgz", "integrity": "sha512-vteSXm8N46bo3FW9MhPGVHAj+KRgrR6TWtlSk6GqToCKjTnOexXdPZyiDyEsfVW38YhqEmVl6w/6iHN8uYVJcw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "c12": "3.1.0", @@ -3979,14 +4222,14 @@ "version": "7.4.1", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.4.1.tgz", "integrity": "sha512-qEtzO8oLouRv18JDQUC3G3Gnv+fGVscHZm/x1DBB/WT+kOvPDQLM2woX6IGgWnSMYYlrxjuALshT7G/blvY0bQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/dev": { "version": "0.20.0", "resolved": "https://registry.npmjs.org/@prisma/dev/-/dev-0.20.0.tgz", "integrity": "sha512-ovlBYwWor0OzG+yH4J3Ot+AneD818BttLA+Ii7wjbcLHUrnC4tbUPVGyNd3c/+71KETPKZfjhkTSpdS15dmXNQ==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "@electric-sql/pglite": "0.3.15", @@ -4027,7 +4270,7 @@ "version": "7.4.1", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-7.4.1.tgz", "integrity": "sha512-BZEBdHvNJx5PzIG37EI/Zi5UUI5hGWjkYsQmKa7OIK6evAvebOTwutjS/VRI6cA6grmA52eLZR+oekGRMqkKxQ==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -4041,14 +4284,14 @@ "version": "7.5.0-4.55ae170b1ced7fc6ed07a15f110549408c501bb3", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.5.0-4.55ae170b1ced7fc6ed07a15f110549408c501bb3.tgz", "integrity": "sha512-fUxVd1TjOW8K4XsZ8dAm88sDW5Ry7AxWDfsYEWwScS6Fjo3caKC6hgNumUfsmsy0Il9LjDn5X0PpVXNt3iwayw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines/node_modules/@prisma/get-platform": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.4.1.tgz", "integrity": "sha512-kN4tmkQzlgm/KtE+jTNSYjsDxxe/5i6GApPI32BN9T0tlgsgSBtDJbjGBICttkAIjsh73dXf8raPKxO/2n2UUg==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "7.4.1" @@ -4058,7 +4301,7 @@ "version": "7.4.1", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-7.4.1.tgz", "integrity": "sha512-Z9kbuxX2bvEsyeS3LZEiEnxG0lVtZbpYgaAnPj69N+A9f2De8Lta0EoFtld9zhfERVPIQWhSWUc8himky3qYdA==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "7.4.1", @@ -4070,7 +4313,7 @@ "version": "7.4.1", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.4.1.tgz", "integrity": "sha512-kN4tmkQzlgm/KtE+jTNSYjsDxxe/5i6GApPI32BN9T0tlgsgSBtDJbjGBICttkAIjsh73dXf8raPKxO/2n2UUg==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "7.4.1" @@ -4080,7 +4323,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.2.0.tgz", "integrity": "sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "7.2.0" @@ -4090,21 +4333,21 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.2.0.tgz", "integrity": "sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/query-plan-executor": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@prisma/query-plan-executor/-/query-plan-executor-7.2.0.tgz", "integrity": "sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/studio-core": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/@prisma/studio-core/-/studio-core-0.13.1.tgz", "integrity": "sha512-agdqaPEePRHcQ7CexEfkX1RvSH9uWDb6pXrZnhCRykhDFAV0/0P3d07WtfiY8hZWb7oRU4v+NkT4cGFHkQJIPg==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "peerDependencies": { "@types/react": "^18.0.0 || ^19.0.0", @@ -4169,6 +4412,82 @@ "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==", "license": "BSD-3-Clause" }, + "node_modules/@redocly/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js-replace": "^1.0.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@redocly/ajv/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@redocly/config": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.22.0.tgz", + "integrity": "sha512-gAy93Ddo01Z3bHuVdPWfCwzgfaYgMdaZPcfL7JZ7hWJoK9V0lXDbigTWkhiPFAaLWzbOJ+kbUQG1+XwIm0KRGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@redocly/openapi-core": { + "version": "1.34.20", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.20.tgz", + "integrity": "sha512-ypeBZ/6BKXR9+7/TtbKhbl4UgD7raHhPS12oknlKno2A8+lnFkxIwiE/Aklu6L2cd/ioH+fCWuMxi9/p3EyAPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@redocly/ajv": "8.11.2", + "@redocly/config": "0.22.0", + "colorette": "1.4.0", + "https-proxy-agent": "7.0.6", + "js-levenshtein": "1.1.6", + "js-yaml": "4.3.2", + "minimatch": "5.1.9", + "pluralize": "8.0.0", + "yaml-ast-parser": "0.0.43" + }, + "engines": { + "node": ">=18.17.0", + "npm": ">=9.5.0" + } + }, + "node_modules/@redocly/openapi-core/node_modules/brace-expansion": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@redocly/openapi-core/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", @@ -4415,13 +4734,12 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.62.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.3.tgz", - "integrity": "sha512-V4KtWtQfAFMU7+9/A/VDps/VI8CHd3cYz0L8sgJzz8qK7eY7wI4ruFD82UYIYvW9Z4DtlTfhQcsl4XyPHW5uSg==", + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.5.tgz", + "integrity": "sha512-3W9bTFcQNJn71cSJVM9RKIiZOy8DO/XLDii8Uv/Pm6WKqDRj7JV3ZfuXIEfyuy5LXpIzAbB/1M4Ukp9GKNa7nA==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4554,7 +4872,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@stellar/freighter-api": { @@ -4579,66 +4897,6 @@ "node": ">=10" } }, - "node_modules/@stellar/js-xdr": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-4.0.0.tgz", - "integrity": "sha512-+NmNa7Tk5BI5XFdy/6xGTqAN4J9a9KgCrCGhj2uEUTCBhLkch0M+QbKzNH8zEnejWe0p8w+0q5hUVX6L3OzoVA==", - "license": "Apache-2.0", - "engines": { - "node": ">=20.0.0", - "pnpm": ">=9.0.0" - } - }, - "node_modules/@stellar/stellar-base": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-15.0.0.tgz", - "integrity": "sha512-XQhxUr9BYiEcFcgc4oWcCMR9QJCny/GmmGsuwPKf/ieIcOeb5149KLHYx9mJCA0ea8QbucR2/GzV58QbXOTxQA==", - "deprecated": "This package is now rolled into @stellar/stellar-sdk. Please use @stellar/stellar-sdk to continue receiving updates and support.", - "license": "Apache-2.0", - "dependencies": { - "@noble/curves": "^1.9.7", - "@stellar/js-xdr": "^4.0.0", - "base32.js": "^0.1.0", - "bignumber.js": "^9.3.1", - "buffer": "^6.0.3", - "sha.js": "^2.4.12" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@stellar/stellar-sdk": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-15.1.0.tgz", - "integrity": "sha512-GsJUcWx2yboVzYdhTe/LHS3V1wVLSHkUkglC5bBoYWGJt31vzIhbSGno60NP9CdCTNkLJdnrsLJ63oA58Zvh5A==", - "license": "Apache-2.0", - "dependencies": { - "@stellar/stellar-base": "^15.0.0", - "axios": "1.15.0", - "bignumber.js": "^9.3.1", - "commander": "^14.0.3", - "eventsource": "^2.0.2", - "feaxios": "^0.0.23", - "randombytes": "^2.1.0", - "toml": "^3.0.0", - "urijs": "^1.19.11" - }, - "bin": { - "stellar-js": "bin/stellar-js" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - } - }, "node_modules/@tailwindcss/node": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", @@ -5218,6 +5476,33 @@ "react": "^18 || ^19" } }, + "node_modules/@tanstack/react-virtual": { + "version": "3.14.13", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.14.13.tgz", + "integrity": "sha512-JbDTAwtzZ99aOeCrAfW5EsE5KSq5RWh6Af2dtFwyLIIk48Ja7vm6n4axu/43T3vfjFnEGJalxJ1wyYjSQD6bSg==", + "license": "MIT", + "dependencies": { + "@tanstack/virtual-core": "3.17.11" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.17.11", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.17.11.tgz", + "integrity": "sha512-+ILjvtHup6Y2hzQ6YzwMgX1Q+oQpxEGOXCEsCNaPoIP0VxMbizIBTmYTDtkerkIQS8/CbP1BRuyt8V/8BCsy1g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@testing-library/dom": { "version": "10.4.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", @@ -5552,7 +5837,7 @@ "version": "19.2.17", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -6219,31 +6504,32 @@ ] }, "node_modules/@vitest/coverage-v8": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.9.tgz", - "integrity": "sha512-Z2cOr0ksM00MpEfyVE8KXIYPEcBFxdbLSs56L8PO0QQMxt/6bDj45uQfxoc96v05KW3clk7vvgP0qfDit9DmfQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.7.tgz", + "integrity": "sha512-NEGWJS2XNu2PfRLQwOO3CTKj1tTETxNBdk454vDxVBhxJYhPaA/eS0nAI0c+1El1P7a60z8+i+ZrQoGESweGKg==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", - "@bcoe/v8-coverage": "^0.2.3", - "debug": "^4.3.7", + "@bcoe/v8-coverage": "^1.0.2", + "ast-v8-to-istanbul": "^0.3.3", + "debug": "^4.4.1", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-lib-source-maps": "^5.0.6", "istanbul-reports": "^3.1.7", - "magic-string": "^0.30.12", + "magic-string": "^0.30.17", "magicast": "^0.3.5", - "std-env": "^3.8.0", + "std-env": "^3.9.0", "test-exclude": "^7.0.1", - "tinyrainbow": "^1.2.0" + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "2.1.9", - "vitest": "2.1.9" + "@vitest/browser": "3.2.7", + "vitest": "3.2.7" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -6251,23 +6537,6 @@ } } }, - "node_modules/@vitest/coverage-v8/node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@vitest/coverage-v8/node_modules/tinyrainbow": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", - "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@vitest/expect": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.7.tgz", @@ -6286,53 +6555,30 @@ } }, "node_modules/@vitest/mocker": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", - "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.7.tgz", + "integrity": "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.9", + "@vitest/spy": "3.2.7", "estree-walker": "^3.0.3", - "magic-string": "^0.30.12" + "magic-string": "^0.30.17" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^5.0.0" + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "peerDependenciesMeta": { "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/mocker/node_modules/@vitest/spy": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", - "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^3.0.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker/node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" + "optional": true + }, + "vite": { + "optional": true + } } }, "node_modules/@vitest/pretty-format": { @@ -6481,6 +6727,16 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -6774,6 +7030,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" @@ -6789,7 +7046,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 6.0.0" @@ -6805,17 +7062,6 @@ "node": ">=4" } }, - "node_modules/axios": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", - "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.11", - "form-data": "^4.0.5", - "proxy-from-env": "^2.1.0" - } - }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -6836,15 +7082,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, - "node_modules/base32.js": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz", - "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -7047,7 +7284,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.3", @@ -7076,7 +7313,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -7092,7 +7329,7 @@ "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "dev": true, + "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -7105,7 +7342,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -7129,6 +7366,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.0", @@ -7242,6 +7480,13 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, "node_modules/check-error": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", @@ -7256,7 +7501,7 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.5.0.tgz", "integrity": "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@chevrotain/cst-dts-gen": "10.5.0", @@ -7309,7 +7554,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "consola": "^3.2.3" @@ -7481,6 +7726,13 @@ "node": ">=12.20" } }, + "node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true, + "license": "MIT" + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -7523,14 +7775,14 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/consola": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": "^14.18.0 || >=16.10.0" @@ -7820,7 +8072,7 @@ "version": "7.1.5", "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", - "dev": true, + "devOptional": true, "license": "BSD-3-Clause", "engines": { "node": ">=16.0.0" @@ -7830,6 +8082,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", @@ -7865,7 +8118,7 @@ "version": "6.1.4", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/delayed-stream": { @@ -7909,7 +8162,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/detect-libc": { @@ -8005,7 +8258,7 @@ "version": "3.18.4", "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.0.0", @@ -8029,7 +8282,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=14" @@ -8779,11 +9032,21 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", + "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" } }, + "node_modules/eventsource-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz", + "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/expect-type": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", @@ -8860,7 +9123,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/extend": { @@ -8873,7 +9136,7 @@ "version": "3.23.2", "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", - "dev": true, + "devOptional": true, "funding": [ { "type": "individual", @@ -9128,6 +9391,7 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, "license": "MIT", "dependencies": { "is-callable": "^1.2.7" @@ -9338,7 +9602,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "is-property": "^1.0.2" @@ -9401,7 +9665,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.2.0.tgz", "integrity": "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/get-proto": { @@ -9452,7 +9716,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -9603,21 +9867,21 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/grammex": { "version": "3.1.12", "resolved": "https://registry.npmjs.org/grammex/-/grammex-3.1.12.tgz", "integrity": "sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/graphmatch": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/graphmatch/-/graphmatch-1.1.1.tgz", "integrity": "sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/happy-dom": { @@ -9666,6 +9930,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" @@ -9750,7 +10015,7 @@ "version": "4.11.4", "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.4.tgz", "integrity": "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=16.9.0" @@ -9814,7 +10079,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/https-proxy-agent": { @@ -9956,6 +10221,19 @@ "node": ">=8" } }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -10128,6 +10406,7 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -10325,7 +10604,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/is-regex": { @@ -10439,6 +10718,7 @@ "version": "1.1.15", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, "license": "MIT", "dependencies": { "which-typed-array": "^1.1.16" @@ -10500,6 +10780,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, "license": "MIT" }, "node_modules/isexe": { @@ -10599,12 +10880,22 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", - "dev": true, + "devOptional": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -10613,9 +10904,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "funding": [ { "type": "github", @@ -10870,7 +11161,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=10" @@ -10933,7 +11224,7 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/lodash.camelcase": { @@ -11012,7 +11303,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.4.tgz", "integrity": "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "bun": ">=1.0.0", @@ -11268,7 +11559,7 @@ "version": "3.15.3", "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.3.tgz", "integrity": "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "aws-ssl-profiles": "^1.1.1", @@ -11289,7 +11580,7 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz", "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "lru.min": "^1.1.0" @@ -11348,59 +11639,6 @@ "node": ">= 0.6" } }, - "node_modules/next": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/next/-/next-16.2.9.tgz", - "integrity": "sha512-MEOJiq/UvuezAdqVSceHbqDgZt1kDw2tpGVOlsdIoJsQdbN2JY2hpVG4xnXGkbdJUOEWhnRfiu/O4Hpc9Juwww==", - "license": "MIT", - "dependencies": { - "@next/env": "16.2.9", - "@swc/helpers": "0.5.15", - "baseline-browser-mapping": "^2.9.19", - "caniuse-lite": "^1.0.30001579", - "postcss": "8.4.31", - "styled-jsx": "5.1.6" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": ">=20.9.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "16.2.9", - "@next/swc-darwin-x64": "16.2.9", - "@next/swc-linux-arm64-gnu": "16.2.9", - "@next/swc-linux-arm64-musl": "16.2.9", - "@next/swc-linux-x64-gnu": "16.2.9", - "@next/swc-linux-x64-musl": "16.2.9", - "@next/swc-win32-arm64-msvc": "16.2.9", - "@next/swc-win32-x64-msvc": "16.2.9", - "sharp": "^0.34.5" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.51.1", - "babel-plugin-react-compiler": "*", - "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@playwright/test": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, "node_modules/next-themes": { "version": "0.4.6", "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", @@ -11411,34 +11649,6 @@ "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, - "node_modules/next/node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -11500,7 +11710,7 @@ "version": "1.6.7", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/node-releases": { @@ -11628,7 +11838,7 @@ "version": "0.6.5", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz", "integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.2.0", @@ -11646,7 +11856,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.1.tgz", "integrity": "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/object-assign": { @@ -11774,7 +11984,7 @@ "version": "2.0.11", "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/on-finished": { @@ -11807,6 +12017,40 @@ "fn.name": "1.x.x" } }, + "node_modules/openapi-typescript": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.13.0.tgz", + "integrity": "sha512-EFP392gcqXS7ntPvbhBzbF8TyBA+baIYEm791Hy5YkjDYKTnk/Tn5OQeKm5BIZvJihpp8Zzr4hzx0Irde1LNGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@redocly/openapi-core": "^1.34.6", + "ansi-colors": "^4.1.3", + "change-case": "^5.4.4", + "parse-json": "^8.3.0", + "supports-color": "^10.2.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "openapi-typescript": "bin/cli.js" + }, + "peerDependencies": { + "typescript": "^5.x" + } + }, + "node_modules/openapi-typescript/node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -11894,6 +12138,24 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parse5": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", @@ -11994,7 +12256,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/pathval": { @@ -12011,7 +12273,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/pg": { @@ -12131,22 +12393,62 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pkg-types": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", - "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "node_modules/playwright": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz", + "integrity": "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.63.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/playwright-core": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0.tgz", + "integrity": "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", "dev": true, "license": "MIT", - "dependencies": { - "confbox": "^0.2.2", - "exsolve": "^1.0.7", - "pathe": "^2.0.3" + "engines": { + "node": ">=4" } }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -12156,7 +12458,6 @@ "version": "8.5.23", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", - "dev": true, "funding": [ { "type": "opencollective", @@ -12185,7 +12486,7 @@ "version": "3.4.7", "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.7.tgz", "integrity": "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==", - "dev": true, + "devOptional": true, "license": "Unlicense", "engines": { "node": ">=12" @@ -12293,7 +12594,7 @@ "version": "7.4.1", "resolved": "https://registry.npmjs.org/prisma/-/prisma-7.4.1.tgz", "integrity": "sha512-gDKOXwnPiMdB+uYMhMeN8jj4K7Cu3Q2wB/wUsITOoOk446HtVb8T9BZxFJ1Zop6alc89k6PMNdR2FZCpbXp/jw==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -12353,7 +12654,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -12365,7 +12666,7 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/protobufjs": { @@ -12434,7 +12735,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, + "devOptional": true, "funding": [ { "type": "individual", @@ -12483,15 +12784,6 @@ ], "license": "MIT" }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -12520,7 +12812,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "defu": "^6.1.4", @@ -12661,7 +12953,7 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/regexp.prototype.flags": { @@ -12689,7 +12981,7 @@ "version": "2.33.4", "resolved": "https://registry.npmjs.org/remeda/-/remeda-2.33.4.tgz", "integrity": "sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ==", - "dev": true, + "devOptional": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/remeda" @@ -12771,7 +13063,7 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 4" @@ -12947,6 +13239,20 @@ "fsevents": "~2.3.2" } }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.3.tgz", + "integrity": "sha512-V4KtWtQfAFMU7+9/A/VDps/VI8CHd3cYz0L8sgJzz8qK7eY7wI4ruFD82UYIYvW9Z4DtlTfhQcsl4XyPHW5uSg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -13136,7 +13442,7 @@ "version": "0.0.5", "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==", - "dev": true + "devOptional": true }, "node_modules/serve-static": { "version": "2.2.1", @@ -13161,6 +13467,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", @@ -13211,84 +13518,6 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, - "node_modules/sha.js": { - "version": "2.4.12", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", - "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", - "license": "(MIT AND BSD-3-Clause)", - "dependencies": { - "inherits": "^2.0.4", - "safe-buffer": "^5.2.1", - "to-buffer": "^1.2.0" - }, - "bin": { - "sha.js": "bin.js" - }, - "engines": { - "node": ">= 0.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" - } - }, - "node_modules/sharp/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -13427,6 +13656,18 @@ "node": ">=10" } }, + "node_modules/smol-toml": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.9.0.tgz", + "integrity": "sha512-hpd+HLON7HdZXqYchMM/+LaTTbdK0AU3NngIJ4KVyWbY9bfQqdL9cD+4yf6dUoU2Ap4VsU0JkQi6FxAI1B2mXQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -13449,7 +13690,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -13497,7 +13738,7 @@ "version": "3.10.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/stop-iteration-iterator": { @@ -14204,7 +14445,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=18" @@ -14308,20 +14549,6 @@ "dev": true, "license": "MIT" }, - "node_modules/to-buffer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", - "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", - "license": "MIT", - "dependencies": { - "isarray": "^2.0.5", - "safe-buffer": "^5.2.1", - "typed-array-buffer": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -14344,12 +14571,6 @@ "node": ">=0.6" } }, - "node_modules/toml": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", - "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", - "license": "MIT" - }, "node_modules/touch": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", @@ -14517,6 +14738,19 @@ "node": ">= 0.8.0" } }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -14535,6 +14769,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.3", @@ -14612,7 +14847,7 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -14646,6 +14881,18 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/uint8array-extras": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.6.0.tgz", + "integrity": "sha512-8iAasVS4wUx0gPLjH8Xtz2PeDzTSiy8QiLGu2DT3X5GU+egFEQhpnbtkehbAwjvDcxIERmCYcysiODFmE3Ud0Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -14764,10 +15011,11 @@ "punycode": "^2.1.0" } }, - "node_modules/urijs": { - "version": "1.19.11", - "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", - "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==", + "node_modules/uri-js-replace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uri-js-replace/-/uri-js-replace-1.0.1.tgz", + "integrity": "sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==", + "dev": true, "license": "MIT" }, "node_modules/util-deprecate": { @@ -14787,7 +15035,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.2.0.tgz", "integrity": "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==", - "dev": true, + "devOptional": true, "license": "MIT", "peerDependencies": { "typescript": ">=5" @@ -15321,47 +15569,51 @@ } }, "node_modules/vitest": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", - "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz", + "integrity": "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "2.1.9", - "@vitest/mocker": "2.1.9", - "@vitest/pretty-format": "^2.1.9", - "@vitest/runner": "2.1.9", - "@vitest/snapshot": "2.1.9", - "@vitest/spy": "2.1.9", - "@vitest/utils": "2.1.9", - "chai": "^5.1.2", - "debug": "^4.3.7", - "expect-type": "^1.1.0", - "magic-string": "^0.30.12", - "pathe": "^1.1.2", - "std-env": "^3.8.0", + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.7", + "@vitest/mocker": "3.2.7", + "@vitest/pretty-format": "^3.2.7", + "@vitest/runner": "3.2.7", + "@vitest/snapshot": "3.2.7", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", "tinybench": "^2.9.0", - "tinyexec": "^0.3.1", - "tinypool": "^1.0.1", - "tinyrainbow": "^1.2.0", - "vite": "^5.0.0", - "vite-node": "2.1.9", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.9", - "@vitest/ui": "2.1.9", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.7", + "@vitest/ui": "3.2.7", "happy-dom": "*", "jsdom": "*" }, @@ -15369,6 +15621,9 @@ "@edge-runtime/vm": { "optional": true }, + "@types/debug": { + "optional": true + }, "@types/node": { "optional": true }, @@ -15386,99 +15641,19 @@ } } }, - "node_modules/vitest/node_modules/@vitest/expect": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", - "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "2.1.9", - "@vitest/utils": "2.1.9", - "chai": "^5.1.2", - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vitest/node_modules/@vitest/pretty-format": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", - "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vitest/node_modules/@vitest/runner": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", - "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "2.1.9", - "pathe": "^1.1.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vitest/node_modules/@vitest/snapshot": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", - "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "2.1.9", - "magic-string": "^0.30.12", - "pathe": "^1.1.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vitest/node_modules/@vitest/spy": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", - "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^3.0.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vitest/node_modules/@vitest/utils": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", - "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "2.1.9", - "loupe": "^3.1.2", - "tinyrainbow": "^1.2.0" + "engines": { + "node": ">=12" }, "funding": { - "url": "https://opencollective.com/vitest" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/vitest/node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true, - "license": "MIT" - }, "node_modules/vitest/node_modules/tinyexec": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", @@ -15486,49 +15661,6 @@ "dev": true, "license": "MIT" }, - "node_modules/vitest/node_modules/tinyrainbow": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", - "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/vitest/node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/vitest/node_modules/vite-node": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", - "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.3.7", - "es-module-lexer": "^1.5.4", - "pathe": "^1.1.2", - "vite": "^5.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", @@ -15671,6 +15803,7 @@ "version": "1.1.20", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", @@ -15924,6 +16057,13 @@ "url": "https://github.com/sponsors/eemeli" } }, + "node_modules/yaml-ast-parser": { + "version": "0.0.43", + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/yargs": { "version": "17.7.3", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", @@ -16019,7 +16159,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/zeptomatch/-/zeptomatch-2.1.0.tgz", "integrity": "sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "grammex": "^3.1.11", From 99fc3e763cb335e395ae582dabdef142899853a8 Mon Sep 17 00:00:00 2001 From: sojetunde8 Date: Mon, 5 Oct 2026 14:30:03 +0100 Subject: [PATCH 08/13] fix(contracts): silence dead_code on PriceOracle consumed by contractclient --- contracts/stream_contract/src/types.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/stream_contract/src/types.rs b/contracts/stream_contract/src/types.rs index 3e1bb77e..aa7694d6 100644 --- a/contracts/stream_contract/src/types.rs +++ b/contracts/stream_contract/src/types.rs @@ -8,6 +8,10 @@ use soroban_sdk::{contractclient, contracttype, Address, BytesN, Env, Vec}; /// The asset argument mirrors the SEP-40 `Asset` wire shape as its own /// contracttype so the generated invocation matches oracle deployments /// without pulling SDK type differences into our ABI. +// The trait itself is never implemented or named directly — `#[contractclient]` +// consumes it to generate `OracleClient`, which lib.rs invokes. rustc's +// dead-code pass doesn't credit that generated usage, so silence the lint. +#[allow(dead_code)] #[contractclient(name = "OracleClient")] pub trait PriceOracle { /// Last recorded price for `asset`, or `None` when the oracle has none. From d177501aa7d3eef31b6ffb2ee4bd4f775797d5bd Mon Sep 17 00:00:00 2001 From: sojetunde8 Date: Mon, 5 Oct 2026 14:30:10 +0100 Subject: [PATCH 09/13] style(contracts): apply rustfmt to stream_contract --- contracts/stream_contract/src/lib.rs | 42 +++++++++++----------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/contracts/stream_contract/src/lib.rs b/contracts/stream_contract/src/lib.rs index 87593ebf..1d2fa189 100644 --- a/contracts/stream_contract/src/lib.rs +++ b/contracts/stream_contract/src/lib.rs @@ -45,12 +45,12 @@ use soroban_sdk::{ use errors::StreamError; use events::{ AdminTransferredEvent, AllowanceStreamCreatedEvent, ContractUpgradedEvent, - DisputeRequestedEvent, DisputeResolvedEvent, EmergencyGuardianUpdatedEvent, - FeeCollectedEvent, FeeConfigUpdatedEvent, HybridCliffStreamCreatedEvent, InitializedEvent, + DisputeRequestedEvent, DisputeResolvedEvent, EmergencyGuardianUpdatedEvent, FeeCollectedEvent, + FeeConfigUpdatedEvent, HybridCliffStreamCreatedEvent, InitializedEvent, MilestoneConditionUnlockedEvent, ProtocolPauseStatusEvent, StateMigratedEvent, - StepVestingStreamCreatedEvent, StreamCancelledEvent, StreamClosedEvent, - StreamCompletedEvent, StreamCreatedEvent, StreamPausedEvent, StreamRateModifiedEvent, - StreamResumedEvent, StreamToppedUpEvent, TokensWithdrawnEvent + StepVestingStreamCreatedEvent, StreamCancelledEvent, StreamClosedEvent, StreamCompletedEvent, + StreamCreatedEvent, StreamPausedEvent, StreamRateModifiedEvent, StreamResumedEvent, + StreamToppedUpEvent, TokensWithdrawnEvent, }; use storage::{ config_exists, get_contract_version, get_recorded_wasm_hash, load_config, load_stream, @@ -58,10 +58,9 @@ use storage::{ save_stream, try_load_config, try_load_stream, }; use types::{ - ConditionalMilestone, DataKey, DisputeStatus, MAX_BATCH_WITHDRAW, - MAX_CONDITIONAL_MILESTONES, MAX_VESTING_STEPS, ORACLE_PRICE_MAX_AGE_SECS, OracleAsset, - OracleClient, ProtocolConfig, Stream, StreamStatus, UnlockCondition, VestingSchedule, - VestingStep + ConditionalMilestone, DataKey, DisputeStatus, OracleAsset, OracleClient, ProtocolConfig, + Stream, StreamStatus, UnlockCondition, VestingSchedule, VestingStep, MAX_BATCH_WITHDRAW, + MAX_CONDITIONAL_MILESTONES, MAX_VESTING_STEPS, ORACLE_PRICE_MAX_AGE_SECS, }; /// Maximum allowed protocol fee: 1 000 bps = 10%. @@ -817,13 +816,13 @@ impl StreamContract { is_allowance_based: false, }, ); - env.storage().instance().set( - &DataKey::ConditionalMilestones(stream_id), - &milestones, - ); env.storage() .instance() - .set(&DataKey::AttestedIds(stream_id), &Vec::>::new(&env)); + .set(&DataKey::ConditionalMilestones(stream_id), &milestones); + env.storage().instance().set( + &DataKey::AttestedIds(stream_id), + &Vec::>::new(&env), + ); Self::transfer_fee(&env, &token_address, stream_id, fee_amount, treasury); @@ -895,13 +894,7 @@ impl StreamContract { .ok_or(StreamError::StreamNotFound)?; let index = (0..milestones.len()) - .find(|&i| { - milestones - .get(i) - .expect("index in range") - .milestone_id - == milestone_id - }) + .find(|&i| milestones.get(i).expect("index in range").milestone_id == milestone_id) .ok_or(StreamError::InvalidMilestone)?; let mut milestone = milestones.get(index).expect("index in range"); if milestone.is_unlocked { @@ -955,10 +948,9 @@ impl StreamContract { milestone.is_unlocked = true; let unlocked_amount = milestone.amount; milestones.set(index, milestone); - env.storage().instance().set( - &DataKey::ConditionalMilestones(stream_id), - &milestones, - ); + env.storage() + .instance() + .set(&DataKey::ConditionalMilestones(stream_id), &milestones); // Promote the tranche into the normal step-unlock flow: rewrite its // gate time from "never" to "now" so `calculate_claimable` counts it. From 5aa35ac5e0b98ff603079f22662ea94e5e46774e Mon Sep 17 00:00:00 2001 From: sojetunde8 Date: Mon, 5 Oct 2026 16:39:52 +0100 Subject: [PATCH 10/13] fix(backend): aggregate analytics in JS over i128 strings and fix logger calls --- .../src/controllers/analytics.controller.ts | 12 ++- backend/src/services/analytics.service.ts | 95 ++++++++++++------- backend/tests/analytics.test.ts | 55 ++++++----- 3 files changed, 98 insertions(+), 64 deletions(-) diff --git a/backend/src/controllers/analytics.controller.ts b/backend/src/controllers/analytics.controller.ts index 7a0980ba..96dfbb88 100644 --- a/backend/src/controllers/analytics.controller.ts +++ b/backend/src/controllers/analytics.controller.ts @@ -30,7 +30,7 @@ export async function getTvlHandler(_req: Request, res: Response) { const snapshot = await getProtocolTvl(); res.json({ success: true, data: snapshot }); } catch (error) { - logger.error({ err: error }, "analytics TVL request failed"); + logger.error("analytics TVL request failed:", error); sendApiError(res, 500, "ANALYTICS_UNAVAILABLE", "TVL snapshot unavailable"); } } @@ -45,27 +45,29 @@ export async function getHistoricalHandler(req: Request, res: Response) { const interval = (req.query.interval ?? "1d") as AnalyticsInterval; if (!VALID_PERIODS.includes(period)) { - return sendApiError( + sendApiError( res, 400, "INVALID_PERIOD", `period must be one of: ${VALID_PERIODS.join(", ")}` ); + return; } if (!VALID_INTERVALS.includes(interval)) { - return sendApiError( + sendApiError( res, 400, "INVALID_INTERVAL", `interval must be one of: ${VALID_INTERVALS.join(", ")}` ); + return; } try { const series = await getHistoricalAnalytics(period, interval); res.json({ success: true, data: series }); } catch (error) { - logger.error({ err: error }, "analytics historical request failed"); + logger.error("analytics historical request failed:", error); sendApiError( res, 500, @@ -86,7 +88,7 @@ export async function getDefiLlamaHandler(_req: Request, res: Response) { // DefiLlama consumes the raw object; no success envelope here. res.json(adapter); } catch (error) { - logger.error({ err: error }, "DefiLlama adapter request failed"); + logger.error("DefiLlama adapter request failed:", error); sendApiError( res, 500, diff --git a/backend/src/services/analytics.service.ts b/backend/src/services/analytics.service.ts index 9a6bfc6f..3136793c 100644 --- a/backend/src/services/analytics.service.ts +++ b/backend/src/services/analytics.service.ts @@ -9,8 +9,7 @@ * endpoints stay available on stock PostgreSQL deployments. */ -import { prisma } from "../lib/prisma.js"; -import { pool } from "../lib/pg-pool.js"; +import { pool, prisma } from "../lib/prisma.js"; import logger from "../logger.js"; /** Period accepted by the historical endpoint. */ @@ -88,38 +87,58 @@ async function hasTimescale(): Promise { timescaleAvailable = result.rows[0]?.hypertable !== null; } catch (error) { logger.warn( - { err: error }, - "TimescaleDB analytics tables unavailable; using live aggregation" + "TimescaleDB analytics tables unavailable; using live aggregation:", + error ); timescaleAvailable = false; } return timescaleAvailable; } -/** Live fallback: aggregate active streams straight from Prisma. */ +/** + * Live fallback: aggregate active streams straight from Prisma. + * + * Amount columns are i128-preserved strings in the schema, so Prisma's + * `_sum` aggregation does not apply to them; rows are summed in JS with + * `BigInt` instead to keep the full precision end to end. + */ async function liveTvl(): Promise { - const grouped = await prisma.stream.groupBy({ - by: ["tokenAddress"], + const streams = await prisma.stream.findMany({ where: { isActive: true }, - _count: { _all: true }, - _sum: { + select: { + tokenAddress: true, depositedAmount: true, withdrawnAmount: true, ratePerSecond: true, }, }); - const tokens: TokenTvl[] = grouped.map((g) => { - const deposited = BigInt(g._sum.depositedAmount ?? 0); - const withdrawn = BigInt(g._sum.withdrawnAmount ?? 0); - const locked = deposited > withdrawn ? deposited - withdrawn : 0n; - return { - tokenAddress: g.tokenAddress, - totalLockedStroops: locked.toString(), - velocityPerSecondStroops: BigInt(g._sum.ratePerSecond ?? 0).toString(), - activeStreamCount: g._count._all, + const byToken = new Map< + string, + { locked: bigint; velocity: bigint; count: number } + >(); + for (const stream of streams) { + const deposited = BigInt(stream.depositedAmount); + const withdrawn = BigInt(stream.withdrawnAmount); + const entry = byToken.get(stream.tokenAddress) ?? { + locked: 0n, + velocity: 0n, + count: 0, }; - }); + entry.locked += deposited > withdrawn ? deposited - withdrawn : 0n; + entry.velocity += BigInt(stream.ratePerSecond); + entry.count += 1; + byToken.set(stream.tokenAddress, entry); + } + + const tokens: TokenTvl[] = [...byToken.entries()].map( + ([tokenAddress, agg]) => ({ + tokenAddress, + totalLockedStroops: agg.locked.toString(), + velocityPerSecondStroops: agg.velocity.toString(), + activeStreamCount: agg.count, + }) + ); const totalTvlStroops = tokens .reduce((acc, t) => acc + BigInt(t.totalLockedStroops), 0n) @@ -135,12 +154,7 @@ async function liveTvl(): Promise { /** Latest snapshot row per token, straight from the hypertable. */ async function timescaleTvl(): Promise { - const result = await pool.query<{ - token_address: string; - total_locked_amount: string; - flow_velocity_per_second: string; - active_stream_count: number; - }>( + const result = await pool.query( `SELECT DISTINCT ON (token_address) token_address, total_locked_amount, @@ -150,7 +164,14 @@ async function timescaleTvl(): Promise { ORDER BY token_address, time DESC` ); - const tokens: TokenTvl[] = result.rows.map((row) => ({ + const snapshotRows = result.rows as { + token_address: string; + total_locked_amount: string; + flow_velocity_per_second: string; + active_stream_count: number; + }[]; + + const tokens: TokenTvl[] = snapshotRows.map((row) => ({ tokenAddress: row.token_address, totalLockedStroops: BigInt(row.total_locked_amount).toString(), velocityPerSecondStroops: BigInt(row.flow_velocity_per_second).toString(), @@ -176,8 +197,8 @@ export async function getProtocolTvl(): Promise { return await timescaleTvl(); } catch (error) { logger.warn( - { err: error }, - "Timescale TVL query failed; falling back to live aggregation" + "Timescale TVL query failed; falling back to live aggregation:", + error ); } } @@ -197,13 +218,7 @@ export async function getHistoricalAnalytics( interval === "1d" ? "daily_protocol_metrics" : "hourly_protocol_metrics"; const days = PERIOD_DAYS[period]; - const result = await pool.query<{ - bucket: Date; - token_address: string; - avg_tvl: string; - aggregate_velocity: string; - peak_streams: number; - }>( + const result = await pool.query( `SELECT bucket, token_address, avg_tvl, aggregate_velocity, peak_streams FROM ${view} WHERE bucket >= now() - ($1 || ' days')::interval @@ -211,11 +226,19 @@ export async function getHistoricalAnalytics( [String(days)] ); + const seriesRows = result.rows as { + bucket: Date; + token_address: string; + avg_tvl: string; + aggregate_velocity: string; + peak_streams: number; + }[]; + return { source: "timescaledb", period, interval, - points: result.rows.map((row) => ({ + points: seriesRows.map((row) => ({ bucket: new Date(row.bucket).toISOString(), tokenAddress: row.token_address, avgTvlStroops: BigInt(row.avg_tvl).toString(), diff --git a/backend/tests/analytics.test.ts b/backend/tests/analytics.test.ts index f7e096ba..d3701d02 100644 --- a/backend/tests/analytics.test.ts +++ b/backend/tests/analytics.test.ts @@ -14,12 +14,9 @@ const queryMock = vi.fn(); vi.mock("../src/lib/prisma.js", () => ({ prisma: { stream: { - groupBy: vi.fn(), + findMany: vi.fn(), }, }, -})); - -vi.mock("../src/lib/pg-pool.js", () => ({ pool: { query: (...args: unknown[]) => queryMock(...args), }, @@ -48,15 +45,26 @@ describe("analytics service (#1480)", () => { // to_regclass returns null → no hypertable. queryMock.mockResolvedValue({ rows: [{ hypertable: null }] }); - (prisma.stream.groupBy as ReturnType).mockResolvedValue([ + // Amount columns are i128 strings in the schema; three active streams on + // one token, aggregated in JS (deposits 1,000,000 − withdrawals 400,000). + (prisma.stream.findMany as ReturnType).mockResolvedValue([ { tokenAddress: "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC", - _count: { _all: 3 }, - _sum: { - depositedAmount: BigInt(1_000_000), - withdrawnAmount: BigInt(400_000), - ratePerSecond: BigInt(50), - }, + depositedAmount: "400000", + withdrawnAmount: "100000", + ratePerSecond: "20", + }, + { + tokenAddress: "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC", + depositedAmount: "350000", + withdrawnAmount: "150000", + ratePerSecond: "20", + }, + { + tokenAddress: "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC", + depositedAmount: "250000", + withdrawnAmount: "150000", + ratePerSecond: "10", }, ]); @@ -64,8 +72,8 @@ describe("analytics service (#1480)", () => { expect(snapshot.source).toBe("live"); expect(snapshot.totalTvlStroops).toBe("600000"); - expect(snapshot.tokens[0].activeStreamCount).toBe(3); - expect(snapshot.tokens[0].velocityPerSecondStroops).toBe("50"); + expect(snapshot.tokens[0]?.activeStreamCount).toBe(3); + expect(snapshot.tokens[0]?.velocityPerSecondStroops).toBe("50"); }); it("reads the latest hypertable snapshot per token when TimescaleDB is present", async () => { @@ -88,20 +96,17 @@ describe("analytics service (#1480)", () => { expect(snapshot.source).toBe("timescaledb"); expect(snapshot.totalTvlStroops).toBe("900000"); // DISTINCT ON (token_address) ... ORDER BY token_address, time DESC - expect(queryMock.mock.calls[1][0]).toContain("DISTINCT ON"); + expect(queryMock.mock.calls[1]?.[0]).toContain("DISTINCT ON"); }); it("maps the snapshot into the DefiLlama adapter shape", async () => { queryMock.mockResolvedValue({ rows: [{ hypertable: null }] }); - (prisma.stream.groupBy as ReturnType).mockResolvedValue([ + (prisma.stream.findMany as ReturnType).mockResolvedValue([ { tokenAddress: "TOKEN_A", - _count: { _all: 1 }, - _sum: { - depositedAmount: BigInt(500), - withdrawnAmount: BigInt(200), - ratePerSecond: BigInt(1), - }, + depositedAmount: "500", + withdrawnAmount: "200", + ratePerSecond: "1", }, ]); @@ -122,6 +127,10 @@ describe("analytics service (#1480)", () => { points: [], }); + // The first call latched the probe result in the service cache; clear it + // so the timescaledb path below actually re-probes the pool. + resetTimescaleCacheForTests(); + // First call is the availability probe, second the data query. queryMock.mockResolvedValueOnce({ rows: [{ hypertable: "stream_flow_snapshots" }] }); queryMock.mockResolvedValueOnce({ @@ -137,8 +146,8 @@ describe("analytics service (#1480)", () => { }); const series = await getHistoricalAnalytics("90d", "1d"); expect(series.source).toBe("timescaledb"); - expect(series.points[0].avgTvlStroops).toBe("123"); - expect(series.points[0].peakStreams).toBe(9); + expect(series.points[0]?.avgTvlStroops).toBe("123"); + expect(series.points[0]?.peakStreams).toBe(9); expect(queryMock.mock.calls.at(-1)![0]).toContain("daily_protocol_metrics"); }); }); From f090e22d87e25fc2a9a58f773288bdafd84deca7 Mon Sep 17 00:00:00 2001 From: sojetunde8 Date: Mon, 5 Oct 2026 16:39:58 +0100 Subject: [PATCH 11/13] fix(frontend): drop unused imports failing production type check --- frontend/src/components/dashboard/CopyButton.tsx | 2 +- frontend/src/components/dashboard/StreamDetailsModal.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/dashboard/CopyButton.tsx b/frontend/src/components/dashboard/CopyButton.tsx index 7cc276f9..792f1c56 100644 --- a/frontend/src/components/dashboard/CopyButton.tsx +++ b/frontend/src/components/dashboard/CopyButton.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { useEffect, useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { Check, Copy } from "lucide-react"; import { copyToClipboard } from "@/lib/clipboard"; diff --git a/frontend/src/components/dashboard/StreamDetailsModal.tsx b/frontend/src/components/dashboard/StreamDetailsModal.tsx index 6df0499e..ef89419e 100644 --- a/frontend/src/components/dashboard/StreamDetailsModal.tsx +++ b/frontend/src/components/dashboard/StreamDetailsModal.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { useState } from "react"; +import React from "react"; import { Button } from "@/components/ui/Button"; import { useModalDialog } from "@/hooks/useModalDialog"; import { CopyButton } from "./CopyButton"; From d3ca263750eaa17bdc98ab6ee3ab03bbb13a80d6 Mon Sep 17 00:00:00 2001 From: sojetunde8 Date: Mon, 5 Oct 2026 16:40:04 +0100 Subject: [PATCH 12/13] test(contracts): cover dispute, allowance, rate and conditional milestone paths --- contracts/stream_contract/src/test.rs | 349 ++++++++++++++++++++++++++ 1 file changed, 349 insertions(+) diff --git a/contracts/stream_contract/src/test.rs b/contracts/stream_contract/src/test.rs index f97ddd6c..7344eb66 100644 --- a/contracts/stream_contract/src/test.rs +++ b/contracts/stream_contract/src/test.rs @@ -5774,3 +5774,352 @@ fn test_batch_withdraw_never_exceeds_escrow_across_many_streams() { assert_eq!(balances.balance(&recipient), 10_000); assert_eq!(balances.balance(&contract), 0, "escrow not fully drained"); } + +// ─── Coverage: modify_rate (#1316) ────────────────────────────────────────── + +#[test] +fn test_coverage_modify_rate_recomputes_end_time_and_emits() { + let env = Env::default(); + env.mock_all_auths(); + let (token, _) = create_token(&env); + let client = create_contract(&env); + let sender = Address::generate(&env); + let recipient = Address::generate(&env); + mint(&env, &token, &sender, 1_000); + + let id = client.create_stream(&sender, &recipient, &token, &1_000, &1_000); + + let new_end = client.modify_rate(&sender, &id, &2); + + // 1_000 remaining at 2/s → 500s projected end, from the current timestamp. + let now = env.ledger().timestamp(); + assert_eq!(new_end, now + 500); + let stream = client.get_stream(&id).unwrap(); + assert_eq!(stream.rate_per_second, 2); + assert_eq!(stream.last_update_time, now); +} + +#[test] +fn test_coverage_modify_rate_rejects_non_linear_schedule() { + let env = Env::default(); + env.mock_all_auths(); + let (token, _) = create_token(&env); + let client = create_contract(&env); + let sender = Address::generate(&env); + let recipient = Address::generate(&env); + mint(&env, &token, &sender, 1_000); + + let steps = step_schedule(&env, &[(100, 500), (200, 500)]); + let id = client.create_step_vesting_stream(&sender, &recipient, &token, &1_000, &steps); + + let result = client.try_modify_rate(&sender, &id, &2_000); + assert_eq!(result, Err(Ok(StreamError::RateModificationUnsupported))); +} + +// ─── Coverage: allowance streams (#1318) ─────────────────────────────────── + +#[test] +fn test_coverage_create_allowance_stream_gates_on_allowance() { + let env = Env::default(); + env.mock_all_auths(); + let (token, _) = create_token(&env); + let client = create_contract(&env); + let sender = Address::generate(&env); + let recipient = Address::generate(&env); + + // No allowance yet → creation is rejected. + let result = client.try_create_allowance_stream(&sender, &recipient, &token, &1_000); + assert_eq!(result, Err(Ok(StreamError::AllowanceLocked))); + + // Approve the contract as spender, then creation succeeds. + token::Client::new(&env, &token).approve( + &sender, + &client.address, + &10_000, + &(env.ledger().sequence() + 1_000), + ); + let id = client.create_allowance_stream(&sender, &recipient, &token, &1_000); + + let stream = client.get_stream(&id).unwrap(); + assert!(stream.is_allowance_based); + assert_eq!(stream.deposited_amount, 0); + assert_eq!(stream.rate_per_second, 1); + assert_eq!(stream.status, StreamStatus::Active); +} + +// ─── Coverage: dispute & escrow (#1319) ──────────────────────────────────── + +/// Seeds an arbiter directly on the stream's storage. The arbiter slot is +/// intentionally immutable through the public API, so tests reach in the same +/// way the escrow deployment flow would. +fn seed_arbiter(env: &Env, client: &StreamContractClient, stream_id: u64, arbiter: &Address) { + let mut stream = client.get_stream(&stream_id).unwrap(); + stream.arbiter = Some(arbiter.clone()); + env.as_contract(&client.address, || { + env.storage() + .persistent() + .set(&types::DataKey::Stream(stream_id), &stream); + }); +} + +#[test] +fn test_coverage_dispute_request_then_rejected_resolution() { + let env = Env::default(); + env.mock_all_auths(); + let (token, _) = create_token(&env); + let client = create_contract(&env); + let sender = Address::generate(&env); + let recipient = Address::generate(&env); + let arbiter = Address::generate(&env); + mint(&env, &token, &sender, 2_000); + + // Both streams are created before the arbiter is seeded below: creation + // re-validates the token contract cross-contract, which must not run + // after direct storage injection. + let no_arbiter_id = client.create_stream(&sender, &recipient, &token, &100, &100); + let id = client.create_stream(&sender, &recipient, &token, &1_000, &1_000); + seed_arbiter(&env, &client, id, &arbiter); + + // Without an arbiter a dispute cannot even be requested. + let result = client.try_request_dispute(&sender, &no_arbiter_id); + assert_eq!(result, Err(Ok(StreamError::DisputeNotSupported))); + + client.request_dispute(&sender, &id); + assert_eq!( + client.get_stream(&id).unwrap().dispute_status, + DisputeStatus::Requested + ); + + // A non-arbiter cannot resolve; the arbiter rejects; the stream stays + // active with the dispute recorded as resolved(false). + let result = client.try_resolve_dispute(&Address::generate(&env), &id, &true); + assert_eq!(result, Err(Ok(StreamError::NotArbiter))); + + client.resolve_dispute(&arbiter, &id, &false); + let stream = client.get_stream(&id).unwrap(); + assert_eq!(stream.dispute_status, DisputeStatus::Resolved(false)); + assert!(stream.is_active); + assert_eq!( + token::Client::new(&env, &token).balance(&client.address), + 1_100 + ); +} + +#[test] +fn test_coverage_dispute_approved_resolution_cancels_and_distributes() { + let env = Env::default(); + env.mock_all_auths(); + let (token, _) = create_token(&env); + let client = create_contract(&env); + let sender = Address::generate(&env); + let recipient = Address::generate(&env); + let arbiter = Address::generate(&env); + mint(&env, &token, &sender, 1_000); + + let id = client.create_stream(&sender, &recipient, &token, &1_000, &1_000); + seed_arbiter(&env, &client, id, &arbiter); + + advance(&env, 500); + client.request_dispute(&sender, &id); + client.resolve_dispute(&arbiter, &id, &true); + + // Resolution pays accrued 500 to the recipient and refunds 500 to the + // sender, then terminates the stream. + let balances = token::Client::new(&env, &token); + assert_eq!(balances.balance(&recipient), 500); + assert_eq!(balances.balance(&sender), 500); + assert_eq!(balances.balance(&client.address), 0); + + let stream = client.get_stream(&id).unwrap(); + assert_eq!(stream.status, StreamStatus::Cancelled); + assert!(!stream.is_active); + assert_eq!(stream.dispute_status, DisputeStatus::Resolved(true)); +} + +// ─── Coverage: close_stream ──────────────────────────────────────────────── + +#[test] +fn test_coverage_close_stream_prunes_fully_withdrawn_stream() { + let env = Env::default(); + env.mock_all_auths(); + let (token, _) = create_token(&env); + let client = create_contract(&env); + let sender = Address::generate(&env); + let recipient = Address::generate(&env); + mint(&env, &token, &sender, 1_000); + + let id = client.create_stream(&sender, &recipient, &token, &1_000, &1_000); + + // Active streams cannot be pruned. + let result = client.try_close_stream(&sender, &id); + assert_eq!(result, Err(Ok(StreamError::StreamStillActive))); + + advance(&env, 1_000); + client.withdraw(&recipient, &id); + assert!(client.is_stream_completed(&id)); + + client.close_stream(&sender, &id); + assert!(client.get_stream(&id).is_none()); + + // Already-pruned streams are gone for good. + let result = client.try_close_stream(&sender, &id); + assert_eq!(result, Err(Ok(StreamError::StreamNotFound))); +} + +// ─── Coverage: conditional streams & milestones (#1482) ──────────────────── + +/// Minimal SEP-40-style price oracle used to exercise `PriceTarget` +/// milestones' cross-contract `lastprice` calls. +#[contract] +pub struct TestOracle; + +use soroban_sdk::{contract, contractimpl}; + +#[contractimpl] +impl TestOracle { + /// Seeds (or clears, with `None`) the single oracle reading. + pub fn set_reading(env: Env, reading: Option) { + env.storage().instance().set(&0u32, &reading); + } + + pub fn lastprice(env: Env, _asset: types::OracleAsset) -> Option { + env.storage().instance().get(&0u32).unwrap_or(None) + } +} + +#[test] +fn test_coverage_conditional_stream_timeonly_lifecycle() { + let env = Env::default(); + env.mock_all_auths(); + let (token, _) = create_token(&env); + let client = create_contract(&env); + let sender = Address::generate(&env); + let recipient = Address::generate(&env); + mint(&env, &token, &sender, 1_000); + + let milestones = vec![ + &env, + ConditionalMilestone { + milestone_id: 1, + amount: 600, + condition: UnlockCondition::TimeOnly(200), + is_unlocked: false, + }, + ConditionalMilestone { + milestone_id: 2, + amount: 400, + condition: UnlockCondition::TimeOnly(500), + is_unlocked: false, + }, + ]; + + let id = client.create_conditional_stream(&sender, &recipient, &token, &1_000, &milestones); + + // The whole gross amount sits in escrow; tranches stay time-locked. + assert_eq!( + token::Client::new(&env, &token).balance(&client.address), + 1_000 + ); + + // Verifying an unknown milestone id is rejected. + let result = client.try_verify_and_unlock_milestone(&recipient, &id, &99); + assert_eq!(result, Err(Ok(StreamError::InvalidMilestone))); + + // Not-yet-due milestone does not unlock. + let result = client.try_verify_and_unlock_milestone(&recipient, &id, &1); + assert_eq!(result, Err(Ok(StreamError::ConditionNotMet))); + + advance(&env, 200); + client.verify_and_unlock_milestone(&recipient, &id, &1); + + // Double-verification is rejected... + let result = client.try_verify_and_unlock_milestone(&recipient, &id, &1); + assert_eq!(result, Err(Ok(StreamError::MilestoneAlreadyUnlocked))); + + // ...and the unlocked tranche is immediately claimable (600 so far). + assert_eq!(client.get_claimable_amount(&id).unwrap(), 600); + + // Neither party other than sender/recipient may verify. + let result = client.try_verify_and_unlock_milestone(&Address::generate(&env), &id, &2); + assert_eq!(result, Err(Ok(StreamError::MilestoneCallerUnauthorized))); + + advance(&env, 300); + client.verify_and_unlock_milestone(&sender, &id, &2); + assert_eq!(client.get_claimable_amount(&id).unwrap(), 1_000); + + let withdrawn = client.withdraw(&recipient, &id); + assert_eq!(withdrawn, 1_000); + assert!(client.is_stream_completed(&id)); +} + +#[test] +fn test_coverage_conditional_stream_price_target_lifecycle() { + let env = Env::default(); + env.mock_all_auths(); + let (token, _) = create_token(&env); + let client = create_contract(&env); + let sender = Address::generate(&env); + let recipient = Address::generate(&env); + let oracle_id = env.register(TestOracle, ()); + mint(&env, &token, &sender, 1_000); + + let milestones = vec![ + &env, + ConditionalMilestone { + milestone_id: 1, + amount: 1_000, + condition: UnlockCondition::PriceTarget(oracle_id.clone(), 500, true), + is_unlocked: false, + }, + ]; + let id = client.create_conditional_stream(&sender, &recipient, &token, &1_000, &milestones); + + // Move past the freshness window so the stale-reading case is testable. + advance(&env, 7_200); + + // No reading at all → OraclePriceUnavailable. + let result = client.try_verify_and_unlock_milestone(&recipient, &id, &1); + assert_eq!(result, Err(Ok(StreamError::OraclePriceUnavailable))); + + // A reading older than the freshness window → OraclePriceStale. + env.as_contract(&oracle_id, || { + TestOracle::set_reading( + env.clone(), + Some(types::PriceData { + price: 600, + timestamp: env.ledger().timestamp().saturating_sub(4_000), + }), + ); + }); + let result = client.try_verify_and_unlock_milestone(&recipient, &id, &1); + assert_eq!(result, Err(Ok(StreamError::OraclePriceStale))); + + // Fresh price below the target → ConditionNotMet. + env.as_contract(&oracle_id, || { + TestOracle::set_reading( + env.clone(), + Some(types::PriceData { + price: 499, + timestamp: env.ledger().timestamp(), + }), + ); + }); + let result = client.try_verify_and_unlock_milestone(&recipient, &id, &1); + assert_eq!(result, Err(Ok(StreamError::ConditionNotMet))); + + // Fresh price at the target (>=) → unlock, then withdraw. + env.as_contract(&oracle_id, || { + TestOracle::set_reading( + env.clone(), + Some(types::PriceData { + price: 500, + timestamp: env.ledger().timestamp(), + }), + ); + }); + client.verify_and_unlock_milestone(&recipient, &id, &1); + + let withdrawn = client.withdraw(&recipient, &id); + assert_eq!(withdrawn, 1_000); + assert!(client.is_stream_completed(&id)); +} From 235f96ce0d66a0247580e90829629280bf620185 Mon Sep 17 00:00:00 2001 From: sojetunde8 Date: Tue, 6 Oct 2026 11:25:47 +0100 Subject: [PATCH 13/13] fix(deps): bump proxy-addr and source-map-js past critical and high advisories --- package-lock.json | 234 +++------------------------------------------- 1 file changed, 11 insertions(+), 223 deletions(-) diff --git a/package-lock.json b/package-lock.json index ffd9e402..33dbfea1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -180,16 +180,6 @@ "@prisma/debug": "6.19.3" } }, - "backend/node_modules/@stellar/js-xdr": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-5.0.0.tgz", - "integrity": "sha512-HBDNKnxr+ecdaEmbZ0mcKkirOF8tXXEbWSw34P3wT40Tn3g+u+cCH17xAWZymzscq8cojHB8340pR8QNVaD32w==", - "license": "Apache-2.0", - "engines": { - "node": ">=22.0.0", - "pnpm": ">=10.0.0" - } - }, "backend/node_modules/@stellar/stellar-sdk": { "version": "17.2.1", "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-17.2.1.tgz", @@ -240,49 +230,12 @@ "undici-types": "~7.18.0" } }, - "backend/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "backend/node_modules/axios": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.20.0.tgz", - "integrity": "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.16.0", - "form-data": "^4.0.6", - "https-proxy-agent": "^5.0.1", - "proxy-from-env": "^2.1.0" - } - }, "backend/node_modules/bignumber.js": { "version": "11.1.5", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-11.1.5.tgz", "integrity": "sha512-6WmzCNtUnfKpbozq+hOgWaZMMzORmYBwF1xZScyoIX3QRYWeKTtxxwDOW5tIz7C9BdjkIYHGTcelCLkXg0mndw==", "license": "MIT" }, - "backend/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "backend/node_modules/prisma": { "version": "6.19.3", "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.3.tgz", @@ -358,16 +311,6 @@ "url": "https://paulmillr.com/funding/" } }, - "frontend/node_modules/@stellar/js-xdr": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-5.0.0.tgz", - "integrity": "sha512-HBDNKnxr+ecdaEmbZ0mcKkirOF8tXXEbWSw34P3wT40Tn3g+u+cCH17xAWZymzscq8cojHB8340pR8QNVaD32w==", - "license": "Apache-2.0", - "engines": { - "node": ">=22.0.0", - "pnpm": ">=10.0.0" - } - }, "frontend/node_modules/@stellar/stellar-sdk": { "version": "17.2.1", "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-17.2.1.tgz", @@ -422,30 +365,6 @@ } } }, - "frontend/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "frontend/node_modules/axios": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.20.0.tgz", - "integrity": "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.16.0", - "form-data": "^4.0.6", - "https-proxy-agent": "^5.0.1", - "proxy-from-env": "^2.1.0" - } - }, "frontend/node_modules/bignumber.js": { "version": "11.1.5", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-11.1.5.tgz", @@ -464,19 +383,6 @@ "node": ">=20.0.0" } }, - "frontend/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/@acemir/cssom": { "version": "0.9.31", "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz", @@ -1932,9 +1838,6 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1951,9 +1854,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1970,9 +1870,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1989,9 +1886,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2008,9 +1902,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2027,9 +1918,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2046,9 +1934,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2065,9 +1950,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2084,9 +1966,6 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2109,9 +1988,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2134,9 +2010,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2159,9 +2032,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2184,9 +2054,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2209,9 +2076,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2234,9 +2098,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2259,9 +2120,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2522,9 +2380,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2541,9 +2396,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2560,9 +2412,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2579,9 +2428,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -4509,9 +4355,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4679,62 +4522,6 @@ "pnpm": ">=10.0.0" } }, - "node_modules/@stellar/stellar-sdk": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-17.1.0.tgz", - "integrity": "sha512-vf/d9KR2B7MkrJBKW+cN8GPgBbiLFrRfr2pkR41VmqI6is6EfYvzhyGztbuJ3XPmcotkCRCveweAAzw2cjDn1w==", - "license": "Apache-2.0", - "dependencies": { - "@exodus/bytes": "^1.15.1", - "@noble/ed25519": "^3.1.0", - "@noble/hashes": "^2.2.0", - "@stellar/js-xdr": "^5.0.0", - "@types/json-schema": "^7.0.15", - "axios": "1.20.0", - "bignumber.js": "^11.1.4", - "commander": "^14.0.3", - "eventsource": "^4.1.0", - "feaxios": "^0.0.23", - "smol-toml": "^1.6.1", - "uint8array-extras": "^1.5.0" - }, - "bin": { - "stellar-js": "bin/stellar-js" - }, - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/@stellar/stellar-sdk/node_modules/@noble/hashes": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz", - "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==", - "license": "MIT", - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@stellar/stellar-sdk/node_modules/bignumber.js": { - "version": "11.1.5", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-11.1.5.tgz", - "integrity": "sha512-6WmzCNtUnfKpbozq+hOgWaZMMzORmYBwF1xZScyoIX3QRYWeKTtxxwDOW5tIz7C9BdjkIYHGTcelCLkXg0mndw==", - "license": "MIT" - }, - "node_modules/@stellar/stellar-sdk/node_modules/eventsource": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-4.1.1.tgz", - "integrity": "sha512-D6bTRWh6KahHTK/m4WnjPQyEinNPf9eFLEZSEoj7d6fTibspnAVYfzHvirL7u/aoX5d9YYfIkBVAhmigUELk9w==", - "license": "MIT", - "dependencies": { - "eventsource-parser": "^3.0.1" - }, - "engines": { - "node": ">=20.0.0" - } - }, "node_modules/@swc/helpers": { "version": "0.5.23", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", @@ -11394,7 +11181,7 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.25.4", @@ -12691,9 +12478,9 @@ } }, "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -12701,6 +12488,10 @@ }, "engines": { "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/proxy-from-env": { @@ -13219,9 +13010,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -13701,9 +13489,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0"