├── .envrc ├── packages ├── core │ ├── tsconfig.json │ ├── src │ │ ├── index.ts │ │ ├── schemas │ │ │ ├── date-range.ts │ │ │ ├── api.ts │ │ │ ├── robinhood-card.ts │ │ │ └── transaction.ts │ │ ├── utils │ │ │ └── arbitrary.ts │ │ ├── dependencies.ts │ │ ├── index.test.ts │ │ └── services │ │ │ ├── api-client.ts │ │ │ ├── graphql.ts │ │ │ ├── robinhood-card │ │ │ ├── index.ts │ │ │ └── get.ts │ │ │ └── finances-aggregator.ts │ └── package.json ├── backend │ ├── tsconfig.json │ ├── src │ │ ├── api │ │ │ ├── index.ts │ │ │ └── transactions.ts │ │ └── index.ts │ └── package.json ├── scripts │ ├── tsconfig.json │ ├── src │ │ ├── schema.ts │ │ └── scratchpad.ts │ └── package.json └── web │ ├── postcss.config.mjs │ ├── src │ ├── app │ │ ├── _components │ │ │ └── transactions │ │ │ │ ├── types.ts │ │ │ │ ├── transactions.module.css │ │ │ │ ├── transaction-pane.tsx │ │ │ │ ├── transactions-chart.tsx │ │ │ │ └── index.tsx │ │ ├── page.tsx │ │ ├── layout.tsx │ │ └── globals.css │ ├── lib │ │ ├── utils.ts │ │ └── view.ts │ ├── react-query │ │ ├── react.tsx │ │ └── query-client.ts │ ├── runtime.ts │ └── components │ │ └── ui │ │ └── select.tsx │ ├── next.config.ts │ ├── .gitignore │ ├── tsconfig.json │ └── package.json ├── vitest.config.ts ├── .vscode └── settings.json ├── README.md ├── .gitignore ├── package.json ├── biome.jsonc ├── tsconfig.json └── bun.lock /.envrc: -------------------------------------------------------------------------------- 1 | dotenv_if_exists .env 2 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/backend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/scripts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/web/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: ["@tailwindcss/postcss"], 3 | }; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | watch: false, 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as Effect from "effect/Effect"; 2 | 3 | export const hello = Effect.fn(function* () { 4 | yield* Effect.log("Hello via Bun!"); 5 | }); 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | 4 | "[javascript][javascriptreact][typescript][typescriptreact][json][jsonc][html][css]": { 5 | "editor.defaultFormatter": "biomejs.biome" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/web/src/app/_components/transactions/types.ts: -------------------------------------------------------------------------------- 1 | import type * as Transaction from "@modo/core/schemas/transaction"; 2 | 3 | export interface SelectedFilters { 4 | date: Date | null; 5 | transaction: Transaction.Transaction | null; 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/src/schemas/date-range.ts: -------------------------------------------------------------------------------- 1 | import * as Schema from "effect/Schema"; 2 | 3 | export const DateRange = Schema.Struct({ 4 | since: Schema.optional(Schema.DateTimeUtc), 5 | until: Schema.optional(Schema.DateTimeUtc), 6 | }); 7 | 8 | export type DateRange = Schema.Schema.Type; 9 | -------------------------------------------------------------------------------- /packages/web/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { Transactions } from "./_components/transactions"; 2 | 3 | export default function Home() { 4 | return ( 5 |
6 |
7 | 8 |
9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/src/utils/arbitrary.ts: -------------------------------------------------------------------------------- 1 | import * as FastCheck from "effect/FastCheck"; 2 | 3 | export const toMock = (arbitrary: FastCheck.Arbitrary): T => { 4 | const [sample] = FastCheck.sample(arbitrary, 1); 5 | // biome-ignore lint/style/noNonNullAssertion: unwrapping the only generated sample 6 | return sample!; 7 | }; 8 | -------------------------------------------------------------------------------- /packages/core/src/dependencies.ts: -------------------------------------------------------------------------------- 1 | import * as Layer from "effect/Layer"; 2 | import * as FinancesAggregator from "./services/finances-aggregator"; 3 | 4 | export const layer = Layer.empty.pipe(Layer.merge(FinancesAggregator.layer)); 5 | 6 | export const developmentLayer = Layer.empty.pipe( 7 | Layer.merge(FinancesAggregator.developmentLayer), 8 | ); 9 | -------------------------------------------------------------------------------- /packages/scripts/src/schema.ts: -------------------------------------------------------------------------------- 1 | import * as BunRuntime from "@effect/platform-bun/BunRuntime"; 2 | import * as Transaction from "@modo/core/schemas/transaction"; 3 | import * as Console from "effect/Console"; 4 | import * as Effect from "effect/Effect"; 5 | 6 | const main = Effect.fn(function* () { 7 | const mock = Transaction.createMock(); 8 | yield* Console.log(mock); 9 | }); 10 | 11 | BunRuntime.runMain(main()); 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # modo 2 | 3 | A web app for viewing transactions with the [Robinhood Gold Card](https://robinhood.com/creditcard/), which only supports viewing transactions through the mobile app. 4 | 5 |

6 | modo 7 |

8 | 9 | > [!NOTE] 10 | > This project is not associated with Robinhood. 11 | -------------------------------------------------------------------------------- /packages/web/next.config.ts: -------------------------------------------------------------------------------- 1 | import NextBundleAnalyzer from "@next/bundle-analyzer"; 2 | import type { NextConfig } from "next"; 3 | 4 | const nextConfig: NextConfig = { 5 | turbopack: { 6 | resolveExtensions: [".mdx", ".tsx", ".ts", ".jsx", ".js", ".json"], 7 | }, 8 | }; 9 | 10 | const withBundleAnalyzer = NextBundleAnalyzer({ 11 | enabled: process.env.ANALYZE === "true", 12 | }); 13 | 14 | export default withBundleAnalyzer(nextConfig); 15 | -------------------------------------------------------------------------------- /packages/web/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | 8 | export const moneyFormatterOptions = { 9 | style: "currency", 10 | currency: "USD", 11 | } as const; 12 | 13 | export const moneyFormatter = new Intl.NumberFormat( 14 | "en-US", 15 | moneyFormatterOptions, 16 | ); 17 | -------------------------------------------------------------------------------- /packages/backend/src/api/index.ts: -------------------------------------------------------------------------------- 1 | import * as HttpLayerRouter from "@effect/platform/HttpLayerRouter"; 2 | import * as Api from "@modo/core/schemas/api"; 3 | import * as Layer from "effect/Layer"; 4 | import { TransactionsApiLayer } from "./transactions"; 5 | 6 | export const HttpApiRoutes = HttpLayerRouter.addHttpApi(Api.ApiSchema).pipe( 7 | Layer.provide(TransactionsApiLayer), 8 | ); 9 | 10 | export const AllRoutes = Layer.mergeAll(HttpApiRoutes).pipe( 11 | Layer.provide(HttpLayerRouter.cors()), 12 | ); 13 | -------------------------------------------------------------------------------- /packages/core/src/index.test.ts: -------------------------------------------------------------------------------- 1 | import { assert, describe, it } from "@effect/vitest"; 2 | import * as Effect from "effect/Effect"; 3 | import * as Exit from "effect/Exit"; 4 | import { hello } from "."; 5 | 6 | describe("core", () => { 7 | it.effect( 8 | "should be defined", 9 | Effect.fn(function* () { 10 | const result = yield* Effect.exit(hello()); 11 | 12 | if (Exit.isFailure(result)) { 13 | return assert.fail("hello should not fail"); 14 | } 15 | }), 16 | ); 17 | }); 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies (bun install) 2 | node_modules 3 | 4 | # output 5 | out 6 | dist 7 | *.tgz 8 | 9 | # code coverage 10 | coverage 11 | *.lcov 12 | 13 | # logs 14 | logs 15 | _.log 16 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 17 | 18 | # dotenv environment variable files 19 | .env 20 | .env.development.local 21 | .env.test.local 22 | .env.production.local 23 | .env.local 24 | 25 | # caches 26 | .eslintcache 27 | .cache 28 | *.tsbuildinfo 29 | 30 | # IntelliJ based IDEs 31 | .idea 32 | 33 | # Finder (MacOS) folder config 34 | .DS_Store 35 | -------------------------------------------------------------------------------- /packages/scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modo/scripts", 3 | "type": "module", 4 | "scripts": { 5 | "clean": "git clean -xdf node_modules", 6 | "typecheck": "tsc --noEmit", 7 | 8 | "scratchpad": "bun run src/scratchpad.ts" 9 | }, 10 | "dependencies": { 11 | "@effect/platform-bun": "catalog:", 12 | "@modo/core": "workspace:*", 13 | "graphql-request": "catalog:", 14 | "effect": "catalog:" 15 | }, 16 | "devDependencies": { 17 | "@types/bun": "catalog:" 18 | }, 19 | "peerDependencies": { 20 | "typescript": "catalog:" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modo/backend", 3 | "module": "src/index.ts", 4 | "type": "module", 5 | "scripts": { 6 | "clean": "git clean -xdf node_modules", 7 | "dev": "bun run --watch src/index.ts", 8 | "typecheck": "tsc --noEmit" 9 | }, 10 | "dependencies": { 11 | "@effect/platform": "catalog:", 12 | "@effect/platform-bun": "catalog:", 13 | "@modo/core": "workspace:*", 14 | "effect": "catalog:" 15 | }, 16 | "devDependencies": { 17 | "@types/bun": "catalog:" 18 | }, 19 | "peerDependencies": { 20 | "typescript": "catalog:" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/web/src/react-query/react.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { QueryClientProvider } from "@tanstack/react-query"; 4 | import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; 5 | import type * as React from "react"; 6 | import { getQueryClient } from "./query-client"; 7 | 8 | export interface ReactQueryProviderProps { 9 | children: React.ReactNode; 10 | } 11 | 12 | export function ReactQueryProvider({ children }: ReactQueryProviderProps) { 13 | const queryClient = getQueryClient(); 14 | 15 | return ( 16 | 17 | {children} 18 | 19 | 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /packages/web/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modo/core", 3 | "exports": { 4 | "./*": [ 5 | "./src/*.ts" 6 | ] 7 | }, 8 | "type": "module", 9 | "scripts": { 10 | "clean": "git clean -xdf node_modules", 11 | "typecheck": "tsc --noEmit", 12 | "test": "vitest run" 13 | }, 14 | "dependencies": { 15 | "@effect/platform": "catalog:", 16 | "@ngneat/falso": "^8.0.1", 17 | "effect": "catalog:", 18 | "graphql": "^16.11.0", 19 | "graphql-request": "catalog:" 20 | }, 21 | "devDependencies": { 22 | "@effect/vitest": "catalog:", 23 | "@types/bun": "catalog:", 24 | "vitest": "catalog:" 25 | }, 26 | "peerDependencies": { 27 | "typescript": "catalog:" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/web/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Geist, Geist_Mono } from "next/font/google"; 3 | import "./globals.css"; 4 | import { ReactQueryProvider } from "@/react-query/react"; 5 | 6 | const geistSans = Geist({ 7 | variable: "--font-geist-sans", 8 | subsets: ["latin"], 9 | }); 10 | 11 | const geistMono = Geist_Mono({ 12 | variable: "--font-geist-mono", 13 | subsets: ["latin"], 14 | }); 15 | 16 | export const metadata: Metadata = { 17 | title: "Modo", 18 | description: "A personal dashboard", 19 | }; 20 | 21 | export default function RootLayout({ 22 | children, 23 | }: Readonly<{ 24 | children: React.ReactNode; 25 | }>) { 26 | return ( 27 | 28 | 31 | {children} 32 | 33 | 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "modo", 3 | "devDependencies": { 4 | "@biomejs/biome": "^2.1.1", 5 | "@effect/language-service": "catalog:", 6 | "vitest": "catalog:" 7 | }, 8 | "private": true, 9 | "scripts": { 10 | "dev": "bun run --filter=* dev", 11 | "clean": "git clean -xdf node_modules && bun run --filter=* clean", 12 | "check": "biome check --write", 13 | "test": "bun run --elide-lines 0 --filter=* test", 14 | "typecheck": "bun run --elide-lines 0 --filter=* typecheck" 15 | }, 16 | "workspaces": { 17 | "packages": [ 18 | "packages/*" 19 | ], 20 | "catalog": { 21 | "@effect/language-service": "^0.24.0", 22 | "@effect/platform": "^0.87.11", 23 | "@effect/platform-bun": "^0.72.15", 24 | "@effect/vitest": "^0.23.12", 25 | "@types/bun": "latest", 26 | "effect": "^3.16.12", 27 | "graphql-request": "^7.2.0", 28 | "typescript": "^5", 29 | "vitest": "^3.2.4" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "target": "ES2017", 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "allowJs": true, 7 | "skipLibCheck": true, 8 | "strict": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "bundler", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "plugins": [ 18 | { 19 | "name": "next" 20 | }, 21 | { 22 | "name": "@effect/language-service", 23 | "namespaceImportPackages": ["effect", "@modo/core"], 24 | "diagnosticSeverity": { 25 | "importFromBarrel": "suggestion" 26 | } 27 | } 28 | ], 29 | "paths": { 30 | "@/*": ["./src/*"] 31 | } 32 | }, 33 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 34 | "exclude": ["node_modules"] 35 | } 36 | -------------------------------------------------------------------------------- /packages/backend/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as HttpLayerRouter from "@effect/platform/HttpLayerRouter"; 2 | import * as BunHttpServer from "@effect/platform-bun/BunHttpServer"; 3 | import * as BunRuntime from "@effect/platform-bun/BunRuntime"; 4 | import * as Dependencies from "@modo/core/dependencies"; 5 | import * as Effect from "effect/Effect"; 6 | import * as Layer from "effect/Layer"; 7 | import { AllRoutes } from "./api"; 8 | 9 | const layer = HttpLayerRouter.serve(AllRoutes).pipe( 10 | Layer.provide(Dependencies.layer), 11 | Layer.provide(BunHttpServer.layer({ port: 3000 })), 12 | ); 13 | 14 | const developmentLayer = HttpLayerRouter.serve(AllRoutes).pipe( 15 | Layer.provide(Dependencies.developmentLayer), 16 | Layer.provide(BunHttpServer.layer({ port: 3000 })), 17 | ); 18 | 19 | const main = Effect.fn(function* () { 20 | if (process.env.NODE_ENV === "development") { 21 | return yield* Layer.launch(developmentLayer); 22 | } 23 | return yield* Layer.launch(layer); 24 | }); 25 | 26 | BunRuntime.runMain(main()); 27 | -------------------------------------------------------------------------------- /biome.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/2.1.1/schema.json", 3 | "vcs": { 4 | "enabled": false, 5 | "clientKind": "git", 6 | "useIgnoreFile": false 7 | }, 8 | "files": { 9 | "ignoreUnknown": false 10 | }, 11 | "formatter": { 12 | "enabled": true, 13 | "includes": ["packages/**", "*.js", "*.ts", "*.json", "!**/.next"], 14 | "indentStyle": "space", 15 | "indentWidth": 2 16 | }, 17 | "linter": { 18 | "enabled": true, 19 | "includes": ["packages/**", "*.js", "*.ts", "*.json", "!**/.next"], 20 | "domains": { 21 | "next": "recommended" 22 | }, 23 | "rules": { 24 | "recommended": true, 25 | "suspicious": { 26 | "noShadowRestrictedNames": "off" 27 | }, 28 | "nursery": { 29 | "useSortedClasses": "error" 30 | } 31 | } 32 | }, 33 | "javascript": { 34 | "formatter": { 35 | "quoteStyle": "double" 36 | } 37 | }, 38 | "assist": { 39 | "enabled": true, 40 | "includes": ["packages/**", "*.js", "*.ts", "*.json", "!**/.next"], 41 | "actions": { 42 | "source": { 43 | "organizeImports": "on" 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/core/src/schemas/api.ts: -------------------------------------------------------------------------------- 1 | import * as HttpApi from "@effect/platform/HttpApi"; 2 | import * as HttpApiEndpoint from "@effect/platform/HttpApiEndpoint"; 3 | import * as HttpApiGroup from "@effect/platform/HttpApiGroup"; 4 | import * as HttpApiSchema from "@effect/platform/HttpApiSchema"; 5 | import * as Schema from "effect/Schema"; 6 | import * as DateRange from "./date-range"; 7 | import * as Transaction from "./transaction"; 8 | 9 | export class InternalServerError extends Schema.TaggedError()( 10 | "InternalServerError", 11 | { 12 | message: Schema.String, 13 | }, 14 | HttpApiSchema.annotations({ status: 500 }), 15 | ) {} 16 | 17 | const customerIdParam = HttpApiSchema.param( 18 | "customerId", 19 | Schema.NonEmptyString, 20 | ); 21 | 22 | export class ApiSchema extends HttpApi.make("api") 23 | .add( 24 | HttpApiGroup.make("transactions") 25 | .add( 26 | HttpApiEndpoint.get("listForCustomer")`/${customerIdParam}` 27 | .addSuccess(Schema.Array(Transaction.Transaction)) 28 | .setUrlParams(DateRange.DateRange), 29 | ) 30 | .prefix("/transactions"), 31 | ) 32 | .addError(InternalServerError) {} 33 | -------------------------------------------------------------------------------- /packages/web/src/react-query/query-client.ts: -------------------------------------------------------------------------------- 1 | import { 2 | defaultShouldDehydrateQuery, 3 | isServer, 4 | QueryClient, 5 | } from "@tanstack/react-query"; 6 | 7 | function makeQueryClient() { 8 | return new QueryClient({ 9 | defaultOptions: { 10 | queries: { 11 | staleTime: 60 * 1000, 12 | }, 13 | dehydrate: { 14 | // include pending queries in dehydration 15 | shouldDehydrateQuery: (query) => 16 | defaultShouldDehydrateQuery(query) || 17 | query.state.status === "pending", 18 | }, 19 | }, 20 | }); 21 | } 22 | 23 | let browserQueryClient: QueryClient | undefined; 24 | 25 | export function getQueryClient() { 26 | if (isServer) { 27 | // Server: always make a new query client 28 | return makeQueryClient(); 29 | } else { 30 | // Browser: make a new query client if we don't already have one 31 | // This is very important, so we don't re-make a new client if React 32 | // suspends during the initial render. This may not be needed if we 33 | // have a suspense boundary BELOW the creation of the query client 34 | if (!browserQueryClient) browserQueryClient = makeQueryClient(); 35 | return browserQueryClient; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/backend/src/api/transactions.ts: -------------------------------------------------------------------------------- 1 | import * as HttpApiBuilder from "@effect/platform/HttpApiBuilder"; 2 | import * as Api from "@modo/core/schemas/api"; 3 | import type * as DateRange from "@modo/core/schemas/date-range"; 4 | import * as FinancesAggregator from "@modo/core/services/finances-aggregator"; 5 | import * as Effect from "effect/Effect"; 6 | 7 | const listForCustomer = Effect.fn(function* ( 8 | customerId: string, 9 | range: DateRange.DateRange, 10 | ) { 11 | const financesAggregator = yield* FinancesAggregator.FinancesAggregator; 12 | const transactions = yield* financesAggregator.listTransactionsForCustomer( 13 | customerId, 14 | range, 15 | ); 16 | return transactions; 17 | }); 18 | 19 | export const TransactionsApiLayer = HttpApiBuilder.group( 20 | Api.ApiSchema, 21 | "transactions", 22 | (handers) => 23 | handers.handle("listForCustomer", (req) => 24 | listForCustomer(req.path.customerId, req.urlParams).pipe( 25 | Effect.catchAll( 26 | () => 27 | new Api.InternalServerError({ 28 | message: 29 | "An unexpected occurred while retrieving the transactions.", 30 | }), 31 | ), 32 | ), 33 | ), 34 | ); 35 | -------------------------------------------------------------------------------- /packages/web/src/runtime.ts: -------------------------------------------------------------------------------- 1 | import * as FetchHttpClient from "@effect/platform/FetchHttpClient"; 2 | import * as ApiClient from "@modo/core/services/api-client"; 3 | import * as Layer from "effect/Layer"; 4 | import * as ManagedRuntime from "effect/ManagedRuntime"; 5 | 6 | const productionLayer = Layer.empty.pipe( 7 | Layer.merge(ApiClient.layer({ baseUrl: "TODO" })), 8 | Layer.provide(FetchHttpClient.layer), 9 | ); 10 | 11 | const developmentLayer = Layer.empty.pipe( 12 | Layer.merge(ApiClient.developmentLayer), 13 | Layer.provide(FetchHttpClient.layer), 14 | ); 15 | 16 | const testLayer = Layer.empty.pipe( 17 | Layer.merge(ApiClient.testLayer), 18 | Layer.provide(FetchHttpClient.layer), 19 | ); 20 | 21 | export const RuntimeClient = 22 | process.env.NODE_ENV === "production" 23 | ? ManagedRuntime.make(productionLayer) 24 | : process.env.NODE_ENV === "test" 25 | ? ManagedRuntime.make(testLayer) 26 | : ManagedRuntime.make(developmentLayer); 27 | 28 | export const RuntimeServer = 29 | process.env.NODE_ENV === "production" 30 | ? ManagedRuntime.make(productionLayer) 31 | : process.env.NODE_ENV === "test" 32 | ? ManagedRuntime.make(testLayer) 33 | : ManagedRuntime.make(developmentLayer); 34 | -------------------------------------------------------------------------------- /packages/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "clean": "git clean -xdf node_modules .next", 7 | "dev": "next dev --turbopack --port 3001", 8 | "build": "next build", 9 | "start": "next start", 10 | "typecheck": "tsc --noEmit", 11 | "lint": "next lint" 12 | }, 13 | "dependencies": { 14 | "@base-ui-components/react": "^1.0.0-beta.1", 15 | "@effect/platform": "catalog:", 16 | "@modo/core": "workspace:*", 17 | "@next/bundle-analyzer": "^15.3.5", 18 | "@number-flow/react": "^0.5.10", 19 | "@tanstack/react-query": "^5.82.0", 20 | "@tanstack/react-query-devtools": "^5.82.0", 21 | "clsx": "^2.1.1", 22 | "effect": "catalog:", 23 | "next": "^15.4.0-canary.127", 24 | "react": "^19.0.0", 25 | "react-dom": "^19.0.0", 26 | "recharts": "^2", 27 | "tailwind-merge": "^3.3.1", 28 | "tw-animate-css": "^1.3.5" 29 | }, 30 | "devDependencies": { 31 | "@effect/language-service": "catalog:", 32 | "@tailwindcss/postcss": "^4", 33 | "@types/node": "^20", 34 | "@types/react": "^19", 35 | "@types/react-dom": "^19", 36 | "tailwindcss": "^4", 37 | "typescript": "catalog:" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // Environment setup & latest features 4 | "lib": ["ESNext"], 5 | "target": "ESNext", 6 | "module": "Preserve", 7 | "moduleDetection": "force", 8 | "jsx": "react-jsx", 9 | "allowJs": true, 10 | 11 | // Bundler mode 12 | "moduleResolution": "bundler", 13 | "allowImportingTsExtensions": true, 14 | "verbatimModuleSyntax": true, 15 | "noEmit": true, 16 | 17 | // Best practices 18 | "strict": true, 19 | "skipLibCheck": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "noUncheckedIndexedAccess": true, 22 | "noImplicitOverride": true, 23 | 24 | // Some stricter flags (disabled by default) 25 | "noUnusedLocals": false, 26 | "noUnusedParameters": false, 27 | "noPropertyAccessFromIndexSignature": false, 28 | "exactOptionalPropertyTypes": true, 29 | 30 | // Plugins 31 | "plugins": [ 32 | { 33 | "name": "@effect/language-service", 34 | "namespaceImportPackages": [ 35 | "effect", 36 | "@effect/platform", 37 | "@effect/platform-bun" 38 | ], 39 | "diagnosticSeverity": { 40 | "importFromBarrel": "suggestion" 41 | } 42 | } 43 | ] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/web/src/app/_components/transactions/transactions.module.css: -------------------------------------------------------------------------------- 1 | .transactions-mask { 2 | --mask-size: 80px; 3 | --mask-range: 60px; 4 | 5 | mask: 6 | linear-gradient(to bottom, black, transparent) 0 0, 7 | linear-gradient(to bottom, transparent, black) 0 100%, 8 | linear-gradient(to bottom, black, black) 0 100%; 9 | mask-size: 10 | 100% 0px, 11 | 100% 0px, 12 | 100% 100%; 13 | mask-repeat: no-repeat; 14 | mask-composite: exclude; 15 | 16 | animation: mask-down, mask-up; 17 | animation-fill-mode: backwards, backwards; 18 | animation-timeline: scroll(self), scroll(self); 19 | animation-range: 20 | calc(100% - var(--mask-range)) 100%, 21 | 0px var(--mask-range); 22 | } 23 | 24 | @keyframes mask-up { 25 | from { 26 | mask-size: 27 | 100% 0px, 28 | 100% var(--mask-size), 29 | 100% 100%; 30 | } 31 | to { 32 | mask-size: 33 | 100% var(--mask-size), 34 | 100% var(--mask-size), 35 | 100% 100%; 36 | } 37 | } 38 | 39 | @keyframes mask-down { 40 | from { 41 | mask-size: 42 | 100% var(--mask-size), 43 | 100% var(--mask-size), 44 | 100% 100%; 45 | } 46 | to { 47 | mask-size: 48 | 100% var(--mask-size), 49 | 100% 0px, 50 | 100% 100%; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/core/src/services/api-client.ts: -------------------------------------------------------------------------------- 1 | import type * as HttpApi from "@effect/platform/HttpApi"; 2 | import * as HttpApiClient from "@effect/platform/HttpApiClient"; 3 | import * as Context from "effect/Context"; 4 | import * as Effect from "effect/Effect"; 5 | import * as Layer from "effect/Layer"; 6 | import * as Api from "../schemas/api"; 7 | 8 | export interface Config { 9 | baseUrl?: string; 10 | } 11 | 12 | type ClientService = T extends HttpApi.HttpApi< 13 | string, 14 | infer Groups, 15 | infer E, 16 | infer R 17 | > 18 | ? HttpApiClient.Client 19 | : never; 20 | 21 | export interface Service extends ClientService {} 22 | 23 | const contextKey = "@modo/ApiClient"; 24 | 25 | export class ApiClient extends Context.Tag(contextKey)() {} 26 | 27 | /** 28 | * Creates a new API client. 29 | * 30 | * @param config - The configuration for the API client. 31 | * @returns An effect that returns the API client service. 32 | */ 33 | export const make = Effect.fn(function* (config: Config) { 34 | const client = yield* HttpApiClient.make(Api.ApiSchema, { 35 | baseUrl: config.baseUrl, 36 | }); 37 | return ApiClient.of(client); 38 | }); 39 | 40 | export const layer = (config: Config) => Layer.effect(ApiClient, make(config)); 41 | 42 | export const developmentLayer = layer({ baseUrl: "http://localhost:3000" }); 43 | 44 | export const testLayer = layer({ baseUrl: "http://localhost:3000" }); 45 | -------------------------------------------------------------------------------- /packages/scripts/src/scratchpad.ts: -------------------------------------------------------------------------------- 1 | import * as BunRuntime from "@effect/platform-bun/BunRuntime"; 2 | import * as RobinhoodCard from "@modo/core/services/robinhood-card/index"; 3 | import * as Config from "effect/Config"; 4 | import * as Console from "effect/Console"; 5 | import * as DateTime from "effect/DateTime"; 6 | import * as Effect from "effect/Effect"; 7 | import * as HashSet from "effect/HashSet"; 8 | 9 | const main = Effect.fn(function* () { 10 | const config = yield* Config.all({ 11 | token: Config.redacted("ROBINHOOD_TOKEN"), 12 | }); 13 | const robinhoodCard = yield* RobinhoodCard.make(config); 14 | const customerIds = yield* robinhoodCard.getCustomerIds(); 15 | yield* Console.log(customerIds); 16 | 17 | const primaryCustomerId = customerIds[0]; 18 | if (!primaryCustomerId) { 19 | return yield* Effect.die("Account has no customer IDs"); 20 | } 21 | 22 | const now = yield* DateTime.now; 23 | const lastMonth = DateTime.subtract(now, { days: 7 }); 24 | const transactions = yield* robinhoodCard.getTransactions(primaryCustomerId, { 25 | since: lastMonth, 26 | until: now, 27 | }); 28 | 29 | const customerIdsFromTransactions = HashSet.fromIterable( 30 | transactions.map((t) => t.links.creditCustomer.id), 31 | ); 32 | yield* Console.log(customerIdsFromTransactions); 33 | 34 | for (const transaction of transactions) { 35 | yield* Console.log( 36 | transaction.transactionAt, 37 | transaction.amountMicro / 1_000_000, 38 | transaction.pointEarnings, 39 | transaction.merchantDetails.merchantName, 40 | ); 41 | } 42 | }); 43 | 44 | BunRuntime.runMain(main()); 45 | -------------------------------------------------------------------------------- /packages/core/src/services/graphql.ts: -------------------------------------------------------------------------------- 1 | import * as Context from "effect/Context"; 2 | import * as Effect from "effect/Effect"; 3 | import * as Schema from "effect/Schema"; 4 | import { GraphQLClient } from "graphql-request"; 5 | 6 | export class GraphQLUnexpectedError extends Schema.TaggedError()( 7 | "GraphQLUnexpectedError", 8 | { cause: Schema.Defect }, 9 | ) {} 10 | 11 | type RequestConfig = NonNullable< 12 | ConstructorParameters[1] 13 | >; 14 | 15 | export interface ServiceConfig { 16 | url: string; 17 | requestConfig?: RequestConfig; 18 | } 19 | 20 | const contextKey = "@modo/GraphQL"; 21 | 22 | export interface Service { 23 | use: ( 24 | fn: (client: GraphQLClient) => PromiseLike, 25 | ) => Effect.Effect; 26 | } 27 | 28 | export class GraphQL extends Context.Tag(contextKey)() {} 29 | 30 | /** 31 | * Creates a new GraphQL client. 32 | * 33 | * @param config - The configuration for the GraphQL client. 34 | * @returns An effect that returns the GraphQL client. 35 | */ 36 | export const make = Effect.fn(function* (config: ServiceConfig) { 37 | const client = yield* Effect.sync( 38 | () => new GraphQLClient(config.url, config.requestConfig), 39 | ); 40 | 41 | return GraphQL.of({ 42 | use: Effect.fn("GraphQL.use")(function* (fn) { 43 | const result = yield* Effect.try({ 44 | try: () => fn(client), 45 | catch: (err) => new GraphQLUnexpectedError({ cause: err }), 46 | }); 47 | 48 | return yield* Effect.tryPromise({ 49 | try: () => result, 50 | catch: (err) => new GraphQLUnexpectedError({ cause: err }), 51 | }); 52 | }), 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /packages/web/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | @import "tw-animate-css"; 3 | 4 | :root { 5 | --background: #ffffff; 6 | --foreground: #000000; 7 | 8 | /* Gray */ 9 | --gray1: hsl(0, 0%, 99.0%); 10 | --gray2: hsl(0, 0%, 97.3%); 11 | --gray3: hsl(0, 0%, 95.1%); 12 | --gray4: hsl(0, 0%, 93.0%); 13 | --gray5: hsl(0, 0%, 90.9%); 14 | --gray6: hsl(0, 0%, 88.7%); 15 | --gray7: hsl(0, 0%, 85.8%); 16 | --gray8: hsl(0, 0%, 78.0%); 17 | --gray9: hsl(0, 0%, 56.1%); 18 | --gray10: hsl(0, 0%, 52.3%); 19 | --gray11: hsl(0, 0%, 43.5%); 20 | --gray12: hsl(0, 0%, 9.0%); 21 | } 22 | 23 | .dark { 24 | --background: #000000; 25 | --foreground: #ffffff; 26 | 27 | /* Gray */ 28 | --gray1: hsl(0, 0%, 8.5%); 29 | --gray2: hsl(0, 0%, 11.0%); 30 | --gray3: hsl(0, 0%, 13.6%); 31 | --gray4: hsl(0, 0%, 15.8%); 32 | --gray5: hsl(0, 0%, 17.9%); 33 | --gray6: hsl(0, 0%, 20.5%); 34 | --gray7: hsl(0, 0%, 24.3%); 35 | --gray8: hsl(0, 0%, 31.2%); 36 | --gray9: hsl(0, 0%, 43.9%); 37 | --gray10: hsl(0, 0%, 49.4%); 38 | --gray11: hsl(0, 0%, 62.8%); 39 | --gray12: hsl(0, 0%, 93.0%); 40 | } 41 | 42 | @theme inline { 43 | --color-background: var(--background); 44 | --color-foreground: var(--foreground); 45 | 46 | /* Gray */ 47 | --color-gray1: var(--gray1); 48 | --color-gray2: var(--gray2); 49 | --color-gray3: var(--gray3); 50 | --color-gray4: var(--gray4); 51 | --color-gray5: var(--gray5); 52 | --color-gray6: var(--gray6); 53 | --color-gray7: var(--gray7); 54 | --color-gray8: var(--gray8); 55 | --color-gray9: var(--gray9); 56 | --color-gray10: var(--gray10); 57 | --color-gray11: var(--gray11); 58 | --color-gray12: var(--gray12); 59 | 60 | --font-sans: var(--font-geist-sans); 61 | --font-mono: var(--font-geist-mono); 62 | } 63 | 64 | @layer base { 65 | body { 66 | @apply bg-background text-foreground; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /packages/web/src/app/_components/transactions/transaction-pane.tsx: -------------------------------------------------------------------------------- 1 | import type * as Transaction from "@modo/core/schemas/transaction"; 2 | import * as React from "react"; 3 | import { cn, moneyFormatter } from "@/lib/utils"; 4 | import type { SelectedFilters } from "./types"; 5 | 6 | export interface TransactionPaneProps { 7 | transaction: Transaction.Transaction; 8 | setSelectedFilters: React.Dispatch>; 9 | } 10 | 11 | export function TransactionPane({ 12 | transaction, 13 | setSelectedFilters, 14 | }: TransactionPaneProps) { 15 | const isPending = transaction.status === "pending"; 16 | const isRefund = transaction.status === "reversed"; 17 | const amount = transaction.amountMicro / 1_000_000; 18 | const cashbackAmount = transaction.cashbackAmountMicro / 1_000_000; 19 | 20 | return ( 21 | 58 | ); 59 | } 60 | 61 | export const MemoizedTransactionPane = React.memo(TransactionPane); 62 | -------------------------------------------------------------------------------- /packages/core/src/schemas/robinhood-card.ts: -------------------------------------------------------------------------------- 1 | import * as Schema from "effect/Schema"; 2 | 3 | export const CreditCustomer = Schema.Struct({ 4 | id: Schema.String, 5 | creditCustomerId: Schema.String, 6 | name: Schema.Struct({ 7 | id: Schema.String, 8 | firstName: Schema.String, 9 | lastName: Schema.String, 10 | }), 11 | }); 12 | 13 | export const TransactionFlow = Schema.Union( 14 | Schema.Literal("INBOUND"), 15 | Schema.Literal("OUTBOUND"), 16 | ); 17 | 18 | export const TransactionStatus = Schema.Union( 19 | Schema.Literal("DECLINED"), 20 | Schema.Literal("EXPIRED"), 21 | Schema.Literal("PENDING"), 22 | Schema.Literal("POSTED"), 23 | Schema.Literal("REVERSED"), 24 | ); 25 | 26 | export const RedemptionStatus = Schema.Union( 27 | Schema.Literal("NOT_REDEEMABLE"), 28 | Schema.Literal("REDEEMABLE"), 29 | ); 30 | 31 | export const TransactionType = Schema.Union( 32 | Schema.Literal("BALANCE_HOLD"), 33 | Schema.Literal("FEE"), 34 | Schema.Literal("INSTALLMENT_LOAN_ADJUSTMENT"), 35 | Schema.Literal("PAYMENT"), 36 | Schema.Literal("PURCHASE"), 37 | Schema.Literal("REFUND"), 38 | ); 39 | 40 | export const TransactionVisibility = Schema.Union( 41 | Schema.Literal("HIDDEN_AUTO"), 42 | Schema.Literal("VISIBLE"), 43 | ); 44 | 45 | export const DisputeEligibility = Schema.Union( 46 | Schema.Literal("ELIGIBLE"), 47 | Schema.Literal("ELIGIBLE_FRAUD_ONLY"), 48 | Schema.Literal("INELIGIBLE"), 49 | Schema.Literal("NOT_ELIGIBLE"), 50 | Schema.Literal("NOT_ELIGIBLE_PAST_CUTOFF"), 51 | ); 52 | 53 | export const InstallmentLoanEligibility = Schema.Union( 54 | Schema.Literal("ELIGIBLE"), 55 | Schema.Literal("INELIGIBLE"), 56 | ); 57 | 58 | export const Transaction = Schema.Struct({ 59 | id: Schema.String, 60 | amountMicro: Schema.Number, 61 | originalAmountMicro: Schema.Number, 62 | flow: TransactionFlow, 63 | transactionStatus: TransactionStatus, 64 | redemptionStatus: RedemptionStatus, 65 | transactionType: TransactionType, 66 | transactionAt: Schema.Number, 67 | visibility: TransactionVisibility, 68 | merchantDetails: Schema.Struct({ 69 | merchantName: Schema.String, 70 | logoUrl: Schema.String, 71 | }), 72 | pointEarnings: Schema.optional(Schema.Number), 73 | pointMultiplier: Schema.optional(Schema.Number), 74 | links: Schema.Struct({ 75 | id: Schema.String, 76 | paymentId: Schema.optional(Schema.String), 77 | creditCustomer: CreditCustomer, 78 | }), 79 | disputeDetails: Schema.Struct({ 80 | eligibility: DisputeEligibility, 81 | }), 82 | installmentLoanEligibility: InstallmentLoanEligibility, 83 | }); 84 | 85 | export type Transaction = Schema.Schema.Type; 86 | -------------------------------------------------------------------------------- /packages/core/src/schemas/transaction.ts: -------------------------------------------------------------------------------- 1 | import { randCompanyName } from "@ngneat/falso"; 2 | import * as Arbitrary from "effect/Arbitrary"; 3 | import * as ParseResult from "effect/ParseResult"; 4 | import * as Schema from "effect/Schema"; 5 | import * as ArbitraryUtils from "../utils/arbitrary"; 6 | import * as RobinhoodCard from "./robinhood-card"; 7 | 8 | export const Status = Schema.Union( 9 | Schema.Literal("posted"), 10 | Schema.Literal("pending"), 11 | Schema.Literal("reversed"), 12 | ); 13 | 14 | const arbitraryMicro = 15 | (min: number, max: number): Arbitrary.LazyArbitrary => 16 | (fc) => 17 | fc 18 | .noBias(fc.integer({ min, max })) // Amount in cents 19 | .map((value) => value / 100) // Turn to dollars 20 | .map((value) => value * 1_000_000); 21 | 22 | export const AmountMicro = Schema.Int.annotations({ 23 | arbitrary: () => arbitraryMicro(-200_00, 1000_00), // -$200 to $1000 24 | }); 25 | 26 | export const CashbackAmountMicro = Schema.Int.annotations({ 27 | arbitrary: () => arbitraryMicro(0, 10_00), // $0 to $10 28 | }); 29 | 30 | export const Transaction = Schema.Struct({ 31 | id: Schema.NonEmptyString.annotations({ 32 | arbitrary: () => (fc) => fc.uuid().map((value) => `transaction-${value}`), 33 | }), 34 | date: Schema.Date.annotations({ 35 | arbitrary: () => (fc) => 36 | fc.noBias( 37 | fc.date({ 38 | min: new Date(2024, 0, 1), 39 | max: new Date(), 40 | }), 41 | ), 42 | }), 43 | amountMicro: AmountMicro, 44 | cashbackAmountMicro: CashbackAmountMicro, 45 | merchantName: Schema.NonEmptyString.annotations({ 46 | arbitrary: () => (fc) => fc.constant(null).map(() => randCompanyName()), 47 | }), 48 | status: Status, 49 | }); 50 | 51 | export type Transaction = Schema.Schema.Type; 52 | 53 | export const createArbitrary = (overrides?: Partial) => 54 | Arbitrary.make(Transaction).map((transaction) => ({ 55 | ...transaction, 56 | ...overrides, 57 | })); 58 | 59 | export const createMock = (overrides?: Partial) => 60 | ArbitraryUtils.toMock(createArbitrary(overrides)); 61 | 62 | export const TransactionFromRobinhood = Schema.transformOrFail( 63 | RobinhoodCard.Transaction, 64 | Transaction, 65 | { 66 | strict: true, 67 | decode: (input) => { 68 | const parsed = { 69 | id: input.id, 70 | date: new Date(input.transactionAt).toString(), 71 | amountMicro: input.amountMicro, 72 | cashbackAmountMicro: (input.pointEarnings ?? 0) * 10_000, // 100 points = $1 73 | merchantName: input.merchantDetails.merchantName, 74 | status: 75 | input.transactionStatus === "POSTED" 76 | ? ("posted" as const) 77 | : input.transactionStatus === "REVERSED" 78 | ? ("reversed" as const) 79 | : ("pending" as const), 80 | }; 81 | return ParseResult.succeed(parsed); 82 | }, 83 | encode: (input, _options, ast) => 84 | ParseResult.fail( 85 | new ParseResult.Forbidden( 86 | ast, 87 | input, 88 | "Cannot encode Transaction to RobinhoodCard.Transaction", 89 | ), 90 | ), 91 | }, 92 | ); 93 | -------------------------------------------------------------------------------- /packages/core/src/services/robinhood-card/index.ts: -------------------------------------------------------------------------------- 1 | import * as Config from "effect/Config"; 2 | import * as Context from "effect/Context"; 3 | import * as Effect from "effect/Effect"; 4 | import * as Layer from "effect/Layer"; 5 | import * as Redacted from "effect/Redacted"; 6 | import * as Schema from "effect/Schema"; 7 | import type * as DateRange from "../../schemas/date-range"; 8 | import type * as RobinhoodCardSchema from "../../schemas/robinhood-card"; 9 | import * as GraphQL from "../graphql"; 10 | import { getCustomerIds, getTransactions } from "./get"; 11 | 12 | export class RobinhoodCardSchemaError extends Schema.TaggedError()( 13 | "RobinhoodCardSchemaError", 14 | { cause: Schema.Defect }, 15 | ) {} 16 | 17 | export class RobinhoodCardUnexpectedError extends Schema.TaggedError()( 18 | "RobinhoodCardUnexpectedError", 19 | { cause: Schema.Defect }, 20 | ) {} 21 | 22 | export interface ServiceConfig { 23 | baseUrl?: string; 24 | token: Redacted.Redacted; 25 | } 26 | 27 | const contextKey = "@modo/RobinhoodCard"; 28 | 29 | export interface Service { 30 | getCustomerIds(): Effect.Effect< 31 | string[], 32 | RobinhoodCardSchemaError | RobinhoodCardUnexpectedError 33 | >; 34 | 35 | getTransactions( 36 | customerId: string, 37 | dateRange: DateRange.DateRange, 38 | ): Effect.Effect< 39 | RobinhoodCardSchema.Transaction[], 40 | RobinhoodCardSchemaError | RobinhoodCardUnexpectedError 41 | >; 42 | } 43 | 44 | export class RobinhoodCard extends Context.Tag(contextKey)< 45 | RobinhoodCard, 46 | Service 47 | >() {} 48 | 49 | /** 50 | * Creates a new Robinhood card service. 51 | * 52 | * @param config - The configuration for the Robinhood card service. 53 | * @returns An effect that returns the Robinhood card service. 54 | */ 55 | export const make = Effect.fn(function* (config: ServiceConfig) { 56 | const baseUrl = config.baseUrl ?? "https://api.robinhood.com/creditcard"; 57 | 58 | const robinhoodGraphQL = yield* GraphQL.make({ 59 | url: `${baseUrl}/graphql`, 60 | requestConfig: { 61 | method: "POST", 62 | headers: { 63 | Authorization: `Bearer ${Redacted.value(config.token)}`, 64 | }, 65 | }, 66 | }); 67 | 68 | return RobinhoodCard.of({ 69 | getCustomerIds() { 70 | return getCustomerIds().pipe( 71 | Effect.provideService(GraphQL.GraphQL, robinhoodGraphQL), 72 | Effect.catchTags({ 73 | ParseError: (err) => new RobinhoodCardSchemaError({ cause: err }), 74 | GraphQLUnexpectedError: (err) => 75 | new RobinhoodCardUnexpectedError({ cause: err }), 76 | }), 77 | ); 78 | }, 79 | 80 | getTransactions(customerId, options) { 81 | return getTransactions(customerId, options).pipe( 82 | Effect.provideService(GraphQL.GraphQL, robinhoodGraphQL), 83 | Effect.catchTags({ 84 | ParseError: (err) => new RobinhoodCardSchemaError({ cause: err }), 85 | GraphQLUnexpectedError: (err) => 86 | new RobinhoodCardUnexpectedError({ cause: err }), 87 | }), 88 | ); 89 | }, 90 | }); 91 | }); 92 | 93 | export const layer = (config: ServiceConfig) => 94 | Layer.effect(RobinhoodCard, make(config)); 95 | 96 | export const layerFromEnv = Layer.effect( 97 | RobinhoodCard, 98 | Effect.gen(function* () { 99 | const config = yield* Config.all({ 100 | token: Config.redacted("ROBINHOOD_TOKEN"), 101 | }); 102 | return yield* make(config); 103 | }), 104 | ); 105 | -------------------------------------------------------------------------------- /packages/core/src/services/finances-aggregator.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "effect"; 2 | import * as Array from "effect/Array"; 3 | import * as DateTime from "effect/DateTime"; 4 | import * as Effect from "effect/Effect"; 5 | import * as Layer from "effect/Layer"; 6 | import * as Order from "effect/Order"; 7 | import * as Schedule from "effect/Schedule"; 8 | import * as Schema from "effect/Schema"; 9 | import type * as DateRange from "../schemas/date-range"; 10 | import * as Transaction from "../schemas/transaction"; 11 | import * as RobinhoodCard from "./robinhood-card"; 12 | 13 | export type ListTransactionsError = 14 | | RobinhoodCard.RobinhoodCardSchemaError 15 | | RobinhoodCard.RobinhoodCardUnexpectedError; 16 | 17 | export interface Service { 18 | listTransactionsForCustomer( 19 | customerId: string, 20 | range: DateRange.DateRange, 21 | ): Effect.Effect; 22 | } 23 | 24 | const serviceKey = "@modo/FinancesAggregator"; 25 | 26 | export class FinancesAggregator extends Effect.Service()( 27 | serviceKey, 28 | { 29 | effect: Effect.gen(function* () { 30 | const robinhoodCard = yield* RobinhoodCard.RobinhoodCard; 31 | 32 | const listTransactionsForCustomer = Effect.fn(function* ( 33 | id: string, 34 | range: DateRange.DateRange, 35 | ) { 36 | const getTransactions = robinhoodCard.getTransactions(id, range); 37 | const transactionsRaw = yield* Effect.retry(getTransactions, { 38 | while: (e) => e._tag === "RobinhoodCardUnexpectedError", 39 | schedule: Schedule.exponential(1000), 40 | times: 3, 41 | }); 42 | const transactionsRawFiltered = Array.filter( 43 | transactionsRaw, 44 | (t) => 45 | t.visibility === "VISIBLE" && 46 | (t.transactionType === "PURCHASE" || 47 | t.transactionType === "REFUND"), 48 | ); 49 | const [errors, transactions] = yield* Effect.partition( 50 | transactionsRawFiltered, 51 | (t) => Schema.decode(Transaction.TransactionFromRobinhood)(t), 52 | ); 53 | yield* Effect.forEach(errors, (e) => Effect.logInfo(e)); 54 | return transactions; 55 | }); 56 | 57 | return { 58 | listTransactionsForCustomer, 59 | } satisfies Service; 60 | }), 61 | dependencies: [RobinhoodCard.layerFromEnv], 62 | }, 63 | ) {} 64 | 65 | export const layer = FinancesAggregator.Default; 66 | 67 | export const developmentLayer = Layer.effect( 68 | FinancesAggregator, 69 | Effect.gen(function* () { 70 | const transactionsByCustomerId = new Map< 71 | string, 72 | Transaction.Transaction[] 73 | >(); 74 | 75 | const listTransactionsForCustomer = Effect.fn(function* ( 76 | customerId: string, 77 | range: DateRange.DateRange, 78 | ) { 79 | const transactions = 80 | transactionsByCustomerId.get(customerId) ?? 81 | pipe( 82 | Array.makeBy(200, () => Transaction.createMock()), 83 | Array.sortWith((t) => t.date, Order.reverse(Order.Date)), 84 | ); 85 | yield* Effect.sync(() => 86 | transactionsByCustomerId.set(customerId, transactions), 87 | ); 88 | 89 | const since = range.since ?? DateTime.unsafeMake(0); 90 | const until = range.until ?? (yield* DateTime.now); 91 | const transactionsFiltered = Array.filter(transactions, (t) => { 92 | const dateTime = DateTime.unsafeMake(t.date); 93 | return ( 94 | DateTime.lessThanOrEqualTo(since, dateTime) && 95 | DateTime.lessThan(dateTime, until) 96 | ); 97 | }); 98 | return transactionsFiltered; 99 | }); 100 | 101 | return yield* Effect.succeed( 102 | new FinancesAggregator({ 103 | listTransactionsForCustomer, 104 | }), 105 | ); 106 | }), 107 | ); 108 | -------------------------------------------------------------------------------- /packages/web/src/lib/view.ts: -------------------------------------------------------------------------------- 1 | import type * as DateRange from "@modo/core/schemas/date-range"; 2 | import * as Array from "effect/Array"; 3 | import * as Data from "effect/Data"; 4 | import * as DateTime from "effect/DateTime"; 5 | 6 | export type View = Data.TaggedEnum<{ 7 | Year: { year: number }; 8 | Month: { year: number; month: number }; 9 | }>; 10 | 11 | export const View = Data.taggedEnum(); 12 | 13 | export const hasPrevious = (view: View) => { 14 | return View.$match(view, { 15 | Year: ({ year }) => year > 1970, 16 | Month: ({ year, month }) => month > 0 || year > 1970, 17 | }); 18 | }; 19 | 20 | export const getPrevious = (view: View) => { 21 | return View.$match(view, { 22 | Year: ({ year }) => View.Year({ year: year - 1 }), 23 | Month: ({ year, month }) => 24 | month > 0 25 | ? View.Month({ year, month: month - 1 }) 26 | : View.Month({ year: year - 1, month: 11 }), 27 | }); 28 | }; 29 | 30 | export const hasNext = (view: View) => { 31 | const now = new Date(); 32 | return View.$match(view, { 33 | Year: ({ year }) => year < now.getFullYear(), 34 | Month: ({ year, month }) => 35 | month < now.getMonth() || year < now.getFullYear(), 36 | }); 37 | }; 38 | 39 | export const getNext = (view: View) => { 40 | return View.$match(view, { 41 | Year: ({ year }) => View.Year({ year: year + 1 }), 42 | Month: ({ year, month }) => 43 | month < 11 44 | ? View.Month({ year, month: month + 1 }) 45 | : View.Month({ year: year + 1, month: 0 }), 46 | }); 47 | }; 48 | 49 | export const toDateRange = (view: View): DateRange.DateRange => { 50 | return View.$match(view, { 51 | Year: ({ year }) => ({ 52 | since: DateTime.unsafeMake({ year }), 53 | until: DateTime.unsafeMake({ year: year + 1 }), 54 | }), 55 | Month: ({ year, month }) => ({ 56 | since: DateTime.unsafeMake({ year, month: month + 1 }), 57 | until: DateTime.unsafeMake({ year, month: month + 2 }), 58 | }), 59 | }); 60 | }; 61 | 62 | export const toString = (view: View) => { 63 | return View.$match(view, { 64 | Year: ({ year }) => { 65 | const date = new Date(year, 0, 1); 66 | return yearFormatter.format(date); 67 | }, 68 | Month: ({ year, month }) => { 69 | const date = new Date(year, month, 1); 70 | return monthFormatter.format(date); 71 | }, 72 | }); 73 | }; 74 | 75 | export const getCurrentView = (type: View["_tag"]): View => { 76 | const now = new Date(); 77 | switch (type) { 78 | case "Year": 79 | return View.Year({ year: now.getFullYear() }); 80 | case "Month": 81 | return View.Month({ year: now.getFullYear(), month: now.getMonth() }); 82 | } 83 | }; 84 | 85 | export const getDateBucket = (view: View, date: Date) => 86 | View.$match(view, { 87 | Year: () => date.getMonth().toString(), 88 | Month: () => date.getDate().toString(), 89 | }); 90 | 91 | export const getDateBuckets = (view: View) => 92 | View.$match(view, { 93 | Year: ({ year }) => Array.makeBy(12, (i) => new Date(year, i, 1)), 94 | 95 | Month: ({ year, month }) => { 96 | const daysInMonth = new Date(year, month + 1, 0).getDate(); 97 | return Array.makeBy(daysInMonth, (i) => new Date(year, month, i + 1)); 98 | }, 99 | }); 100 | 101 | export const getDateLabel = (view: View, date: Date) => { 102 | const now = new Date(); 103 | return View.$match(view, { 104 | Year: () => monthFormatter.format(date), 105 | Month: ({ year }) => 106 | year === now.getFullYear() 107 | ? dayFormatter.format(date) 108 | : dayFormatterPreviousYear.format(date), 109 | }); 110 | }; 111 | 112 | const yearFormatter = new Intl.DateTimeFormat("en-US", { 113 | year: "numeric", 114 | }); 115 | 116 | const monthFormatter = new Intl.DateTimeFormat("en-US", { 117 | month: "short", 118 | year: "numeric", 119 | }); 120 | 121 | const dayFormatter = new Intl.DateTimeFormat("en-US", { 122 | month: "long", 123 | day: "numeric", 124 | }); 125 | 126 | const dayFormatterPreviousYear = new Intl.DateTimeFormat("en-US", { 127 | month: "long", 128 | day: "numeric", 129 | year: "numeric", 130 | }); 131 | -------------------------------------------------------------------------------- /packages/web/src/components/ui/select.tsx: -------------------------------------------------------------------------------- 1 | import { Select as SelectPrimitive } from "@base-ui-components/react/select"; 2 | import { cn } from "@/lib/utils"; 3 | 4 | export function Select({ 5 | ...props 6 | }: React.ComponentProps) { 7 | return ; 8 | } 9 | 10 | export function SelectTrigger({ 11 | className, 12 | children, 13 | ...props 14 | }: React.ComponentProps) { 15 | return ( 16 | 23 | {children} 24 | 25 | 26 | 27 | 28 | ); 29 | } 30 | 31 | export function SelectValue({ 32 | ...props 33 | }: React.ComponentProps) { 34 | return ; 35 | } 36 | 37 | export function SelectPopup({ 38 | className, 39 | children, 40 | ...props 41 | }: React.ComponentProps) { 42 | return ( 43 | 44 | 45 | 46 | 53 | {children} 54 | 55 | 56 | 57 | 58 | ); 59 | } 60 | 61 | export function SelectItem({ 62 | className, 63 | children, 64 | ...props 65 | }: React.ComponentProps) { 66 | return ( 67 | 74 | 75 | 76 | 77 | 78 | {children} 79 | 80 | 81 | ); 82 | } 83 | 84 | function ChevronUpDownIcon(props: React.ComponentProps<"svg">) { 85 | return ( 86 | 95 | Chevron up down 96 | 97 | 98 | 99 | ); 100 | } 101 | 102 | function CheckIcon(props: React.ComponentProps<"svg">) { 103 | return ( 104 | 111 | Check 112 | 113 | 114 | ); 115 | } 116 | -------------------------------------------------------------------------------- /packages/web/src/app/_components/transactions/transactions-chart.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import type * as Transaction from "@modo/core/schemas/transaction"; 4 | import * as Array from "effect/Array"; 5 | import * as React from "react"; 6 | import * as Recharts from "recharts"; 7 | import { moneyFormatter } from "@/lib/utils"; 8 | import type { View } from "@/lib/view"; 9 | import * as ViewUtils from "@/lib/view"; 10 | import type { SelectedFilters } from "./types"; 11 | 12 | export interface TransactionsChartProps { 13 | view: View; 14 | transactions: Transaction.Transaction[]; 15 | filteredTransactions: Transaction.Transaction[] | null; 16 | setSelectedFilters: React.Dispatch>; 17 | } 18 | 19 | export function TransactionsChart({ 20 | view, 21 | transactions, 22 | filteredTransactions, 23 | setSelectedFilters, 24 | }: TransactionsChartProps) { 25 | const buckets = React.useMemo(() => { 26 | const filteredTransactionIds = new Set( 27 | filteredTransactions?.map((t) => t.id) ?? [], 28 | ); 29 | const transactionsGrouped = Array.groupBy(transactions, (t) => 30 | ViewUtils.getDateBucket(view, t.date), 31 | ); 32 | return ViewUtils.getDateBuckets(view).map((date) => { 33 | const dateBucket = ViewUtils.getDateBucket(view, date); 34 | const transactionsOnDate = transactionsGrouped[dateBucket] ?? []; 35 | const [amount, amountFiltered] = transactionsOnDate.reduce( 36 | ([acc, accFiltered], t) => { 37 | const amount = t.amountMicro / 1_000_000; 38 | const isFiltered = 39 | filteredTransactions !== null && filteredTransactionIds.has(t.id); 40 | if (isFiltered) { 41 | return [acc, accFiltered + amount]; 42 | } 43 | return [acc + amount, accFiltered]; 44 | }, 45 | [0, 0], 46 | ); 47 | return { 48 | date, 49 | amount, 50 | amountFiltered, 51 | label: ViewUtils.getDateLabel(view, date), 52 | }; 53 | }); 54 | }, [transactions, filteredTransactions, view]); 55 | 56 | return ( 57 |
58 | 59 | 60 | 63 | setSelectedFilters({ 64 | transaction: null, 65 | date: data.date, 66 | }) 67 | } 68 | dataKey="amount" 69 | isAnimationActive={false} 70 | fill={ 71 | filteredTransactions === null 72 | ? "var(--color-gray8)" 73 | : "var(--color-gray11)" 74 | } 75 | stackId="a" 76 | /> 77 | 80 | setSelectedFilters((filters) => ({ 81 | ...filters, 82 | date: 83 | filters.date && 84 | ViewUtils.getDateBucket(view, filters.date) === 85 | ViewUtils.getDateBucket(view, data.date) 86 | ? null 87 | : data.date, 88 | })) 89 | } 90 | dataKey="amountFiltered" 91 | isAnimationActive={false} 92 | fill="var(--color-gray8)" 93 | stackId="a" 94 | /> 95 | 96 | { 101 | // TODO: make this more robust 102 | const amount = payload?.[0]?.value; 103 | const amountFiltered = payload?.[1]?.value; 104 | if ( 105 | typeof amount !== "number" || 106 | typeof amountFiltered !== "number" 107 | ) { 108 | return ( 109 |
110 | Tooltip 111 |
112 | ); 113 | } 114 | const amountTotal = amount + amountFiltered; 115 | 116 | return ( 117 |
121 |
122 |

{label}

123 |
124 |
125 | {filteredTransactions !== null && ( 126 |
127 |

Filtered

128 |

129 | {moneyFormatter.format(amountFiltered)} 130 |

131 |
132 | )} 133 |
134 |

Total

135 |

136 | {moneyFormatter.format(amountTotal)} 137 |

138 |
139 |
140 |
141 | ); 142 | }} 143 | /> 144 |
145 |
146 |
147 | ); 148 | } 149 | -------------------------------------------------------------------------------- /packages/core/src/services/robinhood-card/get.ts: -------------------------------------------------------------------------------- 1 | import * as Array from "effect/Array"; 2 | import * as DateTime from "effect/DateTime"; 3 | import * as Effect from "effect/Effect"; 4 | import * as Option from "effect/Option"; 5 | import * as Schema from "effect/Schema"; 6 | import { gql } from "graphql-request"; 7 | import type * as DateRange from "../../schemas/date-range"; 8 | import * as RobinhoodCard from "../../schemas/robinhood-card"; 9 | import * as GraphQL from "../graphql"; 10 | 11 | const CriticalData = Schema.Struct({ 12 | authIdentity: Schema.Struct({ 13 | creditCustomers: Schema.NonEmptyArray( 14 | Schema.Struct({ 15 | id: Schema.String, 16 | }), 17 | ), 18 | }), 19 | }); 20 | 21 | /** 22 | * Get the IDs of all customers for the current user. 23 | * 24 | * Customer IDs are unique to the different users of the Robinhood credit card. 25 | * 26 | * @returns An effect that returns an array of customer IDs. 27 | */ 28 | export const getCustomerIds = Effect.fn(function* () { 29 | const graphql = yield* GraphQL.GraphQL; 30 | const result = yield* graphql.use(async (client) => 31 | client.request( 32 | gql` 33 | query CriticalDataLoaderQuery { 34 | authIdentity { 35 | creditCustomers { 36 | id 37 | } 38 | } 39 | } 40 | `, 41 | ), 42 | ); 43 | const decoded = yield* Schema.decodeUnknown(CriticalData)(result); 44 | const customerIds = decoded.authIdentity.creditCustomers.map((c) => c.id); 45 | return customerIds; 46 | }); 47 | 48 | const TRANSACTIONS_PAGE_LIMIT = 50; 49 | 50 | /** 51 | * Get all transactions for a customer in the date range [since, until). 52 | * 53 | * @param customerId - The ID of the customer to get transactions for. 54 | * @param dateRange - The date range to get transactions for. 55 | * @returns An effect that returns an array of transactions. 56 | */ 57 | export const getTransactions = Effect.fn(function* ( 58 | customerId: string, 59 | dateRange: DateRange.DateRange, 60 | ) { 61 | const since = dateRange.since ?? DateTime.unsafeMake(0); 62 | const until = dateRange.until ?? (yield* DateTime.now); 63 | 64 | let cursor: string | undefined; 65 | let transactions = Array.empty(); 66 | 67 | // Loop until we've fetched all transactions in the range. There are two conditions 68 | // that signal we're done: 69 | // 70 | // 1. The cursor is undefined, meaning we've reached the last page of transactions. 71 | // 2. The oldest transaction in the page is before the range start date, meaning all older 72 | // transactions will be out of the range. 73 | while (true) { 74 | const { 75 | transactionSearch: { items: transactionsNew, cursor: cursorNext }, 76 | } = yield* listTransactions(customerId, { 77 | cursor, 78 | limit: TRANSACTIONS_PAGE_LIMIT, 79 | }); 80 | 81 | const transactionsInRange = Array.filter(transactionsNew, (t) => { 82 | const dateTime = DateTime.unsafeMake(t.transactionAt); 83 | return ( 84 | DateTime.lessThanOrEqualTo(since, dateTime) && 85 | DateTime.lessThan(dateTime, until) 86 | ); 87 | }); 88 | transactions = Array.appendAll(transactions, transactionsInRange); 89 | 90 | const hasNextPage = cursorNext !== undefined; 91 | const isNextPageOutOfRange = Array.last(transactionsNew).pipe( 92 | Option.map((t) => DateTime.unsafeMake(t.transactionAt)), 93 | Option.map((d) => DateTime.lessThan(d, since)), 94 | Option.getOrElse(() => true), 95 | ); 96 | 97 | if (!hasNextPage || isNextPageOutOfRange) { 98 | break; 99 | } 100 | 101 | cursor = cursorNext; 102 | } 103 | 104 | return transactions; 105 | }); 106 | 107 | const TransactionList = Schema.Struct({ 108 | transactionSearch: Schema.Struct({ 109 | items: Schema.Array(RobinhoodCard.Transaction), 110 | cursor: Schema.optional(Schema.String), 111 | }), 112 | }); 113 | 114 | interface TransactionListOptions { 115 | ascending?: boolean | undefined; 116 | limit?: number | undefined; 117 | cursor?: string | undefined; 118 | } 119 | 120 | export const listTransactions = Effect.fn(function* ( 121 | customerId: string, 122 | options: TransactionListOptions = {}, 123 | ) { 124 | const graphql = yield* GraphQL.GraphQL; 125 | const result = yield* graphql.use(async (client) => 126 | client.request( 127 | gql` 128 | query TransactionListQuery( 129 | $q: TransactionSearchRequest! 130 | ) { 131 | transactionSearch(q: $q) { 132 | items { 133 | id 134 | amountMicro 135 | originalAmountMicro 136 | flow 137 | transactionStatus 138 | redemptionStatus 139 | transactionType 140 | transactionAt 141 | visibility 142 | merchantDetails { 143 | merchantName 144 | logoUrl 145 | } 146 | pointEarnings 147 | pointMultiplier 148 | links { 149 | id 150 | paymentId 151 | creditCustomer { 152 | id 153 | creditCustomerId 154 | name { 155 | id 156 | firstName 157 | lastName 158 | } 159 | } 160 | } 161 | disputeDetails { 162 | eligibility 163 | } 164 | installmentLoanEligibility 165 | installmentLoan { 166 | id 167 | status 168 | } 169 | } 170 | cursor 171 | } 172 | } 173 | `, 174 | { 175 | q: { 176 | creditCustomerId: customerId, 177 | filters: { values: [] }, 178 | sortDetails: { 179 | field: "TIME", 180 | ascending: options.ascending ?? false, 181 | }, 182 | limit: options.limit ?? 50, 183 | cursor: options.cursor, 184 | }, 185 | }, 186 | ), 187 | ); 188 | const decoded = yield* Schema.decodeUnknown(TransactionList)(result); 189 | return decoded; 190 | }); 191 | -------------------------------------------------------------------------------- /packages/web/src/app/_components/transactions/index.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ScrollArea } from "@base-ui-components/react/scroll-area"; 4 | import type * as Transaction from "@modo/core/schemas/transaction"; 5 | import * as ApiClient from "@modo/core/services/api-client"; 6 | import NumberFlow from "@number-flow/react"; 7 | import { useQuery } from "@tanstack/react-query"; 8 | import * as Effect from "effect/Effect"; 9 | import * as React from "react"; 10 | import { 11 | Select, 12 | SelectItem, 13 | SelectPopup, 14 | SelectTrigger, 15 | SelectValue, 16 | } from "@/components/ui/select"; 17 | import { moneyFormatterOptions } from "@/lib/utils"; 18 | import * as ViewUtils from "@/lib/view"; 19 | import { View } from "@/lib/view"; 20 | import { RuntimeClient } from "@/runtime"; 21 | import { MemoizedTransactionPane as TransactionPane } from "./transaction-pane"; 22 | import styles from "./transactions.module.css"; 23 | import { TransactionsChart } from "./transactions-chart"; 24 | import type { SelectedFilters } from "./types"; 25 | 26 | const customerId = "1"; 27 | 28 | export function Transactions() { 29 | const [view, setView] = React.useState( 30 | View.Year({ year: new Date().getFullYear() }), 31 | ); 32 | const [selectedFilters, setSelectedFilters] = React.useState( 33 | { date: null, transaction: null }, 34 | ); 35 | 36 | const selectedDateBucket = selectedFilters.date 37 | ? ViewUtils.getDateBucket(view, selectedFilters.date) 38 | : null; 39 | 40 | const hasFilters = Object.values(selectedFilters).some((v) => v !== null); 41 | 42 | const handleViewChange = (value: unknown) => { 43 | if (value === "Year" || value === "Month") { 44 | const newView = ViewUtils.getCurrentView(value); 45 | setView(newView); 46 | setSelectedFilters((filters) => ({ ...filters, date: null })); 47 | } 48 | }; 49 | 50 | const handlePrevious = () => { 51 | setView(ViewUtils.getPrevious(view)); 52 | setSelectedFilters((filters) => ({ ...filters, date: null })); 53 | }; 54 | 55 | const handleNext = () => { 56 | setView(ViewUtils.getNext(view)); 57 | setSelectedFilters((filters) => ({ ...filters, date: null })); 58 | }; 59 | 60 | const { data: transactionsRaw = [], isLoading } = useQuery({ 61 | queryKey: ["transactions", customerId, view], 62 | queryFn: async () => 63 | RuntimeClient.runPromise( 64 | Effect.gen(function* () { 65 | const client = yield* ApiClient.ApiClient; 66 | const transactions = yield* client.transactions.listForCustomer({ 67 | path: { customerId }, 68 | urlParams: ViewUtils.toDateRange(view), 69 | }); 70 | // TODO: remove the type assertion 71 | return transactions as Transaction.Transaction[]; 72 | }).pipe( 73 | Effect.tapError((error) => Effect.logError(error)), 74 | Effect.catchAll(() => Effect.succeed([])), 75 | ), 76 | ), 77 | }); 78 | 79 | const transactionsFiltered = React.useMemo(() => { 80 | if (!hasFilters) return null; 81 | const filterByDateBucket = (t: Transaction.Transaction) => 82 | selectedDateBucket 83 | ? ViewUtils.getDateBucket(view, t.date) === selectedDateBucket 84 | : true; 85 | const filterByMerchant = (t: Transaction.Transaction) => 86 | selectedFilters.transaction?.merchantName 87 | ? t.merchantName === selectedFilters.transaction.merchantName 88 | : true; 89 | return transactionsRaw.filter( 90 | (t) => filterByDateBucket(t) && filterByMerchant(t), 91 | ); 92 | }, [transactionsRaw, view, selectedDateBucket, selectedFilters, hasFilters]); 93 | 94 | const transactions = transactionsFiltered ?? transactionsRaw; 95 | 96 | const [totalSpent, totalCashback] = transactions.reduce( 97 | ([spent, cashback], transaction) => [ 98 | spent + transaction.amountMicro / 1_000_000, 99 | cashback + transaction.cashbackAmountMicro / 1_000_000, 100 | ], 101 | [0, 0], 102 | ); 103 | 104 | return ( 105 |
106 |
107 |
108 |

Transactions

109 | 118 |
119 |
120 |
121 |
122 |

Amount spent

123 | {isLoading ? ( 124 |
125 | ) : ( 126 | 131 | )} 132 |
133 |
134 |

Cashback earned

135 | {isLoading ? ( 136 |
137 | ) : ( 138 | 143 | )} 144 |
145 |
146 | 154 |

{ViewUtils.toString(view)}

155 | 163 |
164 |
165 |
166 |
167 | 173 |
174 |
175 |
176 |
177 |

Transactions history

178 |
179 | {selectedFilters.transaction && ( 180 | 192 | )} 193 | {selectedFilters.date && ( 194 | 203 | )} 204 |
205 |
206 | 207 | 210 |
211 | {transactionsFiltered?.length === 0 && !isLoading && ( 212 |
213 |

214 | {hasFilters 215 | ? "No transactions found. Try clearing some filters." 216 | : "No transactions found."} 217 |

218 |
219 | )} 220 | {transactions.map((transaction) => ( 221 | 226 | ))} 227 |
228 |
229 | 230 | 231 | 232 |
233 |
234 |
235 | ); 236 | } 237 | -------------------------------------------------------------------------------- /bun.lock: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1, 3 | "workspaces": { 4 | "": { 5 | "name": "modo", 6 | "devDependencies": { 7 | "@biomejs/biome": "^2.1.1", 8 | "@effect/language-service": "catalog:", 9 | "vitest": "catalog:", 10 | }, 11 | }, 12 | "packages/backend": { 13 | "name": "@modo/backend", 14 | "dependencies": { 15 | "@effect/platform": "catalog:", 16 | "@effect/platform-bun": "catalog:", 17 | "@modo/core": "workspace:*", 18 | "effect": "catalog:", 19 | }, 20 | "devDependencies": { 21 | "@types/bun": "catalog:", 22 | }, 23 | "peerDependencies": { 24 | "typescript": "catalog:", 25 | }, 26 | }, 27 | "packages/core": { 28 | "name": "@modo/core", 29 | "dependencies": { 30 | "@effect/platform": "catalog:", 31 | "@ngneat/falso": "^8.0.1", 32 | "effect": "catalog:", 33 | "graphql": "^16.11.0", 34 | "graphql-request": "catalog:", 35 | }, 36 | "devDependencies": { 37 | "@effect/vitest": "catalog:", 38 | "@types/bun": "catalog:", 39 | "vitest": "catalog:", 40 | }, 41 | "peerDependencies": { 42 | "typescript": "catalog:", 43 | }, 44 | }, 45 | "packages/scripts": { 46 | "name": "@modo/scripts", 47 | "dependencies": { 48 | "@effect/platform-bun": "catalog:", 49 | "@modo/core": "workspace:*", 50 | "effect": "catalog:", 51 | "graphql-request": "catalog:", 52 | }, 53 | "devDependencies": { 54 | "@types/bun": "catalog:", 55 | }, 56 | "peerDependencies": { 57 | "typescript": "catalog:", 58 | }, 59 | }, 60 | "packages/web": { 61 | "name": "web", 62 | "version": "0.1.0", 63 | "dependencies": { 64 | "@base-ui-components/react": "^1.0.0-beta.1", 65 | "@effect/platform": "catalog:", 66 | "@modo/core": "workspace:*", 67 | "@next/bundle-analyzer": "^15.3.5", 68 | "@number-flow/react": "^0.5.10", 69 | "@tanstack/react-query": "^5.82.0", 70 | "@tanstack/react-query-devtools": "^5.82.0", 71 | "clsx": "^2.1.1", 72 | "effect": "catalog:", 73 | "next": "^15.4.0-canary.127", 74 | "react": "^19.0.0", 75 | "react-dom": "^19.0.0", 76 | "recharts": "^2", 77 | "tailwind-merge": "^3.3.1", 78 | "tw-animate-css": "^1.3.5", 79 | }, 80 | "devDependencies": { 81 | "@effect/language-service": "catalog:", 82 | "@tailwindcss/postcss": "^4", 83 | "@types/node": "^20", 84 | "@types/react": "^19", 85 | "@types/react-dom": "^19", 86 | "tailwindcss": "^4", 87 | "typescript": "catalog:", 88 | }, 89 | }, 90 | }, 91 | "catalog": { 92 | "@effect/language-service": "^0.24.0", 93 | "@effect/platform": "^0.87.11", 94 | "@effect/platform-bun": "^0.72.15", 95 | "@effect/vitest": "^0.23.12", 96 | "@types/bun": "latest", 97 | "effect": "^3.16.12", 98 | "graphql-request": "^7.2.0", 99 | "typescript": "^5", 100 | "vitest": "^3.2.4", 101 | }, 102 | "packages": { 103 | "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], 104 | 105 | "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], 106 | 107 | "@babel/runtime": ["@babel/runtime@7.27.6", "", {}, "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q=="], 108 | 109 | "@base-ui-components/react": ["@base-ui-components/react@1.0.0-beta.1", "", { "dependencies": { "@babel/runtime": "^7.27.6", "@floating-ui/react-dom": "^2.1.3", "@floating-ui/utils": "^0.2.9", "reselect": "^5.1.1", "tabbable": "^6.2.0", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-7zmGiz4/+HKnv99lWftItoSMqnj2PdSvt2krh0/GP+Rj0xK0NMnFI/gIVvP7CB2G+k0JPUrRWXjXa3y08oiakg=="], 110 | 111 | "@biomejs/biome": ["@biomejs/biome@2.1.1", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.1.1", "@biomejs/cli-darwin-x64": "2.1.1", "@biomejs/cli-linux-arm64": "2.1.1", "@biomejs/cli-linux-arm64-musl": "2.1.1", "@biomejs/cli-linux-x64": "2.1.1", "@biomejs/cli-linux-x64-musl": "2.1.1", "@biomejs/cli-win32-arm64": "2.1.1", "@biomejs/cli-win32-x64": "2.1.1" }, "bin": { "biome": "bin/biome" } }, "sha512-HFGYkxG714KzG+8tvtXCJ1t1qXQMzgWzfvQaUjxN6UeKv+KvMEuliInnbZLJm6DXFXwqVi6446EGI0sGBLIYng=="], 112 | 113 | "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.1.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2Muinu5ok4tWxq4nu5l19el48cwCY/vzvI7Vjbkf3CYIQkjxZLyj0Ad37Jv2OtlXYaLvv+Sfu1hFeXt/JwRRXQ=="], 114 | 115 | "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.1.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-cC8HM5lrgKQXLAK+6Iz2FrYW5A62pAAX6KAnRlEyLb+Q3+Kr6ur/sSuoIacqlp1yvmjHJqjYfZjPvHWnqxoEIA=="], 116 | 117 | "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.1.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-tw4BEbhAUkWPe4WBr6IX04DJo+2jz5qpPzpW/SWvqMjb9QuHY8+J0M23V8EPY/zWU4IG8Ui0XESapR1CB49Q7g=="], 118 | 119 | "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.1.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-/7FBLnTswu4jgV9ttI3AMIdDGqVEPIZd8I5u2D4tfCoj8rl9dnjrEQbAIDlWhUXdyWlFSz8JypH3swU9h9P+2A=="], 120 | 121 | "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.1.1", "", { "os": "linux", "cpu": "x64" }, "sha512-3WJ1GKjU7NzZb6RTbwLB59v9cTIlzjbiFLDB0z4376TkDqoNYilJaC37IomCr/aXwuU8QKkrYoHrgpSq5ffJ4Q=="], 122 | 123 | "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.1.1", "", { "os": "linux", "cpu": "x64" }, "sha512-kUu+loNI3OCD2c12cUt7M5yaaSjDnGIksZwKnueubX6c/HWUyi/0mPbTBHR49Me3F0KKjWiKM+ZOjsmC+lUt9g=="], 124 | 125 | "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.1.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-vEHK0v0oW+E6RUWLoxb2isI3rZo57OX9ZNyyGH701fZPj6Il0Rn1f5DMNyCmyflMwTnIQstEbs7n2BxYSqQx4Q=="], 126 | 127 | "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.1.1", "", { "os": "win32", "cpu": "x64" }, "sha512-i2PKdn70kY++KEF/zkQFvQfX1e8SkA8hq4BgC+yE9dZqyLzB/XStY2MvwI3qswlRgnGpgncgqe0QYKVS1blksg=="], 128 | 129 | "@discoveryjs/json-ext": ["@discoveryjs/json-ext@0.5.7", "", {}, "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw=="], 130 | 131 | "@effect/cluster": ["@effect/cluster@0.41.15", "", { "peerDependencies": { "@effect/platform": "^0.87.11", "@effect/rpc": "^0.64.12", "@effect/sql": "^0.40.12", "@effect/workflow": "^0.4.12", "effect": "^3.16.12" } }, "sha512-7xgINXrc0iwOrb6pEQ+GFdNsyXs1Z3lBH3gIb7FBLEtqEVuxUybRgKqwqsrwgdPYg7Mfc5awnVmGZV2VSyX2dw=="], 132 | 133 | "@effect/experimental": ["@effect/experimental@0.51.12", "", { "dependencies": { "uuid": "^11.0.3" }, "peerDependencies": { "@effect/platform": "^0.87.11", "effect": "^3.16.12", "ioredis": "^5", "lmdb": "^3" }, "optionalPeers": ["ioredis", "lmdb"] }, "sha512-5/O0OpeFtq3nN8PBei+OgwalfgbVmIG25Z5TRrkc6/9WTB/sRBaxe1Atl+8q+NNhBW6s1s6JVflYOmgTDZZV0w=="], 134 | 135 | "@effect/language-service": ["@effect/language-service@0.24.2", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-wZiMtqbuJBcYNvXyG91c09T9dX1OPcmyH9k1Rz8G56i3kIrvVbs/gB/w4hexDuCjlemApYacMV6fH7O9O+Gtbg=="], 136 | 137 | "@effect/platform": ["@effect/platform@0.87.12", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.5" }, "peerDependencies": { "effect": "^3.16.12" } }, "sha512-IRYynFjo18wmfTd0FVNAXSC631+gE1yBeBfcOHtPLyMlApjhB31YzaCs+T9kY1cBrE7nZ78lJl0MtHmu1W9Wkw=="], 138 | 139 | "@effect/platform-bun": ["@effect/platform-bun@0.72.15", "", { "dependencies": { "@effect/platform-node-shared": "^0.42.15", "multipasta": "^0.2.5" }, "peerDependencies": { "@effect/cluster": "^0.41.15", "@effect/platform": "^0.87.11", "@effect/rpc": "^0.64.12", "@effect/sql": "^0.40.12", "effect": "^3.16.12" } }, "sha512-CThoLWwvqQxNE80SxUeGzdMY+SI3KAb0bL9Xn/lc033/IZ19W+MlykVEXAbeXIAz6OgFQYfvZoh+BxPgPUq/Bg=="], 140 | 141 | "@effect/platform-node-shared": ["@effect/platform-node-shared@0.42.15", "", { "dependencies": { "@parcel/watcher": "^2.5.1", "multipasta": "^0.2.5", "ws": "^8.18.2" }, "peerDependencies": { "@effect/cluster": "^0.41.15", "@effect/platform": "^0.87.11", "@effect/rpc": "^0.64.12", "@effect/sql": "^0.40.12", "effect": "^3.16.12" } }, "sha512-GURkGg9oBbEQOD00SYGh7i9M1bPkVL+FGnBvl1HbRprGZge+wmowbDVLinoG3Bybv/ugM0QMiPNe5zLr7oZWtw=="], 142 | 143 | "@effect/rpc": ["@effect/rpc@0.64.12", "", { "peerDependencies": { "@effect/platform": "^0.87.11", "effect": "^3.16.12" } }, "sha512-vDv3EZUUiRqE1czbXEcCGWPa7QqjlpCd8IaGUzeHLrzwAT2LD/7PsJw1LS6xg51NX+IV2mbgX/vfZ8LttgLhfA=="], 144 | 145 | "@effect/sql": ["@effect/sql@0.40.12", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.33.0", "uuid": "^11.0.3" }, "peerDependencies": { "@effect/experimental": "^0.51.12", "@effect/platform": "^0.87.11", "effect": "^3.16.12" } }, "sha512-IzXOAcft/aAsEVgat/PKe6b5/WvKbfI3V7fC5Un9cn0880HbUO4q2+hTWSTrWX/33l/6yqXMBKDOG+QUSHhSYA=="], 146 | 147 | "@effect/vitest": ["@effect/vitest@0.23.12", "", { "peerDependencies": { "effect": "^3.16.12", "vitest": "^3.0.0" } }, "sha512-kUR6Npn9gXsO+PlxnZQ9kmd7RzjT1Bl0foyypvCKctvRtG/CmqNgDNNZU9s/GZXvVAtbChjULXe1APyE2smrAw=="], 148 | 149 | "@effect/workflow": ["@effect/workflow@0.4.12", "", { "peerDependencies": { "@effect/platform": "^0.87.11", "@effect/rpc": "^0.64.12", "effect": "^3.16.12" } }, "sha512-8zXup+Kv682fIRxvXCBhlun23kQ/UU3fMMaswwQruJVZ22FIHP7+Kusk2NB9p6jAGXXEbngWdL//wilW+tkFDA=="], 150 | 151 | "@emnapi/runtime": ["@emnapi/runtime@1.4.4", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg=="], 152 | 153 | "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.6", "", { "os": "aix", "cpu": "ppc64" }, "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw=="], 154 | 155 | "@esbuild/android-arm": ["@esbuild/android-arm@0.25.6", "", { "os": "android", "cpu": "arm" }, "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg=="], 156 | 157 | "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.6", "", { "os": "android", "cpu": "arm64" }, "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA=="], 158 | 159 | "@esbuild/android-x64": ["@esbuild/android-x64@0.25.6", "", { "os": "android", "cpu": "x64" }, "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A=="], 160 | 161 | "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA=="], 162 | 163 | "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg=="], 164 | 165 | "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.6", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg=="], 166 | 167 | "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.6", "", { "os": "freebsd", "cpu": "x64" }, "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ=="], 168 | 169 | "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.6", "", { "os": "linux", "cpu": "arm" }, "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw=="], 170 | 171 | "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ=="], 172 | 173 | "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.6", "", { "os": "linux", "cpu": "ia32" }, "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw=="], 174 | 175 | "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.6", "", { "os": "linux", "cpu": "none" }, "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg=="], 176 | 177 | "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.6", "", { "os": "linux", "cpu": "none" }, "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw=="], 178 | 179 | "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.6", "", { "os": "linux", "cpu": "ppc64" }, "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw=="], 180 | 181 | "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.6", "", { "os": "linux", "cpu": "none" }, "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w=="], 182 | 183 | "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.6", "", { "os": "linux", "cpu": "s390x" }, "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw=="], 184 | 185 | "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.6", "", { "os": "linux", "cpu": "x64" }, "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig=="], 186 | 187 | "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.6", "", { "os": "none", "cpu": "arm64" }, "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q=="], 188 | 189 | "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.6", "", { "os": "none", "cpu": "x64" }, "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g=="], 190 | 191 | "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.6", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg=="], 192 | 193 | "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.6", "", { "os": "openbsd", "cpu": "x64" }, "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw=="], 194 | 195 | "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.6", "", { "os": "none", "cpu": "arm64" }, "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA=="], 196 | 197 | "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.6", "", { "os": "sunos", "cpu": "x64" }, "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA=="], 198 | 199 | "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q=="], 200 | 201 | "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.6", "", { "os": "win32", "cpu": "ia32" }, "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ=="], 202 | 203 | "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.6", "", { "os": "win32", "cpu": "x64" }, "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA=="], 204 | 205 | "@floating-ui/core": ["@floating-ui/core@1.7.2", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw=="], 206 | 207 | "@floating-ui/dom": ["@floating-ui/dom@1.7.2", "", { "dependencies": { "@floating-ui/core": "^1.7.2", "@floating-ui/utils": "^0.2.10" } }, "sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA=="], 208 | 209 | "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.4", "", { "dependencies": { "@floating-ui/dom": "^1.7.2" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-JbbpPhp38UmXDDAu60RJmbeme37Jbgsm7NrHGgzYYFKmblzRUh6Pa641dII6LsjwF4XlScDrde2UAzDo/b9KPw=="], 210 | 211 | "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], 212 | 213 | "@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="], 214 | 215 | "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.0" }, "os": "darwin", "cpu": "arm64" }, "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg=="], 216 | 217 | "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.0" }, "os": "darwin", "cpu": "x64" }, "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA=="], 218 | 219 | "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ=="], 220 | 221 | "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg=="], 222 | 223 | "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.0", "", { "os": "linux", "cpu": "arm" }, "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw=="], 224 | 225 | "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA=="], 226 | 227 | "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ=="], 228 | 229 | "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw=="], 230 | 231 | "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg=="], 232 | 233 | "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q=="], 234 | 235 | "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q=="], 236 | 237 | "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.0" }, "os": "linux", "cpu": "arm" }, "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A=="], 238 | 239 | "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.0" }, "os": "linux", "cpu": "arm64" }, "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA=="], 240 | 241 | "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.0" }, "os": "linux", "cpu": "ppc64" }, "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA=="], 242 | 243 | "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.0" }, "os": "linux", "cpu": "s390x" }, "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ=="], 244 | 245 | "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.0" }, "os": "linux", "cpu": "x64" }, "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ=="], 246 | 247 | "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" }, "os": "linux", "cpu": "arm64" }, "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ=="], 248 | 249 | "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.0" }, "os": "linux", "cpu": "x64" }, "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ=="], 250 | 251 | "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.3", "", { "dependencies": { "@emnapi/runtime": "^1.4.4" }, "cpu": "none" }, "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg=="], 252 | 253 | "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ=="], 254 | 255 | "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw=="], 256 | 257 | "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.3", "", { "os": "win32", "cpu": "x64" }, "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g=="], 258 | 259 | "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], 260 | 261 | "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.12", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg=="], 262 | 263 | "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], 264 | 265 | "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.4", "", {}, "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw=="], 266 | 267 | "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.29", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ=="], 268 | 269 | "@modo/backend": ["@modo/backend@workspace:packages/backend"], 270 | 271 | "@modo/core": ["@modo/core@workspace:packages/core"], 272 | 273 | "@modo/scripts": ["@modo/scripts@workspace:packages/scripts"], 274 | 275 | "@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="], 276 | 277 | "@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="], 278 | 279 | "@msgpackr-extract/msgpackr-extract-linux-arm": ["@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3", "", { "os": "linux", "cpu": "arm" }, "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw=="], 280 | 281 | "@msgpackr-extract/msgpackr-extract-linux-arm64": ["@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg=="], 282 | 283 | "@msgpackr-extract/msgpackr-extract-linux-x64": ["@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg=="], 284 | 285 | "@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="], 286 | 287 | "@next/bundle-analyzer": ["@next/bundle-analyzer@15.3.5", "", { "dependencies": { "webpack-bundle-analyzer": "4.10.1" } }, "sha512-r1tlg7N4IUWpdqdy8/6bf7pvo2yeN9Oc6OHEiMsMfIooJ5k37Odi9HC1qBS4soULNE7FiQ18JP/TdmQKiaIkoA=="], 288 | 289 | "@next/env": ["@next/env@15.4.0-canary.127", "", {}, "sha512-8lqZBuDCTydUy87QCSoQ3QmmOYB3piZuz9z3ZTvG4ciSoWgT8DVdhYrPwG9VX1h06m/Zn0nyCB0jwlTobBJ4cg=="], 290 | 291 | "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.4.0-canary.127", "", { "os": "darwin", "cpu": "arm64" }, "sha512-7Ti2bjmPoUBVxQFROCTH7GVp3Ohc1rv0zueKqjzL29vMePEKl1s2UzryeSFS8FsS+uR1ZpNgiSEWQBY13r8aFQ=="], 292 | 293 | "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.4.0-canary.127", "", { "os": "darwin", "cpu": "x64" }, "sha512-5jMTKqNC44ZBH2tfLVQGDYk4avlJC/B3b7PPws7QQIWnMm78cVrCL1JsGn1osw24OPpq4UAbtYJEFQljJEvDVA=="], 294 | 295 | "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.4.0-canary.127", "", { "os": "linux", "cpu": "arm64" }, "sha512-Env58MZAWIB73Dzyx3k2gmTLPADupzleV2vN1ICS3Fibgv7EIhoKKlpIazeld/ArkURxWEsrxcbnxpvFGJQDlw=="], 296 | 297 | "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.4.0-canary.127", "", { "os": "linux", "cpu": "arm64" }, "sha512-PV+QfJ/MVglOMmqKraxyjTwtyLv2N2YLnlZ9+wS2mKzorxxd1gANA1vxlwgX3ovQUW13VsrftrYRE4+K9Aro0Q=="], 298 | 299 | "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.4.0-canary.127", "", { "os": "linux", "cpu": "x64" }, "sha512-6dZaR33fJE2yv0WR6YwfFUwCx+1vlRLk3B3BnWK7nKOamkfYCcItqwP/CVTsYiHuDGZlFUhxUPgtbk4zi4LBrw=="], 300 | 301 | "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.4.0-canary.127", "", { "os": "linux", "cpu": "x64" }, "sha512-O/2QhfIe4kbZbjhXUGia3ScvsCXP81vQBs3p2TISQK4rlejIw13PHkdZy61LaBtYMqNFzhl7BuafVS1GiSKZFw=="], 302 | 303 | "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.4.0-canary.127", "", { "os": "win32", "cpu": "arm64" }, "sha512-dQxvmDzUVPPNHMszPLXA+qwhGt+ttkiTUebw7jPPgyO+/zY+PR97m5Jz8sUogxxCTgbGnszvwHWvdV8gnkvgbw=="], 304 | 305 | "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.4.0-canary.127", "", { "os": "win32", "cpu": "x64" }, "sha512-k8pc7SSgtOV6KO4gzCiEp2KeZNyuyTnmlUmFY+Kk2DIekXerv+lPk8GGJKIIFOU1k6spvJz4CbJHwIF1oOJG3Q=="], 306 | 307 | "@ngneat/falso": ["@ngneat/falso@8.0.1", "", { "dependencies": { "seedrandom": "3.0.5", "uuid": "8.3.2" } }, "sha512-C6NHC+4LaxIwJ4ARO/03ckBjt7eZB45Enj2EN5FtDYezZ6tDfvH1rIQ3P4hwWZlQpcMTRPKwBEXi3NCTyUV/zw=="], 308 | 309 | "@number-flow/react": ["@number-flow/react@0.5.10", "", { "dependencies": { "esm-env": "^1.1.4", "number-flow": "0.5.8" }, "peerDependencies": { "react": "^18 || ^19", "react-dom": "^18 || ^19" } }, "sha512-a8Wh5eNITn7Km4xbddAH7QH8eNmnduR6k34ER1hkHSGO4H2yU1DDnuAWLQM99vciGInFODemSc0tdxrXkJEpbA=="], 310 | 311 | "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.34.0", "", {}, "sha512-aKcOkyrorBGlajjRdVoJWHTxfxO1vCNHLJVlSDaRHDIdjU+pX8IYQPvPDkYiujKLbRnWU+1TBwEt0QRgSm4SGA=="], 312 | 313 | "@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="], 314 | 315 | "@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.1", "", { "os": "android", "cpu": "arm64" }, "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA=="], 316 | 317 | "@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw=="], 318 | 319 | "@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg=="], 320 | 321 | "@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ=="], 322 | 323 | "@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA=="], 324 | 325 | "@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q=="], 326 | 327 | "@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w=="], 328 | 329 | "@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg=="], 330 | 331 | "@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A=="], 332 | 333 | "@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg=="], 334 | 335 | "@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw=="], 336 | 337 | "@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ=="], 338 | 339 | "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="], 340 | 341 | "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], 342 | 343 | "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.44.2", "", { "os": "android", "cpu": "arm" }, "sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q=="], 344 | 345 | "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.44.2", "", { "os": "android", "cpu": "arm64" }, "sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA=="], 346 | 347 | "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.44.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA=="], 348 | 349 | "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.44.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw=="], 350 | 351 | "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.44.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg=="], 352 | 353 | "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.44.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA=="], 354 | 355 | "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.44.2", "", { "os": "linux", "cpu": "arm" }, "sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ=="], 356 | 357 | "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.44.2", "", { "os": "linux", "cpu": "arm" }, "sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA=="], 358 | 359 | "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.44.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A=="], 360 | 361 | "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.44.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A=="], 362 | 363 | "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.44.2", "", { "os": "linux", "cpu": "none" }, "sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g=="], 364 | 365 | "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.44.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw=="], 366 | 367 | "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.44.2", "", { "os": "linux", "cpu": "none" }, "sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg=="], 368 | 369 | "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.44.2", "", { "os": "linux", "cpu": "none" }, "sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg=="], 370 | 371 | "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.44.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw=="], 372 | 373 | "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.44.2", "", { "os": "linux", "cpu": "x64" }, "sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ=="], 374 | 375 | "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.44.2", "", { "os": "linux", "cpu": "x64" }, "sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg=="], 376 | 377 | "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.44.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw=="], 378 | 379 | "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.44.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q=="], 380 | 381 | "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.44.2", "", { "os": "win32", "cpu": "x64" }, "sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA=="], 382 | 383 | "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], 384 | 385 | "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], 386 | 387 | "@tailwindcss/node": ["@tailwindcss/node@4.1.11", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.11" } }, "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q=="], 388 | 389 | "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.11", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.11", "@tailwindcss/oxide-darwin-arm64": "4.1.11", "@tailwindcss/oxide-darwin-x64": "4.1.11", "@tailwindcss/oxide-freebsd-x64": "4.1.11", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", "@tailwindcss/oxide-linux-x64-musl": "4.1.11", "@tailwindcss/oxide-wasm32-wasi": "4.1.11", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" } }, "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg=="], 390 | 391 | "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.11", "", { "os": "android", "cpu": "arm64" }, "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg=="], 392 | 393 | "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ=="], 394 | 395 | "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw=="], 396 | 397 | "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA=="], 398 | 399 | "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11", "", { "os": "linux", "cpu": "arm" }, "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg=="], 400 | 401 | "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ=="], 402 | 403 | "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ=="], 404 | 405 | "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg=="], 406 | 407 | "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q=="], 408 | 409 | "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", "@napi-rs/wasm-runtime": "^0.2.11", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g=="], 410 | 411 | "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w=="], 412 | 413 | "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.11", "", { "os": "win32", "cpu": "x64" }, "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg=="], 414 | 415 | "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.11", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.11", "@tailwindcss/oxide": "4.1.11", "postcss": "^8.4.41", "tailwindcss": "4.1.11" } }, "sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA=="], 416 | 417 | "@tanstack/query-core": ["@tanstack/query-core@5.82.0", "", {}, "sha512-JrjoVuaajBQtnoWSg8iaPHaT4mW73lK2t+exxHNOSMqy0+13eKLqJgTKXKImLejQIfdAHQ6Un0njEhOvUtOd5w=="], 418 | 419 | "@tanstack/query-devtools": ["@tanstack/query-devtools@5.81.2", "", {}, "sha512-jCeJcDCwKfoyyBXjXe9+Lo8aTkavygHHsUHAlxQKKaDeyT0qyQNLKl7+UyqYH2dDF6UN/14873IPBHchcsU+Zg=="], 420 | 421 | "@tanstack/react-query": ["@tanstack/react-query@5.82.0", "", { "dependencies": { "@tanstack/query-core": "5.82.0" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-mnk8/ofKEthFeMdhV1dV8YXRf+9HqvXAcciXkoo755d/ocfWq7N/Y9jGOzS3h7ZW9dDGwSIhs3/HANWUBsyqYg=="], 422 | 423 | "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.82.0", "", { "dependencies": { "@tanstack/query-devtools": "5.81.2" }, "peerDependencies": { "@tanstack/react-query": "^5.82.0", "react": "^18 || ^19" } }, "sha512-MC05Zq3zr/59jhgF7dL6JSGPg1krbasDSizmRxjNcvxgh/sUTwRFD9CGN10YYX7LB6jq0ZpFtCjSVGdLiFrKAA=="], 424 | 425 | "@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="], 426 | 427 | "@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="], 428 | 429 | "@types/d3-array": ["@types/d3-array@3.2.1", "", {}, "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="], 430 | 431 | "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="], 432 | 433 | "@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="], 434 | 435 | "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="], 436 | 437 | "@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="], 438 | 439 | "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="], 440 | 441 | "@types/d3-shape": ["@types/d3-shape@3.1.7", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg=="], 442 | 443 | "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="], 444 | 445 | "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="], 446 | 447 | "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], 448 | 449 | "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], 450 | 451 | "@types/node": ["@types/node@20.19.7", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-1GM9z6BJOv86qkPvzh2i6VW5+VVrXxCLknfmTkWEqz+6DqosiY28XUWCTmBcJ0ACzKqx/iwdIREfo1fwExIlkA=="], 452 | 453 | "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="], 454 | 455 | "@types/react-dom": ["@types/react-dom@19.1.6", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw=="], 456 | 457 | "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], 458 | 459 | "@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="], 460 | 461 | "@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], 462 | 463 | "@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], 464 | 465 | "@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], 466 | 467 | "@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], 468 | 469 | "@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], 470 | 471 | "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], 472 | 473 | "acorn-walk": ["acorn-walk@8.3.4", "", { "dependencies": { "acorn": "^8.11.0" } }, "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g=="], 474 | 475 | "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], 476 | 477 | "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], 478 | 479 | "bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="], 480 | 481 | "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], 482 | 483 | "caniuse-lite": ["caniuse-lite@1.0.30001727", "", {}, "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q=="], 484 | 485 | "chai": ["chai@5.2.1", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A=="], 486 | 487 | "check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], 488 | 489 | "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], 490 | 491 | "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], 492 | 493 | "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], 494 | 495 | "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="], 496 | 497 | "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], 498 | 499 | "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], 500 | 501 | "color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="], 502 | 503 | "commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], 504 | 505 | "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], 506 | 507 | "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="], 508 | 509 | "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="], 510 | 511 | "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="], 512 | 513 | "d3-format": ["d3-format@3.1.0", "", {}, "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA=="], 514 | 515 | "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], 516 | 517 | "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="], 518 | 519 | "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="], 520 | 521 | "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="], 522 | 523 | "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="], 524 | 525 | "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="], 526 | 527 | "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="], 528 | 529 | "debounce": ["debounce@1.2.1", "", {}, "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="], 530 | 531 | "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], 532 | 533 | "decimal.js-light": ["decimal.js-light@2.5.1", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="], 534 | 535 | "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], 536 | 537 | "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], 538 | 539 | "dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="], 540 | 541 | "duplexer": ["duplexer@0.1.2", "", {}, "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="], 542 | 543 | "effect": ["effect@3.16.12", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg=="], 544 | 545 | "enhanced-resolve": ["enhanced-resolve@5.18.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ=="], 546 | 547 | "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], 548 | 549 | "esbuild": ["esbuild@0.25.6", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.6", "@esbuild/android-arm": "0.25.6", "@esbuild/android-arm64": "0.25.6", "@esbuild/android-x64": "0.25.6", "@esbuild/darwin-arm64": "0.25.6", "@esbuild/darwin-x64": "0.25.6", "@esbuild/freebsd-arm64": "0.25.6", "@esbuild/freebsd-x64": "0.25.6", "@esbuild/linux-arm": "0.25.6", "@esbuild/linux-arm64": "0.25.6", "@esbuild/linux-ia32": "0.25.6", "@esbuild/linux-loong64": "0.25.6", "@esbuild/linux-mips64el": "0.25.6", "@esbuild/linux-ppc64": "0.25.6", "@esbuild/linux-riscv64": "0.25.6", "@esbuild/linux-s390x": "0.25.6", "@esbuild/linux-x64": "0.25.6", "@esbuild/netbsd-arm64": "0.25.6", "@esbuild/netbsd-x64": "0.25.6", "@esbuild/openbsd-arm64": "0.25.6", "@esbuild/openbsd-x64": "0.25.6", "@esbuild/openharmony-arm64": "0.25.6", "@esbuild/sunos-x64": "0.25.6", "@esbuild/win32-arm64": "0.25.6", "@esbuild/win32-ia32": "0.25.6", "@esbuild/win32-x64": "0.25.6" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg=="], 550 | 551 | "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], 552 | 553 | "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], 554 | 555 | "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], 556 | 557 | "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], 558 | 559 | "expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="], 560 | 561 | "fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="], 562 | 563 | "fast-equals": ["fast-equals@5.2.2", "", {}, "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw=="], 564 | 565 | "fdir": ["fdir@6.4.6", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="], 566 | 567 | "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], 568 | 569 | "find-my-way-ts": ["find-my-way-ts@0.1.6", "", {}, "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA=="], 570 | 571 | "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 572 | 573 | "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], 574 | 575 | "graphql": ["graphql@16.11.0", "", {}, "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw=="], 576 | 577 | "graphql-request": ["graphql-request@7.2.0", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.2.0" }, "peerDependencies": { "graphql": "14 - 16" } }, "sha512-0GR7eQHBFYz372u9lxS16cOtEekFlZYB2qOyq8wDvzRmdRSJ0mgUVX1tzNcIzk3G+4NY+mGtSz411wZdeDF/+A=="], 578 | 579 | "gzip-size": ["gzip-size@6.0.0", "", { "dependencies": { "duplexer": "^0.1.2" } }, "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q=="], 580 | 581 | "html-escaper": ["html-escaper@2.0.2", "", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="], 582 | 583 | "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], 584 | 585 | "is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="], 586 | 587 | "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], 588 | 589 | "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], 590 | 591 | "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], 592 | 593 | "is-plain-object": ["is-plain-object@5.0.0", "", {}, "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="], 594 | 595 | "jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="], 596 | 597 | "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], 598 | 599 | "lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="], 600 | 601 | "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="], 602 | 603 | "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="], 604 | 605 | "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="], 606 | 607 | "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="], 608 | 609 | "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="], 610 | 611 | "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="], 612 | 613 | "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="], 614 | 615 | "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="], 616 | 617 | "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="], 618 | 619 | "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="], 620 | 621 | "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], 622 | 623 | "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], 624 | 625 | "loupe": ["loupe@3.1.4", "", {}, "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg=="], 626 | 627 | "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], 628 | 629 | "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], 630 | 631 | "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], 632 | 633 | "minizlib": ["minizlib@3.0.2", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA=="], 634 | 635 | "mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="], 636 | 637 | "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], 638 | 639 | "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 640 | 641 | "msgpackr": ["msgpackr@1.11.4", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-uaff7RG9VIC4jacFW9xzL3jc0iM32DNHe4jYVycBcjUePT/Klnfj7pqtWJt9khvDFizmjN2TlYniYmSS2LIaZg=="], 642 | 643 | "msgpackr-extract": ["msgpackr-extract@3.0.3", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA=="], 644 | 645 | "multipasta": ["multipasta@0.2.5", "", {}, "sha512-c8eMDb1WwZcE02WVjHoOmUVk7fnKU/RmUcosHACglrWAuPQsEJv+E8430sXj6jNc1jHw0zrS16aCjQh4BcEb4A=="], 646 | 647 | "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], 648 | 649 | "next": ["next@15.4.0-canary.127", "", { "dependencies": { "@next/env": "15.4.0-canary.127", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.4.0-canary.127", "@next/swc-darwin-x64": "15.4.0-canary.127", "@next/swc-linux-arm64-gnu": "15.4.0-canary.127", "@next/swc-linux-arm64-musl": "15.4.0-canary.127", "@next/swc-linux-x64-gnu": "15.4.0-canary.127", "@next/swc-linux-x64-musl": "15.4.0-canary.127", "@next/swc-win32-arm64-msvc": "15.4.0-canary.127", "@next/swc-win32-x64-msvc": "15.4.0-canary.127", "sharp": "^0.34.1" }, "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" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-Fy/BtW9q+uwgZmCReHEKCbZVDEtvNbdExH9UF8Rl3C2EPpq+BBiIes0ogEKOFh+6fYLzgL+X3v/T0GwBgM0b3A=="], 650 | 651 | "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="], 652 | 653 | "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="], 654 | 655 | "number-flow": ["number-flow@0.5.8", "", { "dependencies": { "esm-env": "^1.1.4" } }, "sha512-FPr1DumWyGi5Nucoug14bC6xEz70A1TnhgSHhKyfqjgji2SOTz+iLJxKtv37N5JyJbteGYCm6NQ9p1O4KZ7iiA=="], 656 | 657 | "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], 658 | 659 | "opener": ["opener@1.5.2", "", { "bin": { "opener": "bin/opener-bin.js" } }, "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A=="], 660 | 661 | "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], 662 | 663 | "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], 664 | 665 | "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], 666 | 667 | "picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], 668 | 669 | "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], 670 | 671 | "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], 672 | 673 | "pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="], 674 | 675 | "react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="], 676 | 677 | "react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="], 678 | 679 | "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], 680 | 681 | "react-smooth": ["react-smooth@4.0.4", "", { "dependencies": { "fast-equals": "^5.0.1", "prop-types": "^15.8.1", "react-transition-group": "^4.4.5" }, "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" } }, "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q=="], 682 | 683 | "react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="], 684 | 685 | "recharts": ["recharts@2.15.4", "", { "dependencies": { "clsx": "^2.0.0", "eventemitter3": "^4.0.1", "lodash": "^4.17.21", "react-is": "^18.3.1", "react-smooth": "^4.0.4", "recharts-scale": "^0.4.4", "tiny-invariant": "^1.3.1", "victory-vendor": "^36.6.8" }, "peerDependencies": { "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw=="], 686 | 687 | "recharts-scale": ["recharts-scale@0.4.5", "", { "dependencies": { "decimal.js-light": "^2.4.1" } }, "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w=="], 688 | 689 | "reselect": ["reselect@5.1.1", "", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="], 690 | 691 | "rollup": ["rollup@4.44.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.44.2", "@rollup/rollup-android-arm64": "4.44.2", "@rollup/rollup-darwin-arm64": "4.44.2", "@rollup/rollup-darwin-x64": "4.44.2", "@rollup/rollup-freebsd-arm64": "4.44.2", "@rollup/rollup-freebsd-x64": "4.44.2", "@rollup/rollup-linux-arm-gnueabihf": "4.44.2", "@rollup/rollup-linux-arm-musleabihf": "4.44.2", "@rollup/rollup-linux-arm64-gnu": "4.44.2", "@rollup/rollup-linux-arm64-musl": "4.44.2", "@rollup/rollup-linux-loongarch64-gnu": "4.44.2", "@rollup/rollup-linux-powerpc64le-gnu": "4.44.2", "@rollup/rollup-linux-riscv64-gnu": "4.44.2", "@rollup/rollup-linux-riscv64-musl": "4.44.2", "@rollup/rollup-linux-s390x-gnu": "4.44.2", "@rollup/rollup-linux-x64-gnu": "4.44.2", "@rollup/rollup-linux-x64-musl": "4.44.2", "@rollup/rollup-win32-arm64-msvc": "4.44.2", "@rollup/rollup-win32-ia32-msvc": "4.44.2", "@rollup/rollup-win32-x64-msvc": "4.44.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg=="], 692 | 693 | "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], 694 | 695 | "seedrandom": ["seedrandom@3.0.5", "", {}, "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg=="], 696 | 697 | "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], 698 | 699 | "sharp": ["sharp@0.34.3", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.4", "semver": "^7.7.2" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.3", "@img/sharp-darwin-x64": "0.34.3", "@img/sharp-libvips-darwin-arm64": "1.2.0", "@img/sharp-libvips-darwin-x64": "1.2.0", "@img/sharp-libvips-linux-arm": "1.2.0", "@img/sharp-libvips-linux-arm64": "1.2.0", "@img/sharp-libvips-linux-ppc64": "1.2.0", "@img/sharp-libvips-linux-s390x": "1.2.0", "@img/sharp-libvips-linux-x64": "1.2.0", "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", "@img/sharp-libvips-linuxmusl-x64": "1.2.0", "@img/sharp-linux-arm": "0.34.3", "@img/sharp-linux-arm64": "0.34.3", "@img/sharp-linux-ppc64": "0.34.3", "@img/sharp-linux-s390x": "0.34.3", "@img/sharp-linux-x64": "0.34.3", "@img/sharp-linuxmusl-arm64": "0.34.3", "@img/sharp-linuxmusl-x64": "0.34.3", "@img/sharp-wasm32": "0.34.3", "@img/sharp-win32-arm64": "0.34.3", "@img/sharp-win32-ia32": "0.34.3", "@img/sharp-win32-x64": "0.34.3" } }, "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg=="], 700 | 701 | "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], 702 | 703 | "simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="], 704 | 705 | "sirv": ["sirv@2.0.4", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ=="], 706 | 707 | "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], 708 | 709 | "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], 710 | 711 | "std-env": ["std-env@3.9.0", "", {}, "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw=="], 712 | 713 | "strip-literal": ["strip-literal@3.0.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA=="], 714 | 715 | "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], 716 | 717 | "tabbable": ["tabbable@6.2.0", "", {}, "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="], 718 | 719 | "tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="], 720 | 721 | "tailwindcss": ["tailwindcss@4.1.11", "", {}, "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA=="], 722 | 723 | "tapable": ["tapable@2.2.2", "", {}, "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="], 724 | 725 | "tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="], 726 | 727 | "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], 728 | 729 | "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], 730 | 731 | "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], 732 | 733 | "tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="], 734 | 735 | "tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], 736 | 737 | "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], 738 | 739 | "tinyspy": ["tinyspy@4.0.3", "", {}, "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A=="], 740 | 741 | "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], 742 | 743 | "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], 744 | 745 | "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 746 | 747 | "tw-animate-css": ["tw-animate-css@1.3.5", "", {}, "sha512-t3u+0YNoloIhj1mMXs779P6MO9q3p3mvGn4k1n3nJPqJw/glZcuijG2qTSN4z4mgNRfW5ZC3aXJFLwDtiipZXA=="], 748 | 749 | "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], 750 | 751 | "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], 752 | 753 | "use-sync-external-store": ["use-sync-external-store@1.5.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A=="], 754 | 755 | "uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], 756 | 757 | "victory-vendor": ["victory-vendor@36.9.2", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ=="], 758 | 759 | "vite": ["vite@7.0.3", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", "picomatch": "^4.0.2", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-y2L5oJZF7bj4c0jgGYgBNSdIu+5HF+m68rn2cQXFbGoShdhV1phX9rbnxy9YXj82aS8MMsCLAAFkRxZeWdldrQ=="], 760 | 761 | "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], 762 | 763 | "vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "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" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], 764 | 765 | "web": ["web@workspace:packages/web"], 766 | 767 | "webpack-bundle-analyzer": ["webpack-bundle-analyzer@4.10.1", "", { "dependencies": { "@discoveryjs/json-ext": "0.5.7", "acorn": "^8.0.4", "acorn-walk": "^8.0.0", "commander": "^7.2.0", "debounce": "^1.2.1", "escape-string-regexp": "^4.0.0", "gzip-size": "^6.0.0", "html-escaper": "^2.0.2", "is-plain-object": "^5.0.0", "opener": "^1.5.2", "picocolors": "^1.0.0", "sirv": "^2.0.3", "ws": "^7.3.1" }, "bin": { "webpack-bundle-analyzer": "lib/bin/analyzer.js" } }, "sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ=="], 768 | 769 | "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], 770 | 771 | "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], 772 | 773 | "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], 774 | 775 | "@effect/experimental/uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], 776 | 777 | "@effect/sql/uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], 778 | 779 | "@parcel/watcher/detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], 780 | 781 | "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.4", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.3", "tslib": "^2.4.0" }, "bundled": true }, "sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g=="], 782 | 783 | "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.4.4", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg=="], 784 | 785 | "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw=="], 786 | 787 | "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" }, "bundled": true }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], 788 | 789 | "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="], 790 | 791 | "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 792 | 793 | "bun-types/@types/node": ["@types/node@24.0.12", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-LtOrbvDf5ndC9Xi+4QZjVL0woFymF/xSTKZKPgrrl7H7XoeDvnD+E2IclKVDyaK9UM756W/3BXqSU+JEHopA9g=="], 794 | 795 | "loose-envify/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], 796 | 797 | "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 798 | 799 | "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], 800 | 801 | "prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], 802 | 803 | "webpack-bundle-analyzer/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], 804 | 805 | "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ=="], 806 | 807 | "bun-types/@types/node/undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], 808 | } 809 | } 810 | --------------------------------------------------------------------------------