diff --git a/.github/workflows/vercel-preview-identity.yml b/.github/workflows/vercel-preview-identity.yml index 8265531..1a1813a 100644 --- a/.github/workflows/vercel-preview-identity.yml +++ b/.github/workflows/vercel-preview-identity.yml @@ -4,6 +4,8 @@ on: paths: - "apps/demo-identity-app/**" - "packages/citizen-sdk/**" + - "packages/streaming-sdk/**" + - "packages/react-hooks/**" - ".github/workflows/*identity*.yml" pull_request: types: [opened, synchronize, edited, ready_for_review] @@ -12,6 +14,8 @@ on: paths: - "apps/demo-identity-app/**" - "packages/citizen-sdk/**" + - "packages/streaming-sdk/**" + - "packages/react-hooks/**" - ".github/workflows/*identity*.yml" env: VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} @@ -33,6 +37,9 @@ jobs: - run: npm install --global turbo@latest - run: npm install --global vite@latest - run: yarn install --immutable + # Build dependencies first using turbo + - name: Build dependencies + run: turbo build --filter=demo-identity-app... # Get env vars - run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_DEPLOY_TOKEN }} working-directory: apps/demo-identity-app diff --git a/.gitignore b/.gitignore index a7e62ae..8c4d3cd 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,9 @@ node_modules .env.development.local .env.test.local .env.production.local +**/.env +**/.env.local +**/.env.*.local # Testing coverage @@ -39,4 +42,5 @@ yarn-error.log* .yarn/install-state.gz apps/engagement-app/tsconfig.tsbuildinfo .vscode/settings.json -e2e.log + +docs diff --git a/.vercelignore b/.vercelignore new file mode 100644 index 0000000..294c2c2 --- /dev/null +++ b/.vercelignore @@ -0,0 +1,16 @@ +node_modules +.git +.env.*.local +.DS_Store +*.md +coverage +artifacts +cache +dist +.next +.turbo +.eslintcache +.vercel +packages/*/dist +apps/*/dist +apps/*/.next diff --git a/apps/demo-identity-app/.env.example b/apps/demo-identity-app/.env.example new file mode 100644 index 0000000..3626dc8 --- /dev/null +++ b/apps/demo-identity-app/.env.example @@ -0,0 +1 @@ +VITE_GRAPH_API_KEY= diff --git a/apps/demo-identity-app/.gitignore b/apps/demo-identity-app/.gitignore index 4b96642..f223be1 100644 --- a/apps/demo-identity-app/.gitignore +++ b/apps/demo-identity-app/.gitignore @@ -1,4 +1,5 @@ .vercel .certs .yalc -yalc.lock \ No newline at end of file +yalc.lock +.env \ No newline at end of file diff --git a/apps/demo-identity-app/.vercelignore b/apps/demo-identity-app/.vercelignore new file mode 100644 index 0000000..721b386 --- /dev/null +++ b/apps/demo-identity-app/.vercelignore @@ -0,0 +1,10 @@ +node_modules +.git +.env.*.local +.DS_Store +*.md +coverage +.eslintcache +.turbo +src/**/*.test.ts +src/**/*.test.tsx diff --git a/apps/demo-identity-app/README.md b/apps/demo-identity-app/README.md index 2844eb2..eebdb3b 100644 --- a/apps/demo-identity-app/README.md +++ b/apps/demo-identity-app/README.md @@ -68,4 +68,12 @@ To utilize the `citizen-sdk` within your application: } ``` -For more examples and detailed usage, refer to the [citizen-sdk README](packages/citizen-sdk/README.md). +For more examples and detailed usage, refer to the [citizen-sdk README](../../packages/citizen-sdk/README.md). + +### Developing with `streaming-sdk` + +The demo app includes an interactive test page for Superfluid streams: + +1. **Test UI**: Access it at [http://localhost:3000/streaming](http://localhost:3000/streaming). +2. **Guide**: For detailed instructions on testing streams and GDA pools, see the [Streaming Guide](./STREAMING.md). +3. **Environment**: Ensure you have a `VITE_GRAPH_API_KEY` configured in your `.env` for data fetching. diff --git a/apps/demo-identity-app/STREAMING.md b/apps/demo-identity-app/STREAMING.md new file mode 100644 index 0000000..7227c7f --- /dev/null +++ b/apps/demo-identity-app/STREAMING.md @@ -0,0 +1,46 @@ +# Streaming SDK Test Guide + +This page explains how to use the interactive test UI for the Superfluid Streaming SDK. + +## Location +The test page is built into the demo app: +`src/components/StreamingTestPage.tsx` + +## Quick Start +To start the development server, you can run the standard dev command from the root: + +```bash +yarn dev +``` + +Alternatively, if you want to run just the demo app: + +1. `cd apps/demo-identity-app` +2. `yarn dev` + +Once started, open [http://localhost:3000/streaming](http://localhost:3000/streaming) in your browser. + +## Features + +### Money Streaming +- Create: Set up a new stream by entering a receiver address and amount. The SDK handles the flow rate math for you. +- Update: Change the flow rate for an existing recipient. +- Delete: Stop an active stream. + +### Distribution Pools (GDA) +- Connect: Join a distribution pool to start receiving funds. +- Disconnect: Leave a pool when you no longer want to receive distributions. +- Visibility: Check your connection status and current units directly in the UI. + +## Testing the Frontend +To properly test the data fetching: +1. Make sure your `.env` file in the app folder has a `VITE_GRAPH_API_KEY`. +2. Connect your wallet and switch to a supported network (Celo is best for G$ testing). +3. If you're on Base, you can view SUP reserve holdings to verify the subgraph integration. + +## Unit Tests +If you want to run the underlying logic tests: +```bash +cd packages/streaming-sdk +npm test +``` diff --git a/apps/demo-identity-app/eslint.config.js b/apps/demo-identity-app/eslint.config.js new file mode 100644 index 0000000..5555203 --- /dev/null +++ b/apps/demo-identity-app/eslint.config.js @@ -0,0 +1,29 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + js.configs.recommended, + ...tseslint.configs.recommended, + { + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) \ No newline at end of file diff --git a/apps/demo-identity-app/package.json b/apps/demo-identity-app/package.json index 66cc7a7..2c44b6a 100644 --- a/apps/demo-identity-app/package.json +++ b/apps/demo-identity-app/package.json @@ -1,18 +1,20 @@ { "name": "demo-identity-app", "version": "1.0.0", + "type": "module", "private": true, "scripts": { "dev": "vite --port 3000", "build": "vite build", "startS": "cross-env HTTPS=true SSL_CRT_FILE='./.certs/ssl.crt' SSL_KEY_FILE='./.certs/ssl.key' PORT=1122 vite --port 1122", "preview": "vite preview", - "lint": "eslint . --ext .ts,.tsx", + "lint": "eslint src --cache", "format": "prettier --write ." }, "dependencies": { "@goodsdks/citizen-sdk": "*", "@goodsdks/react-hooks": "*", + "@goodsdks/streaming-sdk": "*", "@reown/appkit": "^1.7.2", "@reown/appkit-adapter-wagmi": "^1.7.2", "@reown/appkit-wallet": "^1.7.2", @@ -34,6 +36,7 @@ "zod": "^3.24.2" }, "devDependencies": { + "@eslint/js": "8.57.0", "@tamagui/babel-plugin": "^1.125.22", "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", @@ -46,10 +49,13 @@ "eslint-config-prettier": "^8.10.0", "eslint-plugin-react": "^7.37.4", "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "0.4.12", + "globals": "15.0.0", "postcss": "^8.5.3", "prettier": "^3.5.3", "sass": "^1.85.1", "typescript": "^5.8.2", + "typescript-eslint": "8.0.0", "vite": "6.3.5" } } diff --git a/apps/demo-identity-app/src/App.tsx b/apps/demo-identity-app/src/App.tsx index 8d30e72..91d3a27 100644 --- a/apps/demo-identity-app/src/App.tsx +++ b/apps/demo-identity-app/src/App.tsx @@ -11,14 +11,16 @@ import { Stack, } from "tamagui" import { config } from "@tamagui/config/v3" -import { useLocation } from "react-router-dom" +import { useLocation, Routes, Route, Link } from "react-router-dom" import { useAccount } from "wagmi" import { useIdentitySDK } from "@goodsdks/react-hooks" +import { XStack } from "tamagui" import { VerifyButton } from "./components/VerifyButton" import { IdentityCard } from "./components/IdentityCard" import { SigningModal } from "./components/SigningModal" import { ClaimButton } from "./components/ClaimButton" +import { StreamingTestPage } from "./components/StreamingTestPage" const tamaguiConfig = createTamagui(config) @@ -99,174 +101,226 @@ const App: React.FC = () => { backgroundColor="#F7FAFC" alignItems="center" > - - - GoodDollar Identity Verification - - - {/* Disclaimer Section */} - - - Disclaimer: - This is a demo showing how to integrate a uniquely identified user - into your dApp using the GoodDollar protocol's Identity. This demo - uses the development contract.{" "} - + + + - Learn more about the sdk and how to integrate it here. - - - - - {/* User Interaction Section */} - - + + + + - {!isConnected ? ( - <> - - Please connect your wallet to proceed. - - - ) : null} - - + + 🌊 Streaming + + + + - {isConnected && (loadingWhitelist || loading) ? ( - - ) : null} + + } /> + + + GoodDollar Identity Verification + - {isConnected && - !loadingWhitelist && - (isVerified || isWhitelisted) && ( - - - - You are successfully verified and/or whitelisted. + {/* Disclaimer Section */} + + + Disclaimer: + This is a demo showing how to integrate a uniquely identified user + into your dApp using the GoodDollar protocol's Identity. This demo + uses the development contract.{" "} + + Learn more about the sdk and how to integrate it here. + - )} - {isConnected && - !loadingWhitelist && - !isVerified && - !isWhitelisted && - !error ? ( - - - - You need to verify your identity via GoodDollar to continue. - - - ) : null} + {/* User Interaction Section */} + + + {!isConnected ? ( + <> + + Please connect your wallet to proceed. + + + ) : null} + + - {isConnected && error ? ( - - Error initializing Identity SDK: {error} - - ) : null} - + {isConnected && (loadingWhitelist || loading) ? ( + + ) : null} - {/* Help Section */} - - - Need help? Visit our docs:{" "} - - GoodDollar Docs - - . - - - Or join our Developer Communities at:{" "} - - GoodBuilders Discord - - . - - - + {isConnected && + !loadingWhitelist && + (isVerified || isWhitelisted) && ( + + + + You are successfully verified and/or whitelisted. + + + )} - {/* Claim UBI Section */} - - - Claim Your Daily UBI - - {isConnected ? ( - - ) : ( - <> - - Please connect your wallet to claim your UBI. - - - - )} - + {isConnected && + !loadingWhitelist && + !isVerified && + !isWhitelisted && + !error ? ( + + + + You need to verify your identity via GoodDollar to continue. + + + ) : null} - setIsSigningModalOpen(false)} - /> + {isConnected && error ? ( + + Error initializing Identity SDK: {error} + + ) : null} + + + {/* Help Section */} + + + Need help? Visit our docs:{" "} + + GoodDollar Docs + + . + + + Or join our Developer Communities at:{" "} + + GoodBuilders Discord + + . + + + + {/* Claim UBI Section */} + + + Claim Your Daily UBI + + {isConnected ? ( + + ) : ( + <> + + Please connect your wallet to claim your UBI. + + + + )} + + + setIsSigningModalOpen(false)} + /> + + } + /> + ) diff --git a/apps/demo-identity-app/src/components/ClaimButton.tsx b/apps/demo-identity-app/src/components/ClaimButton.tsx index 89d7483..9d7f89f 100644 --- a/apps/demo-identity-app/src/components/ClaimButton.tsx +++ b/apps/demo-identity-app/src/components/ClaimButton.tsx @@ -18,7 +18,7 @@ export const ClaimButton: React.FC = () => { const [txHash, setTxHash] = useState(null) const { sdk: claimSDK, loading, error: sdkError } = useClaimSDK("development") const [sdk, setSdk] = useState(null) - const [claimAmount, setClaimAmount] = useState(null) + const [claimAmount, setClaimAmount] = useState(null) const [altClaimAvailable, setAltClaimAvailable] = useState(false) const [altChainId, setAltChainId] = useState(null) @@ -53,8 +53,9 @@ export const ClaimButton: React.FC = () => { setAltClaimAvailable(altClaimAvailable) setAltChainId(altClaimAvailable ? (altChainId ?? null) : null) setSdk(sdk) - } catch (err: any) { - setError(err.message || "Failed to fetch entitlement.") + } catch (err) { + const error = err as Error + setError(error.message || "Failed to fetch entitlement.") } finally { setIsLoading(false) } @@ -65,7 +66,7 @@ export const ClaimButton: React.FC = () => { } else if (!sdk && !loading) { initializeSDK() } - }, [address, claimAmount, claimSDK, loading, chainId]) + }, [address, claimAmount, claimSDK, loading, chainId, sdk, sdkError]) const handleClaim = useCallback(async () => { if (!sdk) { @@ -89,14 +90,15 @@ export const ClaimButton: React.FC = () => { if (!tx) return setTxHash(tx.transactionHash) - } catch (err: any) { - console.error("Claim failed:", err) - setError(err.message || "An unexpected error occurred.") + } catch (err) { + const error = err as Error + console.error("Claim failed:", error) + setError(error.message || "An unexpected error occurred.") } finally { setIsClaiming(false) setClaimAmount(null) } - }, [sdk, claimAmount, chainId]) + }, [sdk]) return ( diff --git a/apps/demo-identity-app/src/components/StreamingTestPage.tsx b/apps/demo-identity-app/src/components/StreamingTestPage.tsx new file mode 100644 index 0000000..a3ae3a3 --- /dev/null +++ b/apps/demo-identity-app/src/components/StreamingTestPage.tsx @@ -0,0 +1,389 @@ +import React, { useState } from "react" +import { + View, + Text, + YStack, + XStack, + Button, + ScrollView, + Separator, + Spinner, + Input, +} from "tamagui" +import { useAccount, usePublicClient, useSwitchChain } from "wagmi" +import { + useCreateStream, + useUpdateStream, + useDeleteStream, + useStreamList, + useGDAPools, + useSupReserves, + useConnectToPool, + useDisconnectFromPool, +} from "@goodsdks/react-hooks" +import { + calculateFlowRate, + formatFlowRate, + getG$Token, + SupportedChains, + type Environment, + type StreamInfo, + type GDAPool, + type SUPReserveLocker, +} from "@goodsdks/streaming-sdk" +import { parseEther, type Address } from "viem" + +/** + * Sub-components + */ +const SectionCard: React.FC> = ({ + children, + gap = "$3", + bg = "white", + title, +}) => ( + + {title && ( + + {title} + + )} + {children} + +) + +const OperationSection: React.FC<{ + title: string + buttonText: string + buttonColor: string + isLoading: boolean + onAction: (receiver: string, amount: string) => void + showAmount?: boolean + timeUnit?: string + setTimeUnit?: (unit: "hour" | "day" | "month") => void +}> = ({ + title, + buttonText, + buttonColor, + isLoading, + onAction, + showAmount = true, + timeUnit, + setTimeUnit, +}) => { + const [receiver, setReceiver] = useState("") + const [amount, setAmount] = useState("10") + + return ( + + + {showAmount && ( + + + {setTimeUnit && ( + + {(["hour", "day", "month"] as const).map(unit => ( + + ))} + + )} + + )} + + + ) + } + +const ActiveStreamsList: React.FC<{ + streams: StreamInfo[] + isLoading: boolean + onRefresh: () => void +}> = ({ streams, isLoading, onRefresh }) => ( + + + + + {isLoading ? : (streams && streams.length > 0) ? ( + + {streams.map((s, i) => ( + + + To: + {s.receiver?.slice(0, 10)}... + + + Flow Rate: + {formatFlowRate(s.flowRate, "month")} + + + ))} + + ) : No active streams found} + +) + +const GDAPoolsSection: React.FC<{ + pools: GDAPool[] + isLoading: boolean + poolAddress: string + setPoolAddress: (addr: string) => void + onConnect: () => void + onDisconnect: () => void + isConnecting: boolean + isDisconnecting: boolean +}> = ({ pools, isLoading, poolAddress, setPoolAddress, onConnect, onDisconnect, isConnecting, isDisconnecting }) => ( + + + + + + + {isLoading ? : (pools && pools.length > 0) ? ( + + {pools.slice(0, 5).map((p, i) => ( + setPoolAddress(p.id)} + hoverStyle={{ backgroundColor: "#EDF2F7" }} + cursor="pointer" + borderWidth={poolAddress === p.id ? 1 : 0} + borderColor="$blue10" + > + {p.id.slice(0, 10)}... Admin: {p.admin?.slice(0, 6)} + Flow Rate: {formatFlowRate(p.flowRate, "month")} + + ))} + + ) : No pools found} + + +) + +/** + * Mutation Helper for Transactions + */ +function runMutationWithAlerts( + mutate: (args: TArgs, options?: { onSuccess?: (data: unknown) => void; onError?: (error: unknown) => void }) => void, + args: TArgs, + { onSuccess, successMessage }: { onSuccess?: (hash: string) => void; successMessage?: string } = {}, +) { + try { + mutate(args, { + onSuccess: (data: unknown) => { + const hash = data as string + if (successMessage) { + alert(`${successMessage} Transaction: ${hash}`) + } + onSuccess?.(hash) + }, + onError: (error: unknown) => { + const err = error as Error + console.error(err) + alert(`Error: ${err.message}`) + }, + }) + } catch (error) { + const err = error as Error + console.error(err) + alert(`Error: ${err.message}`) + } +} + +export const StreamingTestPage: React.FC = () => { + const { address, isConnected } = useAccount() + const publicClient = usePublicClient() + const { switchChain } = useSwitchChain() + + const [environment, setEnvironment] = useState<"production" | "staging" | "development">("production") + const [poolAddress, setPoolAddress] = useState("") + const [timeUnit, setTimeUnit] = useState<"month" | "day" | "hour">("month") + const apiKey = import.meta.env.VITE_GRAPH_API_KEY + + const { mutate: createStream, isLoading: isCreating } = useCreateStream() + const { mutate: updateStream, isLoading: isUpdating } = useUpdateStream() + const { mutate: deleteStream, isLoading: isDeleting } = useDeleteStream() + const { mutate: connectToPool, isLoading: isConnecting } = useConnectToPool() + const { mutate: disconnectFromPool, isLoading: isDisconnecting } = useDisconnectFromPool() + + const { + data: streams, + isLoading: streamsLoading, + refetch: refetchStreams, + } = useStreamList({ + account: address as Address, + environment, + enabled: !!address, + }) as { data: StreamInfo[] | undefined, isLoading: boolean, refetch: () => void } + + const { data: pools, isLoading: poolsLoading } = useGDAPools({ + environment, + enabled: !!address, + }) as { data: GDAPool[] | undefined, isLoading: boolean } + + const { data: supReserves, isLoading: supLoading } = useSupReserves({ + apiKey, + enabled: isConnected && environment === "production", + }) + + const chainId = publicClient?.chain?.id + + const G$_TOKEN = chainId ? getG$Token(chainId, environment) : undefined + + const handleAction = ( + receiver: string, + amount: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + mutation: (args: UseCreateStreamParams | UseUpdateStreamParams | UseDeleteStreamParams, options?: any) => void, + msg: string + ) => { + if (!receiver || (!amount && msg !== "Stream deleted!")) return alert("Please fill in all fields") + if (!G$_TOKEN) return alert('G$ token not configured for this chain/environment') + const flowRate = amount ? calculateFlowRate(parseEther(amount), timeUnit) : undefined + + const args: UseCreateStreamParams & UseUpdateStreamParams & UseDeleteStreamParams = { + receiver: receiver as Address, + token: G$_TOKEN as Address, + environment, + } + if (msg === "Stream created!") args.flowRate = flowRate + if (msg === "Stream updated!") args.newFlowRate = flowRate + + runMutationWithAlerts(mutation, args, { + onSuccess: () => { + refetchStreams() + }, + successMessage: msg + }) + } + + if (!isConnected) { + return ( + + Please connect your wallet to test the Streaming SDK + + + ) + } + + return ( + + + {/* Header Info */} + + + + {publicClient?.chain?.name || "Unknown"} ({chainId}) + + + {[ + { id: SupportedChains.CELO, name: "Celo" }, + { id: SupportedChains.BASE, name: "Base" }, + { id: SupportedChains.CELO_ALFAJORES, name: "Alfajores" }, + { id: 137 as SupportedChains, name: "Polygon" }, + ].map(c => ( + + ))} + + + + + {["production", "staging", "development"].map(env => ( + + ))} + + Connected as {address?.slice(0, 6)}...{address?.slice(-4)} + + + + + + Streaming SDK + G$ Token: {G$_TOKEN || "Not configured"} + {chainId === SupportedChains.BASE && ( + SUP streaming / no G$ on Base + )} + + + {/* Operations */} + handleAction(r, a, createStream, "Stream created!")} timeUnit={timeUnit} setTimeUnit={setTimeUnit} disabled={!G$_TOKEN} /> + handleAction(r, a, updateStream, "Stream updated!")} disabled={!G$_TOKEN} /> + handleAction(r, "", deleteStream, "Stream deleted!")} showAmount={false} disabled={!G$_TOKEN} /> + + + + {/* Data Displays */} + refetchStreams()} /> + + runMutationWithAlerts(connectToPool, { poolAddress: poolAddress as Address }, { successMessage: "Connected!" })} + onDisconnect={() => runMutationWithAlerts(disconnectFromPool, { poolAddress: poolAddress as Address }, { successMessage: "Disconnected!" })} + isConnecting={isConnecting} + isDisconnecting={isDisconnecting} + /> + + + (Only on Base mainnet) + {supLoading ? : (supReserves && supReserves.length > 0) ? ( + + {supReserves.slice(0, 5).map((l: SUPReserveLocker, i: number) => ( + + Locker: {l.id?.slice(0, 10)}... + Owner: {l.lockerOwner?.slice(0, 10)}... + + ))} + + ) : No SUP lockers found (Check API Key)} + + + + ) +} diff --git a/apps/demo-identity-app/src/components/VerifyButton.tsx b/apps/demo-identity-app/src/components/VerifyButton.tsx index 9a556de..8c4ede6 100644 --- a/apps/demo-identity-app/src/components/VerifyButton.tsx +++ b/apps/demo-identity-app/src/components/VerifyButton.tsx @@ -24,6 +24,7 @@ export const VerifyButton: React.FC = ({ ) window.location.href = fvLink + onVerificationSuccess() } catch (error) { console.error("Verification failed:", error) // Handle error (e.g., show toast) diff --git a/apps/demo-identity-app/src/config.tsx b/apps/demo-identity-app/src/config.tsx index 213c098..edf3af2 100644 --- a/apps/demo-identity-app/src/config.tsx +++ b/apps/demo-identity-app/src/config.tsx @@ -1,6 +1,6 @@ import { createAppKit } from "@reown/appkit/react" import { http, WagmiProvider } from "wagmi" -import { celo, fuse, xdc, type AppKitNetwork } from "@reown/appkit/networks" +import { celo, fuse, xdc, base, celoAlfajores, type AppKitNetwork } from "@reown/appkit/networks" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" import { WagmiAdapter } from "@reown/appkit-adapter-wagmi" import React, { ReactNode } from "react" @@ -16,7 +16,7 @@ const metadata = { icons: ["https://avatars.githubusercontent.com/u/179229932"], } -const networks: [AppKitNetwork, ...AppKitNetwork[]] = [celo, fuse, xdc] +const networks: [AppKitNetwork, ...AppKitNetwork[]] = [celo, fuse, xdc, base, celoAlfajores] const wagmiAdapter = new WagmiAdapter({ networks, @@ -26,6 +26,8 @@ const wagmiAdapter = new WagmiAdapter({ [xdc.id]: http("https://rpc.ankr.com/xdc"), [fuse.id]: http("https://rpc.fuse.io"), [celo.id]: http("https://forno.celo.org"), + [base.id]: http("https://mainnet.base.org"), + [celoAlfajores.id]: http("https://alfajores-forno.celo-testnet.org"), }, }) diff --git a/apps/engagement-app/.vercelignore b/apps/engagement-app/.vercelignore new file mode 100644 index 0000000..a13d96b --- /dev/null +++ b/apps/engagement-app/.vercelignore @@ -0,0 +1,10 @@ +node_modules +.git +.env.*.local +.DS_Store +*.md +coverage +.eslintcache +.turbo +src/**/*.test.ts +src/**/*.test.tsx diff --git a/packages/react-hooks/README.md b/packages/react-hooks/README.md index afeeb4f..d62ec87 100644 --- a/packages/react-hooks/README.md +++ b/packages/react-hooks/README.md @@ -66,6 +66,19 @@ export const App = () => ( - Builds on `useIdentitySDK` and returns a ready `ClaimSDK` once identity checks resolve. - Surfaces entitlement errors via the returned `error` string. +### Streaming Hooks + +- `useStreamList({ account, environment, enabled })` + - Fetches all active streams for an account. +- `useGDAPools({ environment, enabled })` + - Lists all available distribution pools. +- `useSupReserves({ apiKey, environment, enabled })` + - Fetches SUP reserve holdings. **Requires `apiKey`** for Base mainnet (decentralized subgraph). +- `useCreateStream()`, `useUpdateStream()`, `useDeleteStream()` + - Mutators for managing 1-to-1 streams. +- `useConnectToPool()`, `useDisconnectFromPool()` + - Mutators for GDA pool memberships. + Both hooks re-run whenever the connected wallet, public client, or environment changes. ## Demo & Further Reading diff --git a/packages/react-hooks/package.json b/packages/react-hooks/package.json index 4f8525e..e698b03 100644 --- a/packages/react-hooks/package.json +++ b/packages/react-hooks/package.json @@ -37,6 +37,8 @@ }, "dependencies": { "@goodsdks/citizen-sdk": "*", + "@goodsdks/streaming-sdk": "workspace:*", + "@tanstack/react-query": "^4.36.1", "lz-string": "^1.5.0", "react": "^19.1.1", "tsup": "^8.4.0" diff --git a/packages/react-hooks/src/index.ts b/packages/react-hooks/src/index.ts index 35b68e6..193ff02 100644 --- a/packages/react-hooks/src/index.ts +++ b/packages/react-hooks/src/index.ts @@ -1 +1,2 @@ export * from "./citizen-sdk" +export * from "./streaming" diff --git a/packages/react-hooks/src/streaming/index.ts b/packages/react-hooks/src/streaming/index.ts new file mode 100644 index 0000000..e17e90f --- /dev/null +++ b/packages/react-hooks/src/streaming/index.ts @@ -0,0 +1,306 @@ +import { useMemo } from "react" +import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query" +import { type Address, type Hash } from "viem" +import { usePublicClient, useWalletClient } from "wagmi" +import { + StreamingSDK, + GdaSDK, + SubgraphClient, + SupportedChains, + type StreamInfo, + type GDAPool, + type PoolMembership, + type SUPReserveLocker, + type Environment, +} from "@goodsdks/streaming-sdk" + +/** + * Hook parameter interfaces + */ +export interface UseCreateStreamParams { + receiver: Address + token: Address + flowRate: bigint + userData?: `0x${string}` + environment?: Environment +} + +export interface UseUpdateStreamParams { + receiver: Address + token: Address + newFlowRate: bigint + userData?: `0x${string}` + environment?: Environment +} + +export interface UseDeleteStreamParams { + receiver: Address + token: Address + environment?: Environment +} + +export interface UseStreamListParams { + account: Address + direction?: "incoming" | "outgoing" | "all" + environment?: Environment + enabled?: boolean +} + +export interface UseGDAPoolsParams { + enabled?: boolean +} + +export interface UsePoolMembershipsParams { + account: Address + enabled?: boolean +} + +export interface UseConnectToPoolParams { + poolAddress: Address + userData?: `0x${string}` +} + +export interface UseDisconnectFromPoolParams { + poolAddress: Address + userData?: `0x${string}` +} + +export interface UseSupReservesParams { + environment?: Environment + apiKey?: string + enabled?: boolean +} + +/** + * React Hooks for Superfluid operations + */ +export function useCreateStream() { + const publicClient = usePublicClient() + const { data: walletClient } = useWalletClient() + const queryClient = useQueryClient() + + const sdks = useMemo(() => { + if (!publicClient) return new Map() + const envs = ["production", "staging", "development"] as const + const m = new Map() + for (const e of envs) { + try { + m.set(e, new StreamingSDK(publicClient, walletClient ? walletClient : undefined, { environment: e })) + } catch (err) { + // ignore + } + } + return m + }, [publicClient, walletClient]) + + return useMutation({ + mutationFn: async ({ + receiver, + token, + flowRate, + userData = "0x", + environment = "production", + }: UseCreateStreamParams): Promise => { + if (!publicClient) throw new Error("Public client not available") + if (!walletClient) throw new Error("Wallet client not available") + const sdk = sdks.get(environment) + if (!sdk) throw new Error("SDK not available for selected environment") + return sdk.createStream({ receiver, token, flowRate, userData }) + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["streams"] }) + }, + }) +} + +export function useUpdateStream() { + const publicClient = usePublicClient() + const { data: walletClient } = useWalletClient() + const queryClient = useQueryClient() + + const sdks = useMemo(() => { + if (!publicClient) return new Map() + const envs = ["production", "staging", "development"] as const + const m = new Map() + for (const e of envs) { + try { + m.set(e, new StreamingSDK(publicClient, walletClient as any, { environment: e })) + } catch (err) { + // ignore + } + } + return m + }, [publicClient, walletClient]) + + return useMutation({ + mutationFn: async ({ + receiver, + token, + newFlowRate, + userData = "0x", + environment = "production", + }: UseUpdateStreamParams): Promise => { + if (!publicClient) throw new Error("Public client not available") + if (!walletClient) throw new Error("Wallet client not available") + const sdk = sdks.get(environment) + if (!sdk) throw new Error("SDK not available for selected environment") + return sdk.updateStream({ receiver, token, newFlowRate, userData }) + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["streams"] }) + }, + }) +} + +export function useDeleteStream() { + const publicClient = usePublicClient() + const { data: walletClient } = useWalletClient() + const queryClient = useQueryClient() + + const sdks = useMemo(() => { + if (!publicClient) return new Map() + const envs = ["production", "staging", "development"] as const + const m = new Map() + for (const e of envs) { + try { + m.set(e, new StreamingSDK(publicClient, walletClient as any, { environment: e })) + } catch (err) { + // ignore + } + } + return m + }, [publicClient, walletClient]) + + return useMutation({ + mutationFn: async ({ + receiver, + token, + environment = "production", + }: UseDeleteStreamParams): Promise => { + if (!publicClient) throw new Error("Public client not available") + if (!walletClient) throw new Error("Wallet client not available") + const sdk = sdks.get(environment) + if (!sdk) throw new Error("SDK not available for selected environment") + return sdk.deleteStream({ receiver, token }) + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["streams"] }) + }, + }) +} + +export function useStreamList({ + account, + direction = "all", + environment = "production", + enabled = true, +}: UseStreamListParams) { + const publicClient = usePublicClient() + + return useQuery({ + queryKey: ["streams", account, direction, environment, publicClient?.chain?.id], + queryFn: async () => { + if (!publicClient) throw new Error("Public client not available") + const sdk = new StreamingSDK(publicClient, undefined, { environment }) + return sdk.getActiveStreams(account, direction) + }, + enabled: enabled && !!account && !!publicClient, + }) +} + +export function useGDAPools({ + enabled = true +}: UseGDAPoolsParams = {}) { + const publicClient = usePublicClient() + const sdk = useMemo(() => { + if (!publicClient) return null + return new GdaSDK(publicClient, undefined, { chainId: publicClient.chain?.id }) + }, [publicClient]) + + return useQuery({ + queryKey: ["gda-pools", publicClient?.chain?.id], + queryFn: async () => { + if (!sdk) throw new Error("Public client not available") + return sdk.getDistributionPools() + }, + enabled: enabled && !!publicClient, + }) +} + +export function usePoolMemberships({ + account, + enabled = true, +}: UsePoolMembershipsParams) { + const publicClient = usePublicClient() + const sdk = useMemo(() => { + if (!publicClient) return null + return new GdaSDK(publicClient) + }, [publicClient]) + + return useQuery({ + queryKey: ["gda-memberships", account, publicClient?.chain?.id], + queryFn: async () => { + if (!sdk) throw new Error("Public client not available") + return sdk.getPoolMemberships(account) + }, + enabled: enabled && !!publicClient && !!account, + }) +} + +export function useConnectToPool() { + const publicClient = usePublicClient() + const { data: walletClient } = useWalletClient() + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async ({ + poolAddress, + userData = "0x", + }: UseConnectToPoolParams): Promise => { + if (!publicClient) throw new Error("Public client not available") + if (!walletClient) throw new Error("Wallet client not available") + const sdk = new GdaSDK(publicClient, walletClient as any) + return sdk.connectToPool({ poolAddress, userData }) + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["gda-pools"] }) + queryClient.invalidateQueries({ queryKey: ["gda-memberships"] }) + }, + }) +} + +export function useDisconnectFromPool() { + const publicClient = usePublicClient() + const { data: walletClient } = useWalletClient() + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async ({ + poolAddress, + userData = "0x", + }: UseDisconnectFromPoolParams): Promise => { + if (!publicClient) throw new Error("Public client not available") + if (!walletClient) throw new Error("Wallet client not available") + const sdk = new GdaSDK(publicClient, walletClient as any) + return sdk.disconnectFromPool({ poolAddress, userData }) + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["gda-pools"] }) + queryClient.invalidateQueries({ queryKey: ["gda-memberships"] }) + }, + }) +} + +export function useSupReserves({ + apiKey, + enabled = true +}: UseSupReservesParams = {}) { + return useQuery({ + queryKey: ["sup-reserves", SupportedChains.BASE, apiKey], + queryFn: async () => { + const client = new SubgraphClient(SupportedChains.BASE, { apiKey }) + return client.querySUPReserves() + }, + enabled, + }) +} diff --git a/packages/streaming-sdk/README.md b/packages/streaming-sdk/README.md new file mode 100644 index 0000000..788d05d --- /dev/null +++ b/packages/streaming-sdk/README.md @@ -0,0 +1,512 @@ +# @goodsdks/streaming-sdk + +TypeScript SDK for interacting with Superfluid streams on Celo and Base, specifically optimized for G$ SuperTokens and GDA (General Distribution Agreement) pools. + +- **Stream Life-cycle**: Managed CRUD operations for 1-to-1 streams. +- **GDA Integration**: Support for Connecting/Disconnecting from distribution pools. +- **Data Layer**: Efficient subgraph-based querying for balances and history. +- **Security**: Strict environment-based configuration for dev, staging, and production. + +## Installation + +```bash +yarn add @goodsdks/streaming-sdk viem +``` + +## Quick Start + +```typescript +import { StreamingSDK, GdaSDK, calculateFlowRate } from '@goodsdks/streaming-sdk' +import { createPublicClient, createWalletClient, http, parseEther } from 'viem' +import { celo } from 'viem/chains' + +// Initialize clients +const publicClient = createPublicClient({ + chain: celo, + transport: http() +}) + +const walletClient = createWalletClient({ + chain: celo, + transport: http() +}) + +// Create SDK instance +const streamingSDK = new StreamingSDK(publicClient, walletClient, { + chainId: 42220, // Celo + environment: 'production' +}) + +// Create a stream (100 G$ per month) +const flowRate = calculateFlowRate(parseEther('100'), 'month') +const hash = await streamingSDK.createStream({ + receiver: '0x...', + token: '', // replace with your G$ SuperToken address for the chosen network + flowRate, + onHash: (hash) => console.log('Transaction:', hash) +}) +``` + +## API Reference + +### StreamingSDK + +Core SDK for stream operations. + +#### Constructor + +```typescript +new StreamingSDK( + publicClient: PublicClient, + walletClient?: WalletClient, + options?: { + chainId?: number + environment?: 'production' | 'staging' | 'development' + apiKey?: string // Optional: required for authenticated subgraphs like SUP Reserve + } +) +``` + +#### Methods + +**createStream(params)** + +Create a new stream. + +```typescript +const hash = await streamingSDK.createStream({ + receiver: '0x...', + token: '0x...', + flowRate: BigInt('1000000000000000'), // wei per second + onHash: (hash) => console.log(hash) +}) +``` + +**updateStream(params)** + +Update an existing stream's flow rate. + +```typescript +const hash = await streamingSDK.updateStream({ + receiver: '0x...', + token: '0x...', + newFlowRate: BigInt('2000000000000000') +}) +``` + +**deleteStream(params)** + +Delete a stream. + +```typescript +const hash = await streamingSDK.deleteStream({ + receiver: '0x...', + token: '0x...' +}) +``` + +**getActiveStreams(account, direction?)** + +Get active streams for an account. + +```typescript +// All streams +const streams = await streamingSDK.getActiveStreams('0x...') + +// Incoming only +const incoming = await streamingSDK.getActiveStreams('0x...', 'incoming') + +// Outgoing only +const outgoing = await streamingSDK.getActiveStreams('0x...', 'outgoing') +``` + +**getSuperTokenBalance(account)** + +Get SuperToken balance for an account. + +```typescript +const balance = await streamingSDK.getSuperTokenBalance('0x...') +console.log(`Balance: ${formatEther(balance)} G$`) +``` + +**getBalanceHistory(account, fromTimestamp?, toTimestamp?)** + +Get historical balance data. + +```typescript +const history = await streamingSDK.getBalanceHistory( + '0x...', + Date.now() - 30 * 24 * 60 * 60 * 1000, // 30 days ago + Date.now() +) +``` + +### GdaSDK + +SDK for GDA (General Distribution Agreement) pool operations. + +#### Constructor + +```typescript +new GdaSDK( + publicClient: PublicClient, + walletClient?: WalletClient, + chainId?: number +) +``` + +#### Methods + +**connectToPool(params)** + +Connect to a distribution pool. + +```typescript +const gdaSDK = new GdaSDK(publicClient, walletClient, 42220) + +const hash = await gdaSDK.connectToPool({ + poolAddress: '0x...', + onHash: (hash) => console.log(hash) +}) +``` + +**disconnectFromPool(params)** + +Disconnect from a distribution pool. + +```typescript +const hash = await gdaSDK.disconnectFromPool({ + poolAddress: '0x...' +}) +``` + +**getDistributionPools()** + +Get all distribution pools. + +```typescript +const pools = await gdaSDK.getDistributionPools() +``` + +**getPoolMemberships(account)** + +Get pool memberships for an account. + +```typescript +const memberships = await gdaSDK.getPoolMemberships('0x...') +``` + +**getPoolDetails(poolId)** + +Get details for a specific pool. + +```typescript +const pool = await gdaSDK.getPoolDetails('0x...') +``` + +### Utility Functions + +**calculateFlowRate(amountWei, timeUnit)** + +Calculate flow rate from amount and time unit. + +```typescript +import { calculateFlowRate } from '@goodsdks/streaming-sdk' +import { parseEther } from 'viem' + +const flowRate = calculateFlowRate(parseEther('100'), 'month') +``` + +**formatFlowRate(flowRate, timeUnit)** + +Format flow rate to human-readable string. + +```typescript +import { formatFlowRate } from '@goodsdks/streaming-sdk' + +const formatted = formatFlowRate(flowRate, 'month') +console.log(formatted) // "100 tokens/month" +``` + +**flowRateFromAmount(amount, timeUnit)** + +Convenience function to calculate flow rate from string amount. + +```typescript +import { flowRateFromAmount } from '@goodsdks/streaming-sdk' + +const flowRate = flowRateFromAmount('100', 'month') +``` + +## Environment Configuration + +The SDK supports three environments with different G$ token addresses: + +```typescript +const sdk = new StreamingSDK(publicClient, walletClient, { + environment: 'production' // or 'staging' or 'development' +}) +``` + +| Environment | Celo G$ Address | +|-------------|-----------------| +| production | `` | +| staging | `0x61FA0fB802fd8345C06da558240E0651886fec69` | +| development | `0xFa51eFDc0910CCdA91732e6806912Fa12e2FD475` | + +## Supported Chains + +- **Celo** (Chain ID: 42220) - Primary network for G$ operations +- **Base** (Chain ID: 8453) - SUP token operations + +## Address Resolution + +To ensure compatibility and reliability, the Streaming SDK resolves Superfluid protocol addresses as follows: + +- **Superfluid Context**: Addresses for the Host and CFA/GDA Forwarders are retrieved directly from the official `@sfpro/sdk` address maps (e.g., `cfaForwarderAddress[chainId]`). +- **G$ SuperTokens**: Token addresses are resolved based on the selected environment (`production`, `staging`, or `development`). On networks like Base where G$ is not yet deployed, the SDK gracefully handles the absence of the token. + +## Subgraph Queries + +The SDK uses Superfluid subgraphs for efficient historical data queries: + +```typescript +const subgraphClient = streamingSDK.getSubgraphClient() + +// Or initialize separately with an API Key for decentralized network access +const client = new SubgraphClient(SupportedChains.BASE, { + apiKey: 'your-graph-api-key' +}) + +// Query streams +const streams = await subgraphClient.queryStreams({ + account: '0x...', + direction: 'all' +}) + +// Query balances +const balances = await subgraphClient.queryBalances('0x...') + +// Query pool memberships +const memberships = await subgraphClient.queryPoolMemberships('0x...') + +// Query SUP reserves +const reserves = await subgraphClient.querySUPReserves() +``` + +## Error Handling + +```typescript +try { + const hash = await streamingSDK.createStream({ + receiver: '0x...', + token: '0x...', + flowRate: BigInt('1000000000000000') + }) +} catch (error) { + if (error.message.includes('Unsupported chain')) { + console.error('Wrong network') + } else if (error.message.includes('Wallet client not initialized')) { + console.error('Connect wallet first') + } else { + console.error('Transaction failed:', error) + } +} +``` + +## Examples + +### Complete Stream Lifecycle + +```typescript +import { StreamingSDK, calculateFlowRate } from '@goodsdks/streaming-sdk' +import { parseEther } from 'viem' + +const sdk = new StreamingSDK(publicClient, walletClient) + +// Create stream +const flowRate = calculateFlowRate(parseEther('100'), 'month') +await sdk.createStream({ + receiver: '0xReceiverAddress', + token: '0xG$Address', + flowRate +}) + +// Update stream +await sdk.updateStream({ + receiver: '0xReceiverAddress', + token: '0xG$Address', + newFlowRate: calculateFlowRate(parseEther('200'), 'month') +}) + +// Delete stream +await sdk.deleteStream({ + receiver: '0xReceiverAddress', + token: '0xG$Address' +}) +``` + +### GDA Pool Workflow + +```typescript +import { GdaSDK } from '@goodsdks/streaming-sdk' + +const gdaSDK = new GdaSDK(publicClient, walletClient, 42220) + +// List all pools +const pools = await gdaSDK.getDistributionPools() + +// Connect to a pool +await gdaSDK.connectToPool({ + poolAddress: pools[0].id +}) + +// Check membership +const memberships = await gdaSDK.getPoolMemberships('0xYourAddress') + +// Disconnect from pool +await gdaSDK.disconnectFromPool({ + poolAddress: pools[0].id +}) +``` + +## Security & Best Practices + +### API Key Management + +If using authenticated subgraph endpoints (for SUP Reserve queries), provide your API key securely: + +```typescript +const sdk = new StreamingSDK(publicClient, walletClient, { + environment: 'production', + apiKey: process.env.SUBGRAPH_API_KEY // Never hardcode keys +}) +``` + +**Guidelines:** +- Always use environment variables for API keys +- Never commit keys to version control +- Rotate API keys regularly +- Use different keys for dev/staging/production + +### Rate Limiting + +Superfluid subgraph enforces rate limits: +- **Standard**: 100 queries per minute +- **Authenticated**: Higher limits with API key + +To avoid rate limits: +```typescript +// ✅ Good: Batch queries +const [streams, pools, balances] = await Promise.all([ + sdk.getActiveStreams(account), + gdaSDK.getDistributionPools(), + sdk.getSuperTokenBalance(account) +]) + +// ❌ Avoid: Sequential queries +await sdk.getActiveStreams(account) +await gdaSDK.getDistributionPools() +await sdk.getSuperTokenBalance(account) +``` + +### Transaction Safety + +Always validate before creating/updating streams: + +```typescript +// ✅ Validate flow rate +if (newFlowRate <= 0n) { + throw new Error("Flow rate must be positive") +} + +// ✅ Check user has wallet connected +if (!walletClient) { + throw new Error("Connect wallet first") +} + +// ✅ Use onHash for user feedback +await sdk.createStream({ + receiver: recp, + token: token, + flowRate, + onHash: (hash) => { + console.log(`Transaction submitted: ${hash}`) + // Show user transaction hash for tracking + } +}) +``` + +### Subgraph Data Freshness + +Subgraph data may lag behind chain state by 1-2 blocks: + +```typescript +// For critical operations, query both contract + subgraph +const subgraphStreams = await sdk.getActiveStreams(account) +// Optionally: read directly from contract for latest state +const contractState = await publicClient.readContract({ + address: cfaForwarder, + abi: cfaForwarderAbi, + functionName: 'getFlow', + args: [token, sender, receiver] +}) +``` + +### Error Handling + +Implement proper error handling for network failures: + +```typescript +import { StreamingSDK } from '@goodsdks/streaming-sdk' + +try { + const streams = await sdk.getActiveStreams(account) +} catch (error) { + if (error instanceof Error) { + if (error.message.includes('network')) { + // Handle network failure - retry with backoff + console.error('Network error querying streams') + } else if (error.message.includes('timeout')) { + // Handle timeout + console.error('Subgraph query timeout') + } else { + // Handle other errors + console.error('Failed to fetch streams:', error.message) + } + } +} +``` + +## TypeScript Types + +All types are exported for use in your application: + +```typescript +import type { + StreamInfo, + CreateStreamParams, + UpdateStreamParams, + DeleteStreamParams, + GDAPool, + PoolMembership, + SuperTokenBalance, + Environment +} from '@goodsdks/streaming-sdk' +``` + +## License + +MIT + +## Related Packages + +- [@goodsdks/react-hooks](../react-hooks) - React hooks for streaming operations +- [@goodsdks/citizen-sdk](../citizen-sdk) - GoodDollar identity and claim SDK +- [@goodsdks/savings-sdk](../savings-sdk) - G$ savings/staking SDK + +## References + +- [Superfluid Documentation](https://docs.superfluid.org) +- [Superfluid SDK](https://sdk.superfluid.pro) +- [GoodDollar Protocol](https://github.com/GoodDollar/GoodProtocol) diff --git a/packages/streaming-sdk/TESTING.md b/packages/streaming-sdk/TESTING.md new file mode 100644 index 0000000..796c9f8 --- /dev/null +++ b/packages/streaming-sdk/TESTING.md @@ -0,0 +1,352 @@ +# Testing the Superfluid Streaming SDK + +## Quick Testing Options + +### 1. **Create a Test Script (Recommended for Quick Validation)** + +Create a simple test file to verify the SDK works: + +```bash +# Create test directory +mkdir -p test-sdk +cd test-sdk +npm init -y +npm install viem @goodsdks/streaming-sdk +``` + +**test-sdk/index.js:** +```javascript +import { StreamingSDK, calculateFlowRate, SupportedChains } from '@goodsdks/streaming-sdk' +import { createPublicClient, http } from 'viem' +import { celo } from 'viem/chains' + +async function testSDK() { + console.log('🧪 Testing Superfluid Streaming SDK...\n') + + // 1. Test SDK initialization + console.log('✅ Step 1: Initialize SDK') + const publicClient = createPublicClient({ + chain: celo, + transport: http('https://forno.celo.org') + }) + + const sdk = new StreamingSDK(publicClient, undefined, { + chainId: SupportedChains.CELO, + environment: 'production' + }) + console.log(' SDK initialized successfully\n') + + // 2. Test flow rate calculation + console.log('✅ Step 2: Test flow rate utilities') + const flowRate = calculateFlowRate(BigInt('100000000000000000000'), 'month') // 100 tokens/month + console.log(` Flow rate: ${flowRate.toString()} wei/second\n`) + + // 3. Test subgraph queries + console.log('✅ Step 3: Query active streams') + try { + const streams = await sdk.getActiveStreams('0x0000000000000000000000000000000000000001') + console.log(` Found ${streams.length} active streams\n`) + } catch (error) { + console.log(` Query executed (account may not exist): ${error.message}\n`) + } + + // 4. Test balance query + console.log('✅ Step 4: Query SuperToken balance') + try { + const balance = await sdk.getSuperTokenBalance('0x0000000000000000000000000000000000000001') + console.log(` Balance: ${balance.toString()} wei\n`) + } catch (error) { + console.log(` Query executed: ${error.message}\n`) + } + + console.log('🎉 All tests completed!') +} + +testSDK().catch(console.error) +``` + +Run with: +```bash +node index.js +``` + +--- + +### 2. **Test in an Existing App (Integration Testing)** + +Add to your existing GoodDollar app: + +```bash +cd apps/demo-identity-app # or any existing app +yarn add @goodsdks/streaming-sdk +``` + +**Example usage in a React component:** +```typescript +import { usePublicClient, useWalletClient } from 'wagmi' +import { StreamingSDK, calculateFlowRate } from '@goodsdks/streaming-sdk' +import { parseEther } from 'viem' + +function StreamingDemo() { + const publicClient = usePublicClient() + const { data: walletClient } = useWalletClient() + + const createStream = async () => { + if (!publicClient || !walletClient) return + + const sdk = new StreamingSDK(publicClient, walletClient, { + environment: 'production' + }) + + const flowRate = calculateFlowRate(parseEther('100'), 'month') + + const hash = await sdk.createStream({ + receiver: '0x...', + token: '', // replace with your local/testnet token address + flowRate, + onHash: (hash) => console.log('Transaction:', hash) + }) + + console.log('Stream created:', hash) + } + + return +} +``` + +--- + +### 3. **Test Subgraph Queries (No Wallet Needed)** + +You can test subgraph functionality without a wallet: + +```javascript +import { SubgraphClient, SupportedChains } from '@goodsdks/streaming-sdk' + +async function testSubgraph() { + const client = new SubgraphClient(SupportedChains.CELO) + + // Test querying streams for a known address + const streams = await client.queryStreams({ + account: '0x...', // Use a real address with streams + direction: 'all' + }) + + console.log('Streams found:', streams) + + // Test querying pools + const pools = await client.queryPools() + console.log('GDA Pools:', pools) +} + +testSubgraph() +``` + +--- + +### 4. **Test with Superfluid Subgraph Playground** + +Visit the Superfluid subgraph explorer to verify queries work: + +**Celo Subgraph:** +https://api.thegraph.com/subgraphs/id/6dRuPxMvaJAp32hvcTsYbAya69A4t1KUHh2EnV3YQeXU + +Test this query: +```graphql +{ + streams(where: { currentFlowRate_gt: "0" }, first: 5) { + id + sender + receiver + currentFlowRate + token { + id + symbol + } + } +} +``` + +--- + +### 5. **Manual Testing Checklist** + +#### ✅ **Build Verification** +```bash +# From repo root +yarn build +# Should complete without errors +``` + +#### ✅ **Type Checking** +```bash +yarn tsc --noEmit --project packages/streaming-sdk/tsconfig.json +# Should show no errors +``` + +#### ✅ **Import Test** +```bash +node -e "import('@goodsdks/streaming-sdk').then(m => console.log(Object.keys(m)))" +# Should show exported members +``` + +#### ✅ **Package Exports** +Check that all exports are accessible: +```javascript +import { + StreamingSDK, + GdaSDK, + SubgraphClient, + calculateFlowRate, + SupportedChains, + // ... etc +} from '@goodsdks/streaming-sdk' + +console.log('All exports loaded successfully') +``` + +--- + +### 6. **Test on Testnet (Safe Testing)** + +Before mainnet, test on Celo Alfajores testnet: + +1. Get testnet CELO from faucet +2. Wrap to SuperToken +3. Test stream creation with small amounts + +```typescript +const sdk = new StreamingSDK(publicClient, walletClient, { + chainId: 44787, // Alfajores testnet + environment: 'development' +}) +``` + +--- + +### 7. **React Hooks Testing** + +Test the React hooks in a component: + +```typescript +import { useCreateStream, useStreamList } from '@goodsdks/react-hooks' +import { useAccount } from 'wagmi' + +function StreamingComponent() { + const { address } = useAccount() + const { data: streams, isLoading } = useStreamList({ + account: address!, + enabled: !!address + }) + + const { mutate: createStream } = useCreateStream() + + return ( +
+

Streams: {streams?.length ?? 0}

+ +
+ ) +} +``` + +--- + +## Recommended Testing Flow + +### Phase 1: Local Verification (5 minutes) +1. ✅ Run `yarn build` - verify no errors +2. ✅ Run type check - verify no type errors +3. ✅ Test imports in Node REPL + +### Phase 2: Subgraph Testing (10 minutes) +1. ✅ Test SubgraphClient with real addresses +2. ✅ Verify queries return expected data +3. ✅ Test on Superfluid playground + +### Phase 3: Integration Testing (30 minutes) +1. ✅ Add to existing app +2. ✅ Test read operations (no wallet needed) +3. ✅ Test with wallet on testnet +4. ✅ Create a test stream with small amount + +### Phase 4: Production Testing (Careful!) +1. ✅ Test on mainnet with very small amounts (0.01 G$) +2. ✅ Verify transaction on block explorer +3. ✅ Confirm stream appears in Superfluid dashboard + +--- + +## Common Issues & Solutions + +### Issue: "Module not found" +**Solution:** Run `yarn install` from repo root + +### Issue: "Chain not supported" +**Solution:** Ensure you're using Celo (42220) or Base (8453) + +### Issue: "Wallet client not initialized" +**Solution:** Pass walletClient to SDK constructor for write operations + +### Issue: Subgraph query fails +**Solution:** Check network connectivity and subgraph endpoint + +--- + +## Quick Validation Script + +Save this as `validate-sdk.sh`: + +```bash +#!/bin/bash +echo "🧪 Validating Superfluid Streaming SDK..." + +# Build +echo "1️⃣ Building packages..." +yarn build > /dev/null 2>&1 +if [ $? -eq 0 ]; then + echo " ✅ Build successful" +else + echo " ❌ Build failed" + exit 1 +fi + +# Type check +echo "2️⃣ Type checking..." +yarn tsc --noEmit --project packages/streaming-sdk/tsconfig.json > /dev/null 2>&1 +if [ $? -eq 0 ]; then + echo " ✅ Type check passed" +else + echo " ❌ Type check failed" + exit 1 +fi + +# Check exports +echo "3️⃣ Checking exports..." +node -e "import('@goodsdks/streaming-sdk').then(() => console.log(' ✅ Exports valid'))" 2>/dev/null + +echo "" +echo "🎉 SDK validation complete!" +``` + +Run with: +```bash +chmod +x validate-sdk.sh +./validate-sdk.sh +``` + +--- + +## Next Steps + +1. **For PR Review:** Run Phase 1 & 2 tests and include results +2. **For Production Use:** Complete all 4 phases +3. **For Contributors:** Add automated tests using Vitest + +Would you like me to create any of these test files for you? diff --git a/packages/streaming-sdk/package.json b/packages/streaming-sdk/package.json new file mode 100644 index 0000000..afa3300 --- /dev/null +++ b/packages/streaming-sdk/package.json @@ -0,0 +1,46 @@ +{ + "name": "@goodsdks/streaming-sdk", + "version": "1.0.0", + "type": "module", + "scripts": { + "build": "tsup --clean", + "dev": "tsc --watch", + "test": "vitest", + "test:watch": "vitest --watch", + "test:coverage": "vitest --coverage" + }, + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + "import": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + }, + "require": { + "types": "./dist/index.d.cts", + "require": "./dist/index.cjs" + } + }, + "files": [ + "dist", + "src" + ], + "devDependencies": { + "@repo/typescript-config": "workspace:*", + "@types/node": "latest", + "@vitest/coverage-v8": "^4.0.18", + "typescript": "latest", + "viem": "latest", + "vitest": "^4.0.18" + }, + "peerDependencies": { + "viem": "*" + }, + "dependencies": { + "@sfpro/sdk": "^0.1.0", + "graphql": "^16.9.0", + "graphql-request": "^7.1.2", + "tsup": "^8.3.5" + } +} diff --git a/packages/streaming-sdk/src/constants.ts b/packages/streaming-sdk/src/constants.ts new file mode 100644 index 0000000..492e786 --- /dev/null +++ b/packages/streaming-sdk/src/constants.ts @@ -0,0 +1,88 @@ +import { Address } from "viem" +import { Environment } from "./types" +import { cfaForwarderAddress, gdaForwarderAddress } from "@sfpro/sdk/abi" + +// Network definitions +export enum SupportedChains { + CELO = 42220, + CELO_ALFAJORES = 44787, + BASE = 8453, + BASE_SEPOLIA = 84532, +} + +export type SupportedChainId = SupportedChains + +/** + * Get G$ SuperToken address for a given chain and environment. + * Returning undefined for chains like Base where G$ is not yet deployed. + */ +export function getG$Token( + chainId: number, + env: Environment = 'production' +): Address | undefined { + const addresses: Record>> = { + production: { + [SupportedChains.CELO]: "0x62B8B11039FcfE5aB0C56E502b1C372A3d2a9c7A", + }, + staging: { + [SupportedChains.CELO]: "0x61FA0fB802fd8345C06da558240E0651886fec69", + }, + development: { + [SupportedChains.CELO_ALFAJORES]: "0xFa51eFDc0910CCdA91732e6806912Fa12e2FD475", + [SupportedChains.CELO]: "0xFa51eFDc0910CCdA91732e6806912Fa12e2FD475", // Fallback for local testing + }, + } + + return addresses[env]?.[chainId] +} + +// Protocol indexers +export const SUBGRAPH_URLS: Record = { + [SupportedChains.CELO]: "https://celo-mainnet.subgraph.x.superfluid.dev/", + [SupportedChains.CELO_ALFAJORES]: + "https://celo-alfajores.subgraph.x.superfluid.dev/", + [SupportedChains.BASE]: "https://base-mainnet.subgraph.x.superfluid.dev/", + [SupportedChains.BASE_SEPOLIA]: + "https://base-sepolia.subgraph.x.superfluid.dev/", + supReserve: + "https://gateway.thegraph.com/api/subgraphs/id/6dRuPxMvaJAp32hvcTsYbAya69A4t1KUHh2EnV3YQeXU", +} + +// Standard protocol interfaces +export const CFA_FORWARDER_ADDRESSES = cfaForwarderAddress +export const GDA_FORWARDER_ADDRESSES = gdaForwarderAddress + +// Metadata for frontend integration +export interface ChainConfig { + id: SupportedChains + name: string + rpcUrls: string[] + explorer: string +} + +export const CHAIN_CONFIGS: Record = { + [SupportedChains.CELO]: { + id: SupportedChains.CELO, + name: "Celo", + rpcUrls: ["https://forno.celo.org"], + explorer: "https://celoscan.io", + }, + [SupportedChains.CELO_ALFAJORES]: { + id: SupportedChains.CELO_ALFAJORES, + name: "Celo Alfajores", + rpcUrls: ["https://alfajores-forno.celo-testnet.org"], + explorer: "https://alfajores.celoscan.io", + }, + [SupportedChains.BASE]: { + id: SupportedChains.BASE, + name: "Base", + rpcUrls: ["https://mainnet.base.org"], + explorer: "https://basescan.org", + }, + [SupportedChains.BASE_SEPOLIA]: { + id: SupportedChains.BASE_SEPOLIA, + name: "Base Sepolia", + rpcUrls: ["https://sepolia.base.org"], + explorer: "https://sepolia.basescan.org", + }, +} diff --git a/packages/streaming-sdk/src/gda-sdk.ts b/packages/streaming-sdk/src/gda-sdk.ts new file mode 100644 index 0000000..07d6603 --- /dev/null +++ b/packages/streaming-sdk/src/gda-sdk.ts @@ -0,0 +1,154 @@ +import { + Address, + Hash, + PublicClient, + WalletClient, + type SimulateContractParameters, +} from "viem" +import { Environment } from "./types" +import { gdaForwarderAbi } from "@sfpro/sdk/abi" +import { + GDAPool, + PoolMembership, + ConnectToPoolParams, + DisconnectFromPoolParams, + StreamingSDKOptions, +} from "./types" +import { validateChain } from "./utils" +import { SupportedChains, GDA_FORWARDER_ADDRESSES } from "./constants" +import { SubgraphClient } from "./subgraph/client" + +export class GdaSDK { + private publicClient: PublicClient + private walletClient: WalletClient | null = null + private chainId: SupportedChains + private subgraphClient: SubgraphClient + private gdaForwarder: Address + + constructor( + publicClient: PublicClient, + walletClient?: WalletClient, + options?: StreamingSDKOptions, + ) { + if (!publicClient) { + throw new Error("Public client is required") + } + + this.publicClient = publicClient + this.chainId = validateChain( + options?.chainId ?? publicClient.chain?.id, + ) + + // Retrieve protocol address directly from official map + this.gdaForwarder = (GDA_FORWARDER_ADDRESSES as Record)[this.chainId] + + if (walletClient) { + this.setWalletClient(walletClient) + } + + this.subgraphClient = new SubgraphClient(this.chainId, { + apiKey: options?.apiKey, + }) + } + + setWalletClient(walletClient: WalletClient) { + const chainId = validateChain(walletClient.chain?.id) + if (chainId !== this.chainId) { + throw new Error( + `Wallet client chain (${chainId}) does not match SDK chain (${this.chainId})`, + ) + } + this.walletClient = walletClient + } + + async connectToPool(params: ConnectToPoolParams): Promise { + const { poolAddress, userData = "0x", onHash } = params + + return this.submitAndWait( + { + address: this.gdaForwarder, + abi: gdaForwarderAbi, + functionName: "connectPool", + args: [poolAddress, userData], + }, + onHash, + ) + } + + async disconnectFromPool(params: DisconnectFromPoolParams): Promise { + const { poolAddress, userData = "0x", onHash } = params + + return this.submitAndWait( + { + address: this.gdaForwarder, + abi: gdaForwarderAbi, + functionName: "disconnectPool", + args: [poolAddress, userData], + }, + onHash, + ) + } + + async getDistributionPools(): Promise { + return this.subgraphClient.queryPools() + } + + async getPoolMemberships(account: Address): Promise { + return this.subgraphClient.queryPoolMemberships(account) + } + + async getPoolDetails(poolId: Address): Promise { + const pools = await this.getDistributionPools() + return pools.find((p) => p.id.toLowerCase() === poolId.toLowerCase()) ?? null + } + + async querySUPReserves() { + return this.subgraphClient.querySUPReserves() + } + + /** + * Submit transaction and wait for receipt + */ + private async submitAndWait( + simulateParams: SimulateContractParameters, + onHash?: (hash: Hash) => void, + ): Promise { + if (!this.walletClient) { + throw new Error("Wallet client not initialized") + } + + const account = await this.getAccount() + + const { request } = await this.publicClient.simulateContract({ + account, + ...simulateParams, + }) + + const hash = await this.walletClient.writeContract(request) + + if (onHash) { + onHash(hash) + } + + await this.publicClient.waitForTransactionReceipt({ hash }) + + return hash + } + + /** + * Get current account address from wallet client + */ + private async getAccount(): Promise
{ + if (!this.walletClient) { + throw new Error("Wallet client not initialized") + } + + const [account] = await this.walletClient.getAddresses() + + if (!account) { + throw new Error("No account found in wallet client") + } + + return account + } +} diff --git a/packages/streaming-sdk/src/index.ts b/packages/streaming-sdk/src/index.ts new file mode 100644 index 0000000..1e70a89 --- /dev/null +++ b/packages/streaming-sdk/src/index.ts @@ -0,0 +1,29 @@ +// Core SDK classes +export { StreamingSDK } from "./streaming-sdk" +export { GdaSDK } from "./gda-sdk" +export { SubgraphClient } from "./subgraph/client" + +// Types +export * from "./types" + +// Constants +export { + SupportedChains, + getG$Token, + SUBGRAPH_URLS, + CHAIN_CONFIGS, +} from "./constants" + +// Utilities +export { + calculateFlowRate, + calculateStreamedAmount, + formatFlowRate, + flowRateFromAmount, + type TimeUnit, + isSupportedChain, + validateChain, + getSuperTokenAddress, + getSuperTokenAddressSafe, + getChainConfig, +} from "./utils" diff --git a/packages/streaming-sdk/src/sdk.test.ts b/packages/streaming-sdk/src/sdk.test.ts new file mode 100644 index 0000000..e89eb37 --- /dev/null +++ b/packages/streaming-sdk/src/sdk.test.ts @@ -0,0 +1,396 @@ +import { describe, it, expect, vi, beforeEach } from "vitest" +import { Address, parseEther } from "viem" +import { + StreamingSDK, + GdaSDK, + SupportedChains, + isSupportedChain, + validateChain, + calculateFlowRate, + formatFlowRate, + flowRateFromAmount, +} from "./index" + +/** + * --- MOCKS --- + */ + +const createMockPublicClient = (chainId: number = SupportedChains.CELO) => ({ + chain: { id: chainId, name: "Celo" }, + simulateContract: vi.fn().mockResolvedValue({ request: {} }), + waitForTransactionReceipt: vi.fn().mockResolvedValue({ status: "success" }), +}) + +const createMockWalletClient = (chainId: number = SupportedChains.CELO) => ({ + chain: { id: chainId }, + getAddresses: vi.fn().mockResolvedValue(["0x0000000000000000000000000000000000000001"]), + writeContract: vi.fn().mockResolvedValue("0xhash"), +}) + +const TEST_SUPERTOKEN = "0x62B8B11039FcfE5aB0C56E502b1C372A3d2a9c7A" as Address + +/** + * --- UTILS TESTS --- + */ + +describe("Utilities", () => { + describe("isSupportedChain", () => { + it("should return true for SupportedChains", () => { + expect(isSupportedChain(SupportedChains.CELO)).toBe(true) + expect(isSupportedChain(SupportedChains.BASE)).toBe(true) + }) + it("should return false for unsupported chains", () => { + expect(isSupportedChain(1)).toBe(false) + }) + }) + + describe("validateChain", () => { + it("should return chainId for supported chains", () => { + expect(validateChain(SupportedChains.CELO)).toBe(SupportedChains.CELO) + }) + it("should throw for unsupported chains", () => { + expect(() => validateChain(1)).toThrow("Unsupported chain ID") + }) + }) + + describe("Flow Rate Calculation", () => { + it("should calculate flow rate correctly", () => { + const amount = parseEther("100") + const month = 2592000 + const expected = amount / BigInt(month) + expect(calculateFlowRate(amount, "month")).toBe(expected) + }) + + it("should format flow rate correctly", () => { + const flowRate = parseEther("1") / BigInt(3600) + const formatted = formatFlowRate(flowRate, "hour") + expect(formatted).toContain("tokens/hour") + }) + + it("should derive flow rate from amount string", () => { + const flowRate = flowRateFromAmount("100", "month") + expect(flowRate).toBe(parseEther("100") / BigInt(2592000)) + }) + }) +}) + +/** + * --- SDK TESTS --- + */ + +describe("StreamingSDK", () => { + let publicClient: any + let walletClient: any + + beforeEach(() => { + publicClient = createMockPublicClient() + walletClient = createMockWalletClient() + }) + + it("should initialize with public client", () => { + const sdk = new StreamingSDK(publicClient) + expect(sdk).toBeDefined() + }) + + it("should create a stream", async () => { + const sdk = new StreamingSDK(publicClient, walletClient) + const hash = await sdk.createStream({ + receiver: "0xreceiver" as Address, + token: TEST_SUPERTOKEN, + flowRate: BigInt(100), + }) + + expect(hash).toBe("0xhash") + expect(publicClient.simulateContract).toHaveBeenCalled() + }) + + it("should delete a stream", async () => { + const sdk = new StreamingSDK(publicClient, walletClient) + const hash = await sdk.deleteStream({ + receiver: "0xreceiver" as Address, + token: TEST_SUPERTOKEN, + }) + expect(hash).toBe("0xhash") + }) +}) + +describe("GdaSDK", () => { + let publicClient: any + let walletClient: any + + beforeEach(() => { + publicClient = createMockPublicClient() + walletClient = createMockWalletClient() + }) + + it("should initialize GdaSDK", () => { + const sdk = new GdaSDK(publicClient) + expect(sdk).toBeDefined() + }) + + it("should connect to pool", async () => { + const sdk = new GdaSDK(publicClient, walletClient) + const hash = await sdk.connectToPool({ + poolAddress: "0xpool" as Address, + }) + expect(hash).toBe("0xhash") + }) + + it("should fetch distribution pools via subgraph", async () => { + const sdk = new GdaSDK(publicClient) + const mockPools = [{ id: "0xpool", token: "0xtoken", totalUnits: BigInt(0), flowRate: BigInt(0), admin: "0xadmin" }] + + // Mocking private subgraph client response + vi.spyOn(sdk as any, "getDistributionPools").mockResolvedValue(mockPools) + + const pools = await sdk.getDistributionPools() + expect(pools).toEqual(mockPools) + }) +}) + +/** + * --- ERROR HANDLING TESTS --- + */ + +describe("Error Handling", () => { + let publicClient: any + let walletClient: any + + beforeEach(() => { + publicClient = createMockPublicClient() + walletClient = createMockWalletClient() + }) + + describe("StreamingSDK", () => { + it("should throw when public client is missing", () => { + expect(() => new StreamingSDK(null as any)).toThrow("Public client is required") + }) + + it("should throw when wallet not initialized for write operations", async () => { + const sdk = new StreamingSDK(publicClient) // No wallet client + await expect( + sdk.createStream({ + receiver: "0xreceiver" as Address, + token: TEST_SUPERTOKEN, + flowRate: BigInt(100), + }) + ).rejects.toThrow("Wallet client not initialized") + }) + + it("should throw for invalid flow rate (zero)", async () => { + const sdk = new StreamingSDK(publicClient, walletClient) + await expect( + sdk.createStream({ + receiver: "0xreceiver" as Address, + token: TEST_SUPERTOKEN, + flowRate: BigInt(0), + }) + ).rejects.toThrow("Flow rate must be greater than zero") + }) + + it("should throw for negative flow rate", async () => { + const sdk = new StreamingSDK(publicClient, walletClient) + await expect( + sdk.updateStream({ + receiver: "0xreceiver" as Address, + token: TEST_SUPERTOKEN, + newFlowRate: BigInt(-100), + }) + ).rejects.toThrow("newFlowRate must be a positive non-zero value") + }) + + it("should throw when wallet chain doesn't match SDK chain", () => { + const misMatchedWallet = createMockWalletClient(SupportedChains.BASE) // Base + const sdk = new StreamingSDK(publicClient, undefined, { chainId: SupportedChains.CELO }) + expect(() => sdk.setWalletClient(misMatchedWallet)).toThrow( + "does not match SDK chain" + ) + }) + + it("should validate unsupported chain on initialization", () => { + expect(() => new StreamingSDK(publicClient, walletClient, { chainId: 1 })).toThrow( + "Unsupported chain ID" + ) + }) + }) + + describe("GdaSDK", () => { + it("should throw when public client is missing", () => { + expect(() => new GdaSDK(null as any)).toThrow("Public client is required") + }) + + it("should throw when wallet chain doesn't match", () => { + const misMatchedWallet = createMockWalletClient(8453) // Base + const celoClient = createMockPublicClient(SupportedChains.CELO) + const sdk = new GdaSDK(celoClient, undefined) + expect(() => sdk.setWalletClient(misMatchedWallet)).toThrow( + "does not match SDK chain" + ) + }) + }) +}) + +/** + * --- EDGE CASES & UTILITY TESTS --- + */ + +describe("Edge Cases & Utilities", () => { + describe("Chain Configuration", () => { + it("should support all Celo chains", () => { + expect(isSupportedChain(SupportedChains.CELO)).toBe(true) + expect(isSupportedChain(SupportedChains.CELO_ALFAJORES)).toBe(true) + }) + + it("should support all Base chains", () => { + expect(isSupportedChain(SupportedChains.BASE)).toBe(true) + expect(isSupportedChain(SupportedChains.BASE_SEPOLIA)).toBe(true) + }) + + it("should reject other chains", () => { + expect(isSupportedChain(1)).toBe(false) + expect(isSupportedChain(137)).toBe(false) + expect(isSupportedChain(undefined)).toBe(false) + }) + }) + + describe("Flow Rate Utilities", () => { + it("should calculate flow rate for all time units", () => { + const amount = parseEther("1") + expect(calculateFlowRate(amount, "second")).toBe(amount) + expect(calculateFlowRate(amount, "minute")).toBe(amount / BigInt(60)) + expect(calculateFlowRate(amount, "hour")).toBe(amount / BigInt(3600)) + expect(calculateFlowRate(amount, "day")).toBe(amount / BigInt(86400)) + expect(calculateFlowRate(amount, "week")).toBe(amount / BigInt(604800)) + expect(calculateFlowRate(amount, "year")).toBe(amount / BigInt(31536000)) + }) + + it("should handle small amounts correctly", () => { + const smallAmount = BigInt(1) + const flowRate = calculateFlowRate(smallAmount, "month") + expect(flowRate).toBe(BigInt(0)) // 1 wei / 2592000 seconds = 0 + }) + + it("should format flow rate with precision", () => { + const flowRate = parseEther("1") / BigInt(3600) // 1 token per hour + const formatted = formatFlowRate(flowRate, "hour") + expect(formatted).toMatch(/tokens\/hour/) + expect(formatted).toContain("hour") + }) + + it("should calculate streamed amount correctly", () => { + const flowRate = parseEther("100") / BigInt(2592000) // 100 tokens/month + const secondsInDay = BigInt(86400) + const streamedInDay = flowRate * secondsInDay + + expect(streamedInDay).toBeGreaterThan(BigInt(0)) + expect(streamedInDay).toBeLessThan(parseEther("100")) + }) + }) + + describe("Environment Configuration", () => { + it("should use production environment by default", () => { + const publicClient = createMockPublicClient() + const sdk = new StreamingSDK(publicClient) + expect(sdk).toBeDefined() + }) + + it("should support staging environment", () => { + const publicClient = createMockPublicClient() + const sdk = new StreamingSDK(publicClient, undefined, { environment: "staging" }) + expect(sdk).toBeDefined() + }) + + it("should support development environment", () => { + const publicClient = createMockPublicClient() + const sdk = new StreamingSDK(publicClient, undefined, { environment: "development" }) + expect(sdk).toBeDefined() + }) + }) + + describe("StreamingSDK Methods", () => { + let publicClient: any + let walletClient: any + + beforeEach(() => { + publicClient = createMockPublicClient() + walletClient = createMockWalletClient() + }) + + it("should update stream with new flow rate", async () => { + const sdk = new StreamingSDK(publicClient, walletClient) + const hash = await sdk.updateStream({ + receiver: "0xreceiver" as Address, + token: TEST_SUPERTOKEN, + newFlowRate: BigInt(250), + }) + expect(hash).toBe("0xhash") + expect(publicClient.simulateContract).toHaveBeenCalled() + }) + + it("should include userData in stream creation", async () => { + const sdk = new StreamingSDK(publicClient, walletClient) + const userData = "0x1234" as `0x${string}` + const hash = await sdk.createStream({ + receiver: "0xreceiver" as Address, + token: TEST_SUPERTOKEN, + flowRate: BigInt(100), + userData, + }) + expect(hash).toBe("0xhash") + }) + + it("should call onHash callback when provided", async () => { + const sdk = new StreamingSDK(publicClient, walletClient) + const onHashMock = vi.fn() + + await sdk.createStream({ + receiver: "0xreceiver" as Address, + token: TEST_SUPERTOKEN, + flowRate: BigInt(100), + onHash: onHashMock, + }) + + expect(onHashMock).toHaveBeenCalledWith("0xhash") + }) + }) + + describe("GdaSDK Methods", () => { + let publicClient: any + let walletClient: any + + beforeEach(() => { + publicClient = createMockPublicClient() + walletClient = createMockWalletClient() + }) + + it("should disconnect from pool", async () => { + const sdk = new GdaSDK(publicClient, walletClient) + const hash = await sdk.disconnectFromPool({ + poolAddress: "0xpool" as Address, + }) + expect(hash).toBe("0xhash") + }) + + it("should include userData in pool operations", async () => { + const sdk = new GdaSDK(publicClient, walletClient) + const userData = "0xabcd" as `0x${string}` + const hash = await sdk.connectToPool({ + poolAddress: "0xpool" as Address, + userData, + }) + expect(hash).toBe("0xhash") + }) + + it("should call onHash callback for pool operations", async () => { + const sdk = new GdaSDK(publicClient, walletClient) + const onHashMock = vi.fn() + + await sdk.connectToPool({ + poolAddress: "0xpool" as Address, + onHash: onHashMock, + }) + + expect(onHashMock).toHaveBeenCalledWith("0xhash") + }) + }) +}) diff --git a/packages/streaming-sdk/src/streaming-sdk.ts b/packages/streaming-sdk/src/streaming-sdk.ts new file mode 100644 index 0000000..9af6a96 --- /dev/null +++ b/packages/streaming-sdk/src/streaming-sdk.ts @@ -0,0 +1,221 @@ +import { + Address, + Hash, + PublicClient, + WalletClient, + type SimulateContractParameters, +} from "viem" +import { cfaForwarderAbi } from "@sfpro/sdk/abi" +import { + StreamingSDKOptions, + CreateStreamParams, + UpdateStreamParams, + DeleteStreamParams, + StreamInfo, + GetStreamsOptions, + Environment, +} from "./types" +import { validateChain } from "./utils" +import { SupportedChains, CFA_FORWARDER_ADDRESSES, getG$Token } from "./constants" +import { SubgraphClient } from "./subgraph/client" + +export class StreamingSDK { + private publicClient: PublicClient + private walletClient: WalletClient | null = null + private chainId: SupportedChains + private environment: Environment + private subgraphClient: SubgraphClient + private cfaForwarder: Address + + constructor( + publicClient: PublicClient, + walletClient?: WalletClient, + options?: StreamingSDKOptions, + ) { + if (!publicClient) { + throw new Error("Public client is required") + } + + this.publicClient = publicClient + this.chainId = validateChain( + options?.chainId ?? publicClient.chain?.id, + ) + this.environment = options?.environment ?? "production" + + // Retrieve protocol addresses directly from official maps + this.cfaForwarder = (CFA_FORWARDER_ADDRESSES as Record)[this.chainId] + + if (walletClient) { + this.setWalletClient(walletClient) + } + + this.subgraphClient = new SubgraphClient(this.chainId, { + apiKey: options?.apiKey, + }) + } + + setWalletClient(walletClient: WalletClient) { + const chainId = validateChain(walletClient.chain?.id) + if (chainId !== this.chainId) { + throw new Error( + `Wallet client chain (${chainId}) does not match SDK chain (${this.chainId})`, + ) + } + this.walletClient = walletClient + } + + async createStream(params: CreateStreamParams): Promise { + const { receiver, token, flowRate, userData = "0x", onHash } = params + + if (flowRate <= BigInt(0)) { + throw new Error("Flow rate must be greater than zero") + } + + return this.submitAndWait( + { + address: this.cfaForwarder, + abi: cfaForwarderAbi, + functionName: "setFlowrate", + args: [token, receiver, flowRate], + }, + onHash, + ) + } + + async updateStream(params: UpdateStreamParams): Promise { + const { receiver, token, newFlowRate, userData = "0x", onHash } = params + + if (newFlowRate <= BigInt(0)) { + throw new Error("newFlowRate must be a positive non-zero value") + } + + const account = await this.getAccount() + + return this.submitAndWait( + { + address: this.cfaForwarder, + abi: cfaForwarderAbi, + functionName: "updateFlow", + args: [token, account, receiver, newFlowRate, userData], + }, + onHash, + ) + } + + async deleteStream(params: DeleteStreamParams): Promise { + const { receiver, token, onHash } = params + + const account = await this.getAccount() + + return this.submitAndWait( + { + address: this.cfaForwarder, + abi: cfaForwarderAbi, + functionName: "deleteFlow", + args: [token, account, receiver, "0x"], + }, + onHash, + ) + } + + async getActiveStreams( + account: Address, + direction?: "incoming" | "outgoing" | "all", + ): Promise { + const streams = await this.subgraphClient.queryStreams({ + account, + direction: direction ?? "all", + }) + + return streams.map((stream) => ({ + sender: stream.sender, + receiver: stream.receiver, + token: stream.token, + flowRate: stream.currentFlowRate, + timestamp: BigInt(stream.createdAtTimestamp), + streamedSoFar: stream.streamedUntilUpdatedAt, + })) + } + + async getSuperTokenBalance(account: Address): Promise { + const token = getG$Token(this.chainId, this.environment) + + if (!token) return BigInt(0) + + const balances = await this.subgraphClient.queryBalances(account) + const tokenBalance = balances.find( + (b) => b.token.toLowerCase() === token.toLowerCase(), + ) + + return tokenBalance?.balance ?? BigInt(0) + } + + /** + * Get balance history for an account + */ + async getBalanceHistory( + account: Address, + fromTimestamp?: number, + toTimestamp?: number, + ) { + return this.subgraphClient.queryBalanceHistory({ + account, + fromTimestamp, + toTimestamp, + }) + } + + async querySUPReserves() { + return this.subgraphClient.querySUPReserves() + } + + getSubgraphClient(): SubgraphClient { + return this.subgraphClient + } + + /** + * Submit transaction and wait for receipt + */ + private async submitAndWait( + simulateParams: SimulateContractParameters, + onHash?: (hash: Hash) => void, + ): Promise { + if (!this.walletClient) { + throw new Error("Wallet client not initialized") + } + + const account = await this.getAccount() + + const { request } = await this.publicClient.simulateContract({ + account, + ...simulateParams, + }) + + const hash = await this.walletClient.writeContract(request) + + if (onHash) { + onHash(hash) + } + + await this.publicClient.waitForTransactionReceipt({ hash }) + + return hash + } + + /** + * Get current account address from wallet client + */ + private async getAccount(): Promise
{ + if (!this.walletClient) { + throw new Error("Wallet client not initialized") + } + + const [account] = await this.walletClient.getAddresses() + + if (!account) { + throw new Error("No account found in wallet client") + } + + return account + } +} diff --git a/packages/streaming-sdk/src/subgraph/client.ts b/packages/streaming-sdk/src/subgraph/client.ts new file mode 100644 index 0000000..77f10e4 --- /dev/null +++ b/packages/streaming-sdk/src/subgraph/client.ts @@ -0,0 +1,285 @@ +import { GraphQLClient, gql } from "graphql-request" +import { Address } from "viem" +import { SUBGRAPH_URLS, SupportedChains } from "../constants" +import { + StreamQueryResult, + SuperTokenBalance, + GDAPool, + PoolMembership, + SUPReserveLocker, + GetStreamsOptions, + GetBalanceHistoryOptions, +} from "../types" + +/** + * GraphQL query definitions + */ +const GET_STREAMS = gql` + query GetStreams($account: String!, $skip: Int = 0, $first: Int = 100) { + outgoingStreams: streams( + where: { sender: $account, currentFlowRate_gt: "0" } + first: $first + skip: $skip + orderBy: createdAtTimestamp + orderDirection: desc + ) { + id + sender { id } + receiver { id } + token { id, symbol } + currentFlowRate + streamedUntilUpdatedAt + updatedAtTimestamp + createdAtTimestamp + } + incomingStreams: streams( + where: { receiver: $account, currentFlowRate_gt: "0" } + first: $first + skip: $skip + orderBy: createdAtTimestamp + orderDirection: desc + ) { + id + sender { id } + receiver { id } + token { id, symbol } + currentFlowRate + streamedUntilUpdatedAt + updatedAtTimestamp + createdAtTimestamp + } + } +` + +const GET_TOKEN_BALANCE = gql` + query GetTokenBalance($account: String!) { + account(id: $account) { + id + accountTokenSnapshots { + token { id, name, symbol } + balanceUntilUpdatedAt + updatedAtTimestamp + totalNetFlowRate + } + } + } +` + +const GET_BALANCE_HISTORY = gql` + query GetBalanceHistory($account: String!, $fromTimestamp: Int, $toTimestamp: Int) { + accountTokenSnapshotLogs( + where: { + account: $account + timestamp_gte: $fromTimestamp + timestamp_lte: $toTimestamp + } + orderBy: timestamp + orderDirection: asc + ) { + id + timestamp + token { id, symbol } + balance + totalNetFlowRate + } + } +` + +const GET_POOL_MEMBERSHIPS = gql` + query GetPoolMemberships($account: String!) { + account(id: $account) { + poolMemberships { + pool { + id + token { id, symbol } + totalUnits + totalAmountDistributedUntilUpdatedAt + flowRate + admin { id } + } + units + isConnected + totalAmountClaimed + } + } + } +` + +const GET_DISTRIBUTION_POOLS = gql` + query GetDistributionPools($first: Int = 100, $skip: Int = 0) { + pools( + first: $first + skip: $skip + orderBy: createdAtTimestamp + orderDirection: desc + ) { + id + token { id, symbol } + totalUnits + totalAmountDistributedUntilUpdatedAt + flowRate + admin { id } + createdAtTimestamp + } + } +` + +const GET_SUP_RESERVES = gql` + query GetSUPReserves { + lockers(orderBy: blockTimestamp, orderDirection: desc) { + id + lockerOwner { id } + blockNumber + blockTimestamp + } + } +` + +/** + * Subgraph data structures + */ +interface SubgraphAccount { id: string } +interface SubgraphToken { id: string; symbol: string; name?: string } +interface SubgraphStream { + id: string + sender: SubgraphAccount + receiver: SubgraphAccount + token: SubgraphToken + currentFlowRate: string + streamedUntilUpdatedAt: string + updatedAtTimestamp: string + createdAtTimestamp: string +} +interface SubgraphSnapshot { token: SubgraphToken; balanceUntilUpdatedAt: string; updatedAtTimestamp: string } +interface SubgraphSnapshotLog { token: SubgraphToken; balance: string; timestamp: string } +interface SubgraphPool { + id: string + token: SubgraphToken + totalUnits: string + totalAmountDistributedUntilUpdatedAt: string + flowRate: string + admin: SubgraphAccount +} +interface SubgraphPoolMembership { pool: SubgraphPool; units: string; isConnected: boolean; totalAmountClaimed: string } +interface SubgraphLocker { id: string; lockerOwner: SubgraphAccount; blockNumber: string; blockTimestamp: string } + +export class SubgraphClient { + private client: GraphQLClient + private chainId: SupportedChains + private apiKey?: string + + constructor(chainId: SupportedChains, options: { apiKey?: string } = {}) { + this.chainId = chainId + this.apiKey = options.apiKey + const endpoint = SUBGRAPH_URLS[chainId] + if (!endpoint) { + throw new Error(`No subgraph endpoint configured for chain ${chainId}`) + } + + const headers: Record = {} + if (options.apiKey) { + headers["Authorization"] = `Bearer ${options.apiKey}` + } + + this.client = new GraphQLClient(endpoint, { + headers, + }) + } + + async queryStreams(options: GetStreamsOptions): Promise { + const { account, direction = "all" } = options + const data = await this.client.request<{ + outgoingStreams: SubgraphStream[] + incomingStreams: SubgraphStream[] + }>(GET_STREAMS, { account: account.toLowerCase(), first: 100, skip: 0 }) + + let streams: SubgraphStream[] = [] + if (direction === "outgoing") streams = data.outgoingStreams + else if (direction === "incoming") streams = data.incomingStreams + else streams = [...data.outgoingStreams, ...data.incomingStreams] + + return streams.map((s) => ({ + id: s.id, + sender: s.sender.id as Address, + receiver: s.receiver.id as Address, + token: s.token.id as Address, + currentFlowRate: BigInt(s.currentFlowRate), + streamedUntilUpdatedAt: BigInt(s.streamedUntilUpdatedAt), + updatedAtTimestamp: Number(s.updatedAtTimestamp), + createdAtTimestamp: Number(s.createdAtTimestamp), + })) + } + + async queryBalances(account: Address): Promise { + const data = await this.client.request<{ + account: { accountTokenSnapshots: SubgraphSnapshot[] } | null + }>(GET_TOKEN_BALANCE, { account: account.toLowerCase() }) + + return data.account?.accountTokenSnapshots.map((s) => ({ + account, + token: s.token.id as Address, + balance: BigInt(s.balanceUntilUpdatedAt), + balanceUntilUpdatedAt: BigInt(s.balanceUntilUpdatedAt), + updatedAtTimestamp: Number(s.updatedAtTimestamp), + })) || [] + } + + async queryBalanceHistory(options: GetBalanceHistoryOptions): Promise { + const { account, fromTimestamp, toTimestamp } = options + const data = await this.client.request<{ + accountTokenSnapshotLogs: SubgraphSnapshotLog[] + }>(GET_BALANCE_HISTORY, { + account: account.toLowerCase(), + fromTimestamp: fromTimestamp ? Math.floor(fromTimestamp / 1000) : undefined, + toTimestamp: toTimestamp ? Math.floor(toTimestamp / 1000) : undefined, + }) + + return data.accountTokenSnapshotLogs.map((log) => ({ + account, + token: log.token.id as Address, + balance: BigInt(log.balance), + balanceUntilUpdatedAt: BigInt(log.balance), + updatedAtTimestamp: Number(log.timestamp), + })) + } + + async queryPoolMemberships(account: Address): Promise { + const data = await this.client.request<{ + account: { poolMemberships: SubgraphPoolMembership[] } | null + }>(GET_POOL_MEMBERSHIPS, { account: account.toLowerCase() }) + + return data.account?.poolMemberships.map((m) => ({ + pool: m.pool.id as Address, + account, + units: BigInt(m.units), + isConnected: m.isConnected, + totalAmountClaimed: BigInt(m.totalAmountClaimed), + })) || [] + } + + async queryPools(): Promise { + const data = await this.client.request<{ pools: SubgraphPool[] }>(GET_DISTRIBUTION_POOLS, { first: 100, skip: 0 }) + return data.pools.map((p) => ({ + id: p.id as Address, + token: p.token.id as Address, + totalUnits: BigInt(p.totalUnits), + totalAmountClaimed: BigInt(p.totalAmountDistributedUntilUpdatedAt), + flowRate: BigInt(p.flowRate), + admin: p.admin.id as Address, + })) + } + + async querySUPReserves(): Promise { + const headers: Record = {} + if (this.apiKey) headers["Authorization"] = `Bearer ${this.apiKey}` + const supClient = new GraphQLClient(SUBGRAPH_URLS.supReserve, { headers }) + const data = await supClient.request<{ lockers: SubgraphLocker[] }>(GET_SUP_RESERVES) + + return data.lockers.map((l) => ({ + id: l.id, + lockerOwner: l.lockerOwner.id as Address, + blockNumber: BigInt(l.blockNumber), + blockTimestamp: BigInt(l.blockTimestamp), + })) + } +} diff --git a/packages/streaming-sdk/src/types.ts b/packages/streaming-sdk/src/types.ts new file mode 100644 index 0000000..51aba76 --- /dev/null +++ b/packages/streaming-sdk/src/types.ts @@ -0,0 +1,111 @@ +import { Address, Hash } from "viem" + +export type Environment = "production" | "staging" | "development" + +export interface StreamingSDKOptions { + chainId?: number + environment?: Environment + apiKey?: string +} + +// Stream Types +export interface StreamInfo { + sender: Address + receiver: Address + token: Address + flowRate: bigint + timestamp: bigint + streamedSoFar?: bigint +} + +export interface CreateStreamParams { + receiver: Address + token: Address + flowRate: bigint + userData?: `0x${string}` + onHash?: (hash: Hash) => void +} + +export interface UpdateStreamParams { + receiver: Address + token: Address + newFlowRate: bigint + userData?: `0x${string}` + onHash?: (hash: Hash) => void +} + +export interface DeleteStreamParams { + receiver: Address + token: Address + onHash?: (hash: Hash) => void +} + +// Subgraph Types +export interface SuperTokenBalance { + account: Address + token: Address + balance: bigint + balanceUntilUpdatedAt: bigint + updatedAtTimestamp: number +} + +export interface StreamQueryResult { + id: string + sender: Address + receiver: Address + token: Address + currentFlowRate: bigint + streamedUntilUpdatedAt: bigint + updatedAtTimestamp: number + createdAtTimestamp: number +} + +// GDA Pool Types +export interface GDAPool { + id: Address + token: Address + totalUnits: bigint + totalAmountClaimed: bigint + flowRate: bigint + admin: Address +} + +export interface PoolMembership { + pool: Address + account: Address + units: bigint + isConnected: boolean + totalAmountClaimed: bigint +} + +export interface ConnectToPoolParams { + poolAddress: Address + userData?: `0x${string}` + onHash?: (hash: Hash) => void +} + +export interface DisconnectFromPoolParams { + poolAddress: Address + userData?: `0x${string}` + onHash?: (hash: Hash) => void +} + +// SUP Reserve Types +export interface SUPReserveLocker { + id: string + lockerOwner: Address + blockNumber: bigint + blockTimestamp: bigint +} + +// Query Options +export interface GetStreamsOptions { + account: Address + direction?: "incoming" | "outgoing" | "all" +} + +export interface GetBalanceHistoryOptions { + account: Address + fromTimestamp?: number + toTimestamp?: number +} diff --git a/packages/streaming-sdk/src/utils.ts b/packages/streaming-sdk/src/utils.ts new file mode 100644 index 0000000..af4ed73 --- /dev/null +++ b/packages/streaming-sdk/src/utils.ts @@ -0,0 +1,104 @@ +import { Address, parseEther, formatEther } from "viem" +import { + SupportedChains, + CHAIN_CONFIGS, + getG$Token, +} from "./constants" +import { Environment } from "./types" + +/** + * Chain utilities + */ +export function isSupportedChain( + chainId: number | undefined, +): chainId is SupportedChains { + return ( + chainId === SupportedChains.CELO || + chainId === SupportedChains.CELO_ALFAJORES || + chainId === SupportedChains.BASE || + chainId === SupportedChains.BASE_SEPOLIA + ) +} + +export function validateChain(chainId: number | undefined): SupportedChains { + if (!isSupportedChain(chainId)) { + throw new Error( + `Unsupported chain ID: ${chainId}. Supported chains: Celo (42220), Alfajores (44787), Base (8453), Base Sepolia (84532)`, + ) + } + return chainId +} + +export function getSuperTokenAddress( + chainId: SupportedChains, + environment: Environment, +): Address { + const address = getG$Token(chainId, environment) + if (!address) { + throw new Error( + `G$ SuperToken address not configured for chain ${CHAIN_CONFIGS[chainId].name} in ${environment} environment`, + ) + } + return address +} + +export function getSuperTokenAddressSafe( + chainId: number | undefined, + environment: Environment, +): Address | undefined { + if (!isSupportedChain(chainId)) return undefined + return getG$Token(chainId, environment) +} + +export function getChainConfig(chainId: SupportedChains) { + return CHAIN_CONFIGS[chainId] +} + +/** + * Flow rate conversion utilities + */ +export type TimeUnit = "second" | "minute" | "hour" | "day" | "week" | "month" | "year" + +const TIME_UNITS_IN_SECONDS: Record = { + second: 1, + minute: 60, + hour: 3600, + day: 86400, + week: 604800, + month: 2592000, + year: 31536000, +} + +export function calculateFlowRate( + amountWei: bigint, + timeUnit: TimeUnit, +): bigint { + const secondsInUnit = BigInt(TIME_UNITS_IN_SECONDS[timeUnit]) + return amountWei / secondsInUnit +} + +export function calculateStreamedAmount( + flowRate: bigint, + durationSeconds: bigint, +): bigint { + return flowRate * durationSeconds +} + +export function formatFlowRate(flowRate: bigint, timeUnit: TimeUnit): string { + const secondsInUnit = BigInt(TIME_UNITS_IN_SECONDS[timeUnit]) + const amountPerUnit = flowRate * secondsInUnit + const formatted = formatEther(amountPerUnit) + const [integer, fraction] = formatted.split(".") + if (fraction && fraction.length > 4) { + return `${integer}.${fraction.slice(0, 4)} tokens/${timeUnit}` + } + return `${formatted} tokens/${timeUnit}` +} + +export function flowRateFromAmount( + amount: string, + timeUnit: TimeUnit, +): bigint { + const amountWei = parseEther(amount) + return calculateFlowRate(amountWei, timeUnit) +} diff --git a/packages/streaming-sdk/tsconfig.json b/packages/streaming-sdk/tsconfig.json new file mode 100644 index 0000000..0896596 --- /dev/null +++ b/packages/streaming-sdk/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "@repo/typescript-config/base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": [ + "src" + ], + "exclude": [ + "node_modules", + "dist" + ] +} \ No newline at end of file diff --git a/packages/streaming-sdk/tsup.config.ts b/packages/streaming-sdk/tsup.config.ts new file mode 100644 index 0000000..158c45f --- /dev/null +++ b/packages/streaming-sdk/tsup.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + dts: true, + format: ["esm", "cjs"], + treeshake: true, +}); diff --git a/packages/streaming-sdk/vitest.config.ts b/packages/streaming-sdk/vitest.config.ts new file mode 100644 index 0000000..7ae15f1 --- /dev/null +++ b/packages/streaming-sdk/vitest.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "vitest/config" + +export default defineConfig({ + test: { + globals: true, + environment: "node", + coverage: { + provider: "v8", + reporter: ["text", "json", "html"], + include: ["src/**/*.ts"], + exclude: ["src/**/*.test.ts", "src/**/__tests__/**"], + }, + }, +}) diff --git a/vercel.json b/vercel.json index 45c873b..b7d7882 100644 --- a/vercel.json +++ b/vercel.json @@ -1,5 +1,15 @@ { + "buildCommand": "yarn install --immutable && turbo build", + "installCommand": "yarn install --immutable", + "env": { + "TURBO_TOKEN": "@turbo_token@", + "TURBO_TEAM": "@team_id@" + }, "git": { "deploymentEnabled": false + }, + "remoteCache": { + "teamId": "@team_id@", + "token": "@turbo_token@" } } diff --git a/yarn.lock b/yarn.lock index e83fe8c..a1cdd6f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -747,6 +747,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/helper-string-parser@npm:7.27.1" + checksum: 10c0/8bda3448e07b5583727c103560bcf9c4c24b3c1051a4c516d4050ef69df37bb9a4734a585fe12725b8c2763de0a265aa1e909b485a4e3270b7cfd3e4dbe4b602 + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.25.9": version: 7.25.9 resolution: "@babel/helper-validator-identifier@npm:7.25.9" @@ -754,6 +761,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.28.5": + version: 7.28.5 + resolution: "@babel/helper-validator-identifier@npm:7.28.5" + checksum: 10c0/42aaebed91f739a41f3d80b72752d1f95fd7c72394e8e4bd7cdd88817e0774d80a432451bcba17c2c642c257c483bf1d409dd4548883429ea9493a3bc4ab0847 + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.25.9": version: 7.25.9 resolution: "@babel/helper-validator-option@npm:7.25.9" @@ -803,6 +817,17 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.29.0": + version: 7.29.0 + resolution: "@babel/parser@npm:7.29.0" + dependencies: + "@babel/types": "npm:^7.29.0" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/333b2aa761264b91577a74bee86141ef733f9f9f6d4fc52548e4847dc35dfbf821f58c46832c637bfa761a6d9909d6a68f7d1ed59e17e4ffbb958dc510c17b62 + languageName: node + linkType: hard + "@babel/plugin-syntax-jsx@npm:^7.25.9": version: 7.25.9 resolution: "@babel/plugin-syntax-jsx@npm:7.25.9" @@ -941,6 +966,23 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.29.0": + version: 7.29.0 + resolution: "@babel/types@npm:7.29.0" + dependencies: + "@babel/helper-string-parser": "npm:^7.27.1" + "@babel/helper-validator-identifier": "npm:^7.28.5" + checksum: 10c0/23cc3466e83bcbfab8b9bd0edaafdb5d4efdb88b82b3be6728bbade5ba2f0996f84f63b1c5f7a8c0d67efded28300898a5f930b171bb40b311bca2029c4e9b4f + languageName: node + linkType: hard + +"@bcoe/v8-coverage@npm:^1.0.2": + version: 1.0.2 + resolution: "@bcoe/v8-coverage@npm:1.0.2" + checksum: 10c0/1eb1dc93cc17fb7abdcef21a6e7b867d6aa99a7ec88ec8207402b23d9083ab22a8011213f04b2cf26d535f1d22dc26139b7929e6c2134c254bd1e14ba5e678c3 + languageName: node + linkType: hard + "@bytecodealliance/preview2-shim@npm:0.17.0": version: 0.17.0 resolution: "@bytecodealliance/preview2-shim@npm:0.17.0" @@ -1026,6 +1068,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/aix-ppc64@npm:0.27.3" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/android-arm64@npm:0.24.2" @@ -1040,6 +1089,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/android-arm64@npm:0.27.3" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/android-arm@npm:0.24.2" @@ -1054,6 +1110,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/android-arm@npm:0.27.3" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/android-x64@npm:0.24.2" @@ -1068,6 +1131,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/android-x64@npm:0.27.3" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/darwin-arm64@npm:0.24.2" @@ -1082,6 +1152,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/darwin-arm64@npm:0.27.3" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/darwin-x64@npm:0.24.2" @@ -1096,6 +1173,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/darwin-x64@npm:0.27.3" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/freebsd-arm64@npm:0.24.2" @@ -1110,6 +1194,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/freebsd-arm64@npm:0.27.3" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/freebsd-x64@npm:0.24.2" @@ -1124,6 +1215,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/freebsd-x64@npm:0.27.3" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/linux-arm64@npm:0.24.2" @@ -1138,6 +1236,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/linux-arm64@npm:0.27.3" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/linux-arm@npm:0.24.2" @@ -1152,6 +1257,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/linux-arm@npm:0.27.3" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/linux-ia32@npm:0.24.2" @@ -1166,6 +1278,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/linux-ia32@npm:0.27.3" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/linux-loong64@npm:0.24.2" @@ -1180,6 +1299,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/linux-loong64@npm:0.27.3" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/linux-mips64el@npm:0.24.2" @@ -1194,6 +1320,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/linux-mips64el@npm:0.27.3" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/linux-ppc64@npm:0.24.2" @@ -1208,6 +1341,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/linux-ppc64@npm:0.27.3" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/linux-riscv64@npm:0.24.2" @@ -1222,6 +1362,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/linux-riscv64@npm:0.27.3" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/linux-s390x@npm:0.24.2" @@ -1236,6 +1383,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/linux-s390x@npm:0.27.3" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/linux-x64@npm:0.24.2" @@ -1250,6 +1404,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/linux-x64@npm:0.27.3" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-arm64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/netbsd-arm64@npm:0.24.2" @@ -1264,6 +1425,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-arm64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/netbsd-arm64@npm:0.27.3" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/netbsd-x64@npm:0.24.2" @@ -1278,6 +1446,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/netbsd-x64@npm:0.27.3" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openbsd-arm64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/openbsd-arm64@npm:0.24.2" @@ -1292,6 +1467,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-arm64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/openbsd-arm64@npm:0.27.3" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/openbsd-x64@npm:0.24.2" @@ -1306,6 +1488,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/openbsd-x64@npm:0.27.3" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openharmony-arm64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/openharmony-arm64@npm:0.27.3" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/sunos-x64@npm:0.24.2" @@ -1320,6 +1516,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/sunos-x64@npm:0.27.3" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/win32-arm64@npm:0.24.2" @@ -1334,6 +1537,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/win32-arm64@npm:0.27.3" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/win32-ia32@npm:0.24.2" @@ -1348,6 +1558,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/win32-ia32@npm:0.27.3" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.24.2": version: 0.24.2 resolution: "@esbuild/win32-x64@npm:0.24.2" @@ -1362,6 +1579,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/win32-x64@npm:0.27.3" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.1 resolution: "@eslint-community/eslint-utils@npm:4.4.1" @@ -1434,6 +1658,13 @@ __metadata: languageName: node linkType: hard +"@eslint/js@npm:8.57.0": + version: 8.57.0 + resolution: "@eslint/js@npm:8.57.0" + checksum: 10c0/9a518bb8625ba3350613903a6d8c622352ab0c6557a59fe6ff6178bf882bf57123f9d92aa826ee8ac3ee74b9c6203fe630e9ee00efb03d753962dcf65ee4bd94 + languageName: node + linkType: hard + "@eslint/js@npm:8.57.1": version: 8.57.1 resolution: "@eslint/js@npm:8.57.1" @@ -1999,7 +2230,9 @@ __metadata: resolution: "@goodsdks/react-hooks@workspace:packages/react-hooks" dependencies: "@goodsdks/citizen-sdk": "npm:*" + "@goodsdks/streaming-sdk": "workspace:*" "@repo/typescript-config": "workspace:*" + "@tanstack/react-query": "npm:^4.36.1" "@types/react": "npm:^19" lz-string: "npm:^1.5.0" react: "npm:^19.1.1" @@ -2041,6 +2274,25 @@ __metadata: languageName: unknown linkType: soft +"@goodsdks/streaming-sdk@npm:*, @goodsdks/streaming-sdk@workspace:*, @goodsdks/streaming-sdk@workspace:packages/streaming-sdk": + version: 0.0.0-use.local + resolution: "@goodsdks/streaming-sdk@workspace:packages/streaming-sdk" + dependencies: + "@repo/typescript-config": "workspace:*" + "@sfpro/sdk": "npm:^0.1.0" + "@types/node": "npm:latest" + "@vitest/coverage-v8": "npm:^4.0.18" + graphql: "npm:^16.9.0" + graphql-request: "npm:^7.1.2" + tsup: "npm:^8.3.5" + typescript: "npm:latest" + viem: "npm:latest" + vitest: "npm:^4.0.18" + peerDependencies: + viem: "*" + languageName: unknown + linkType: soft + "@goodsdks/ui-components@npm:*, @goodsdks/ui-components@workspace:packages/ui-components": version: 0.0.0-use.local resolution: "@goodsdks/ui-components@workspace:packages/ui-components" @@ -2057,6 +2309,15 @@ __metadata: languageName: unknown linkType: soft +"@graphql-typed-document-node/core@npm:^3.2.0": + version: 3.2.0 + resolution: "@graphql-typed-document-node/core@npm: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 + checksum: 10c0/94e9d75c1f178bbae8d874f5a9361708a3350c8def7eaeb6920f2c820e82403b7d4f55b3735856d68e145e86c85cbfe2adc444fdc25519cd51f108697e99346c + languageName: node + linkType: hard + "@hookform/resolvers@npm:^3.10.0": version: 3.10.0 resolution: "@hookform/resolvers@npm:3.10.0" @@ -2177,6 +2438,13 @@ __metadata: languageName: node linkType: hard +"@jridgewell/sourcemap-codec@npm:^1.5.5": + version: 1.5.5 + resolution: "@jridgewell/sourcemap-codec@npm:1.5.5" + checksum: 10c0/f9e538f302b63c0ebc06eecb1dd9918dd4289ed36147a0ddce35d6ea4d7ebbda243cda7b2213b6a5e1d8087a298d5cf630fb2bd39329cdecb82017023f6081a0 + languageName: node + linkType: hard + "@jridgewell/trace-mapping@npm:0.3.9": version: 0.3.9 resolution: "@jridgewell/trace-mapping@npm:0.3.9" @@ -2197,6 +2465,16 @@ __metadata: languageName: node linkType: hard +"@jridgewell/trace-mapping@npm:^0.3.31": + version: 0.3.31 + resolution: "@jridgewell/trace-mapping@npm:0.3.31" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.1.0" + "@jridgewell/sourcemap-codec": "npm:^1.4.14" + checksum: 10c0/4b30ec8cd56c5fd9a661f088230af01e0c1a3888d11ffb6b47639700f71225be21d1f7e168048d6d4f9449207b978a235c07c8f15c07705685d16dc06280e9d9 + languageName: node + linkType: hard + "@lit-labs/ssr-dom-shim@npm:^1.0.0, @lit-labs/ssr-dom-shim@npm:^1.1.0, @lit-labs/ssr-dom-shim@npm:^1.2.0": version: 1.3.0 resolution: "@lit-labs/ssr-dom-shim@npm:1.3.0" @@ -4178,6 +4456,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-android-arm-eabi@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.57.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@rollup/rollup-android-arm64@npm:4.34.9": version: 4.34.9 resolution: "@rollup/rollup-android-arm64@npm:4.34.9" @@ -4192,6 +4477,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-android-arm64@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-android-arm64@npm:4.57.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-darwin-arm64@npm:4.34.9": version: 4.34.9 resolution: "@rollup/rollup-darwin-arm64@npm:4.34.9" @@ -4206,6 +4498,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-darwin-arm64@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-darwin-arm64@npm:4.57.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-darwin-x64@npm:4.34.9": version: 4.34.9 resolution: "@rollup/rollup-darwin-x64@npm:4.34.9" @@ -4220,6 +4519,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-darwin-x64@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-darwin-x64@npm:4.57.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@rollup/rollup-freebsd-arm64@npm:4.34.9": version: 4.34.9 resolution: "@rollup/rollup-freebsd-arm64@npm:4.34.9" @@ -4234,6 +4540,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-freebsd-arm64@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.57.1" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-freebsd-x64@npm:4.34.9": version: 4.34.9 resolution: "@rollup/rollup-freebsd-x64@npm:4.34.9" @@ -4248,6 +4561,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-freebsd-x64@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-freebsd-x64@npm:4.57.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@rollup/rollup-linux-arm-gnueabihf@npm:4.34.9": version: 4.34.9 resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.34.9" @@ -4262,6 +4582,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm-gnueabihf@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.57.1" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-arm-musleabihf@npm:4.34.9": version: 4.34.9 resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.34.9" @@ -4276,6 +4603,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm-musleabihf@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.57.1" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-linux-arm64-gnu@npm:4.34.9": version: 4.34.9 resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.34.9" @@ -4290,6 +4624,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm64-gnu@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.57.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-arm64-musl@npm:4.34.9": version: 4.34.9 resolution: "@rollup/rollup-linux-arm64-musl@npm:4.34.9" @@ -4304,6 +4645,27 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm64-musl@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.57.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-gnu@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.57.1" + conditions: os=linux & cpu=loong64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-musl@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-linux-loong64-musl@npm:4.57.1" + conditions: os=linux & cpu=loong64 & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-linux-loongarch64-gnu@npm:4.34.9": version: 4.34.9 resolution: "@rollup/rollup-linux-loongarch64-gnu@npm:4.34.9" @@ -4332,6 +4694,20 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-ppc64-gnu@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.57.1" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-musl@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-linux-ppc64-musl@npm:4.57.1" + conditions: os=linux & cpu=ppc64 & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-linux-riscv64-gnu@npm:4.34.9": version: 4.34.9 resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.34.9" @@ -4346,6 +4722,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-riscv64-gnu@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.57.1" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-riscv64-musl@npm:4.44.2": version: 4.44.2 resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.44.2" @@ -4353,6 +4736,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-riscv64-musl@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.57.1" + conditions: os=linux & cpu=riscv64 & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-linux-s390x-gnu@npm:4.34.9": version: 4.34.9 resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.34.9" @@ -4367,6 +4757,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-s390x-gnu@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.57.1" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-x64-gnu@npm:4.34.9": version: 4.34.9 resolution: "@rollup/rollup-linux-x64-gnu@npm:4.34.9" @@ -4381,6 +4778,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-x64-gnu@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.57.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-x64-musl@npm:4.34.9": version: 4.34.9 resolution: "@rollup/rollup-linux-x64-musl@npm:4.34.9" @@ -4395,6 +4799,27 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-x64-musl@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.57.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-openbsd-x64@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-openbsd-x64@npm:4.57.1" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-openharmony-arm64@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-openharmony-arm64@npm:4.57.1" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-win32-arm64-msvc@npm:4.34.9": version: 4.34.9 resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.34.9" @@ -4409,6 +4834,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-arm64-msvc@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.57.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-win32-ia32-msvc@npm:4.34.9": version: 4.34.9 resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.34.9" @@ -4423,6 +4855,20 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-ia32-msvc@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.57.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-gnu@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-win32-x64-gnu@npm:4.57.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@rollup/rollup-win32-x64-msvc@npm:4.34.9": version: 4.34.9 resolution: "@rollup/rollup-win32-x64-msvc@npm:4.34.9" @@ -4437,6 +4883,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-x64-msvc@npm:4.57.1": + version: 4.57.1 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.57.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@safe-global/safe-apps-provider@npm:0.18.5": version: 0.18.5 resolution: "@safe-global/safe-apps-provider@npm:0.18.5" @@ -4623,6 +5076,27 @@ __metadata: languageName: node linkType: hard +"@sfpro/sdk@npm:^0.1.0": + version: 0.1.9 + resolution: "@sfpro/sdk@npm:0.1.9" + peerDependencies: + "@wagmi/core": ^2 + react: ">=18" + viem: ^2 + wagmi: ^2 + peerDependenciesMeta: + "@wagmi/core": + optional: true + react: + optional: true + viem: + optional: true + wagmi: + optional: true + checksum: 10c0/b800a428948d18674fb9ca2a1cec013cd56cdfd844f9b859249d7c174d1f4f99a2eafe74f542f4c11122c7c049389f68d5bbdc72278bcbbbb89586379034abe7 + languageName: node + linkType: hard + "@smithy/abort-controller@npm:^4.0.1": version: 4.0.1 resolution: "@smithy/abort-controller@npm:4.0.1" @@ -7170,6 +7644,16 @@ __metadata: languageName: node linkType: hard +"@types/chai@npm:^5.2.2": + version: 5.2.3 + resolution: "@types/chai@npm:5.2.3" + dependencies: + "@types/deep-eql": "npm:*" + assertion-error: "npm:^2.0.1" + checksum: 10c0/e0ef1de3b6f8045a5e473e867c8565788c444271409d155588504840ad1a53611011f85072188c2833941189400228c1745d78323dac13fcede9c2b28bacfb2f + languageName: node + linkType: hard + "@types/cookie@npm:^0.6.0": version: 0.6.0 resolution: "@types/cookie@npm:0.6.0" @@ -7269,7 +7753,7 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:1.0.8": +"@types/estree@npm:1.0.8, @types/estree@npm:^1.0.0": version: 1.0.8 resolution: "@types/estree@npm:1.0.8" checksum: 10c0/39d34d1afaa338ab9763f37ad6066e3f349444f9052b9676a7cc0252ef9485a41c6d81c9c4e0d26e9077993354edf25efc853f3224dd4b447175ef62bdcc86a5 @@ -7362,6 +7846,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:latest": + version: 25.2.3 + resolution: "@types/node@npm:25.2.3" + dependencies: + undici-types: "npm:~7.16.0" + checksum: 10c0/925833029ce0bb4a72c36f90b93287184d3511aeb0fa60a994ae94b5430c22f9be6693d67d210df79267cb54c6f6978caaefb149d99ab5f83af5827ba7cb9822 + languageName: node + linkType: hard + "@types/pbkdf2@npm:^3.0.0": version: 3.1.2 resolution: "@types/pbkdf2@npm:3.1.2" @@ -7460,6 +7953,29 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/eslint-plugin@npm:8.0.0": + version: 8.0.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.0.0" + dependencies: + "@eslint-community/regexpp": "npm:^4.10.0" + "@typescript-eslint/scope-manager": "npm:8.0.0" + "@typescript-eslint/type-utils": "npm:8.0.0" + "@typescript-eslint/utils": "npm:8.0.0" + "@typescript-eslint/visitor-keys": "npm:8.0.0" + graphemer: "npm:^1.4.0" + ignore: "npm:^5.3.1" + natural-compare: "npm:^1.4.0" + ts-api-utils: "npm:^1.3.0" + peerDependencies: + "@typescript-eslint/parser": ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/e98304410039bbb7104fdd8ad7a70f2fb81430c117b66d609b44d10cc8937c8a936a4e5993b0b6df5361c00df43a146e89632a37f2407ce9bed3555733c71fc2 + languageName: node + linkType: hard + "@typescript-eslint/eslint-plugin@npm:8.26.0": version: 8.26.0 resolution: "@typescript-eslint/eslint-plugin@npm:8.26.0" @@ -7505,6 +8021,24 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/parser@npm:8.0.0": + version: 8.0.0 + resolution: "@typescript-eslint/parser@npm:8.0.0" + dependencies: + "@typescript-eslint/scope-manager": "npm:8.0.0" + "@typescript-eslint/types": "npm:8.0.0" + "@typescript-eslint/typescript-estree": "npm:8.0.0" + "@typescript-eslint/visitor-keys": "npm:8.0.0" + debug: "npm:^4.3.4" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/7b462bc975c8e0c0d9fbc4955186d61b73aad9d5b9392e8fa68ad4b7c631582edc05176fcbfbebee603695421225e8c5f5ee28812fa47e3060fc7854b84497d5 + languageName: node + linkType: hard + "@typescript-eslint/parser@npm:8.26.0": version: 8.26.0 resolution: "@typescript-eslint/parser@npm:8.26.0" @@ -7548,6 +8082,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:8.0.0": + version: 8.0.0 + resolution: "@typescript-eslint/scope-manager@npm:8.0.0" + dependencies: + "@typescript-eslint/types": "npm:8.0.0" + "@typescript-eslint/visitor-keys": "npm:8.0.0" + checksum: 10c0/d8397055f046be54302b603a59d358c74292f72af3d12ca1e652316a785400d3e2fd20d79e3e316e3278ff7f1c1ffb271f9f6a7a265b88041c5a4e8332f550a0 + languageName: node + linkType: hard + "@typescript-eslint/scope-manager@npm:8.26.0": version: 8.26.0 resolution: "@typescript-eslint/scope-manager@npm:8.26.0" @@ -7575,6 +8119,21 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/type-utils@npm:8.0.0": + version: 8.0.0 + resolution: "@typescript-eslint/type-utils@npm:8.0.0" + dependencies: + "@typescript-eslint/typescript-estree": "npm:8.0.0" + "@typescript-eslint/utils": "npm:8.0.0" + debug: "npm:^4.3.4" + ts-api-utils: "npm:^1.3.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/96ba2a58ceff420dac79a9c3c2bde15e0a56e8c47baf441a62886c9d8df3db6e9d886cd5c717c6f9a8cfceb545a511c7d452aa1537c2cd3b127bd47509e559ae + languageName: node + linkType: hard + "@typescript-eslint/type-utils@npm:8.26.0": version: 8.26.0 resolution: "@typescript-eslint/type-utils@npm:8.26.0" @@ -7597,6 +8156,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:8.0.0": + version: 8.0.0 + resolution: "@typescript-eslint/types@npm:8.0.0" + checksum: 10c0/c15efce96e4b80c2bef7ea4fa7f046609816ae8bc3a4e31d9d671e237520f6b96595e1330a891ec7042bc7b09fc16d265bad49fd878d5fb8be4b59b8a752e5b5 + languageName: node + linkType: hard + "@typescript-eslint/types@npm:8.26.0": version: 8.26.0 resolution: "@typescript-eslint/types@npm:8.26.0" @@ -7622,6 +8188,25 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:8.0.0": + version: 8.0.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.0.0" + dependencies: + "@typescript-eslint/types": "npm:8.0.0" + "@typescript-eslint/visitor-keys": "npm:8.0.0" + debug: "npm:^4.3.4" + globby: "npm:^11.1.0" + is-glob: "npm:^4.0.3" + minimatch: "npm:^9.0.4" + semver: "npm:^7.6.0" + ts-api-utils: "npm:^1.3.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/a82f3eb2a66a4b2715d09f8f9547c1f0c27ea60c1d10d0777c8ce998b760dbb8ef14466fc2056220b8a236c2d2dc3ee99f482502f5c268bd40909b272bb47eb4 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:8.26.0": version: 8.26.0 resolution: "@typescript-eslint/typescript-estree@npm:8.26.0" @@ -7658,6 +8243,20 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/utils@npm:8.0.0": + version: 8.0.0 + resolution: "@typescript-eslint/utils@npm:8.0.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.4.0" + "@typescript-eslint/scope-manager": "npm:8.0.0" + "@typescript-eslint/types": "npm:8.0.0" + "@typescript-eslint/typescript-estree": "npm:8.0.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + checksum: 10c0/ecba01996d1aa330c640c41c1212fed2328a47768348ab6886080e0e55bc3b2041939a635a7440d47db533f0c4b21e1eb8b58535a9eaebbbe2c035906e12ba06 + languageName: node + linkType: hard + "@typescript-eslint/utils@npm:8.26.0": version: 8.26.0 resolution: "@typescript-eslint/utils@npm:8.26.0" @@ -7683,6 +8282,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:8.0.0": + version: 8.0.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.0.0" + dependencies: + "@typescript-eslint/types": "npm:8.0.0" + eslint-visitor-keys: "npm:^3.4.3" + checksum: 10c0/8c59a2e971370c2b9a5727541c72d6b64fd0448ab03dd8b4274a26bddea5e1b4c560dd7856e1f48577cd333f7bbbed7a0f1849d39e2d1b48a748a3668c1a3723 + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:8.26.0": version: 8.26.0 resolution: "@typescript-eslint/visitor-keys@npm:8.26.0" @@ -7715,6 +8324,110 @@ __metadata: languageName: node linkType: hard +"@vitest/coverage-v8@npm:^4.0.18": + version: 4.0.18 + resolution: "@vitest/coverage-v8@npm:4.0.18" + dependencies: + "@bcoe/v8-coverage": "npm:^1.0.2" + "@vitest/utils": "npm:4.0.18" + ast-v8-to-istanbul: "npm:^0.3.10" + istanbul-lib-coverage: "npm:^3.2.2" + istanbul-lib-report: "npm:^3.0.1" + istanbul-reports: "npm:^3.2.0" + magicast: "npm:^0.5.1" + obug: "npm:^2.1.1" + std-env: "npm:^3.10.0" + tinyrainbow: "npm:^3.0.3" + peerDependencies: + "@vitest/browser": 4.0.18 + vitest: 4.0.18 + peerDependenciesMeta: + "@vitest/browser": + optional: true + checksum: 10c0/e23e0da86f0b2a020c51562bc40ebdc7fc7553c24f8071dfb39a6df0161badbd5eaf2eebbf8ceaef18933a18c1934ff52d1c0c4bde77bb87e0c1feb0c8cbee4d + languageName: node + linkType: hard + +"@vitest/expect@npm:4.0.18": + version: 4.0.18 + resolution: "@vitest/expect@npm:4.0.18" + dependencies: + "@standard-schema/spec": "npm:^1.0.0" + "@types/chai": "npm:^5.2.2" + "@vitest/spy": "npm:4.0.18" + "@vitest/utils": "npm:4.0.18" + chai: "npm:^6.2.1" + tinyrainbow: "npm:^3.0.3" + checksum: 10c0/123b0aa111682e82ec5289186df18037b1a1768700e468ee0f9879709aaa320cf790463c15c0d8ee10df92b402f4394baf5d27797e604d78e674766d87bcaadc + languageName: node + linkType: hard + +"@vitest/mocker@npm:4.0.18": + version: 4.0.18 + resolution: "@vitest/mocker@npm:4.0.18" + dependencies: + "@vitest/spy": "npm:4.0.18" + estree-walker: "npm:^3.0.3" + magic-string: "npm:^0.30.21" + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + checksum: 10c0/fb0a257e7e167759d4ad228d53fa7bad2267586459c4a62188f2043dd7163b4b02e1e496dc3c227837f776e7d73d6c4343613e89e7da379d9d30de8260f1ee4b + languageName: node + linkType: hard + +"@vitest/pretty-format@npm:4.0.18": + version: 4.0.18 + resolution: "@vitest/pretty-format@npm:4.0.18" + dependencies: + tinyrainbow: "npm:^3.0.3" + checksum: 10c0/0086b8c88eeca896d8e4b98fcdef452c8041a1b63eb9e85d3e0bcc96c8aa76d8e9e0b6990ebb0bb0a697c4ebab347e7735888b24f507dbff2742ddce7723fd94 + languageName: node + linkType: hard + +"@vitest/runner@npm:4.0.18": + version: 4.0.18 + resolution: "@vitest/runner@npm:4.0.18" + dependencies: + "@vitest/utils": "npm:4.0.18" + pathe: "npm:^2.0.3" + checksum: 10c0/fdb4afa411475133c05ba266c8092eaf1e56cbd5fb601f92ec6ccb9bab7ca52e06733ee8626599355cba4ee71cb3a8f28c84d3b69dc972e41047edc50229bc01 + languageName: node + linkType: hard + +"@vitest/snapshot@npm:4.0.18": + version: 4.0.18 + resolution: "@vitest/snapshot@npm:4.0.18" + dependencies: + "@vitest/pretty-format": "npm:4.0.18" + magic-string: "npm:^0.30.21" + pathe: "npm:^2.0.3" + checksum: 10c0/d3bfefa558db9a69a66886ace6575eb96903a5ba59f4d9a5d0fecb4acc2bb8dbb443ef409f5ac1475f2e1add30bd1d71280f98912da35e89c75829df9e84ea43 + languageName: node + linkType: hard + +"@vitest/spy@npm:4.0.18": + version: 4.0.18 + resolution: "@vitest/spy@npm:4.0.18" + checksum: 10c0/6de537890b3994fcadb8e8d8ac05942320ae184f071ec395d978a5fba7fa928cbb0c5de85af86a1c165706c466e840de8779eaff8c93450c511c7abaeb9b8a4e + languageName: node + linkType: hard + +"@vitest/utils@npm:4.0.18": + version: 4.0.18 + resolution: "@vitest/utils@npm:4.0.18" + dependencies: + "@vitest/pretty-format": "npm:4.0.18" + tinyrainbow: "npm:^3.0.3" + checksum: 10c0/4a3c43c1421eb90f38576926496f6c80056167ba111e63f77cf118983902673737a1a38880b890d7c06ec0a12475024587344ee502b3c43093781533022f2aeb + languageName: node + linkType: hard + "@wagmi/connectors@npm:5.7.7": version: 5.7.7 resolution: "@wagmi/connectors@npm:5.7.7" @@ -8753,6 +9466,24 @@ __metadata: languageName: node linkType: hard +"assertion-error@npm:^2.0.1": + version: 2.0.1 + resolution: "assertion-error@npm:2.0.1" + checksum: 10c0/bbbcb117ac6480138f8c93cf7f535614282dea9dc828f540cdece85e3c665e8f78958b96afac52f29ff883c72638e6a87d469ecc9fe5bc902df03ed24a55dba8 + languageName: node + linkType: hard + +"ast-v8-to-istanbul@npm:^0.3.10": + version: 0.3.11 + resolution: "ast-v8-to-istanbul@npm:0.3.11" + dependencies: + "@jridgewell/trace-mapping": "npm:^0.3.31" + estree-walker: "npm:^3.0.3" + js-tokens: "npm:^10.0.0" + checksum: 10c0/0667dcb5f42bd16f5d50b8687f3471f9b9d000ea7f8808c3cd0ddabc1ef7d5b1a61e19f498d5ca7b1285e6c185e11d0ae724c4f9291491b50b6340110ce63108 + languageName: node + linkType: hard + "astral-regex@npm:^2.0.0": version: 2.0.0 resolution: "astral-regex@npm:2.0.0" @@ -9276,6 +10007,13 @@ __metadata: languageName: node linkType: hard +"chai@npm:^6.2.1": + version: 6.2.2 + resolution: "chai@npm:6.2.2" + checksum: 10c0/e6c69e5f0c11dffe6ea13d0290936ebb68fcc1ad688b8e952e131df6a6d5797d5e860bc55cef1aca2e950c3e1f96daf79e9d5a70fb7dbaab4e46355e2635ed53 + languageName: node + linkType: hard + "chalk@npm:4.1.2, chalk@npm:^4.0.0, chalk@npm:^4.1.0": version: 4.1.2 resolution: "chalk@npm:4.1.2" @@ -10033,8 +10771,10 @@ __metadata: version: 0.0.0-use.local resolution: "demo-identity-app@workspace:apps/demo-identity-app" dependencies: + "@eslint/js": "npm:8.57.0" "@goodsdks/citizen-sdk": "npm:*" "@goodsdks/react-hooks": "npm:*" + "@goodsdks/streaming-sdk": "npm:*" "@reown/appkit": "npm:^1.7.2" "@reown/appkit-adapter-wagmi": "npm:^1.7.2" "@reown/appkit-wallet": "npm:^1.7.2" @@ -10057,6 +10797,8 @@ __metadata: eslint-config-prettier: "npm:^8.10.0" eslint-plugin-react: "npm:^7.37.4" eslint-plugin-react-hooks: "npm:^5.2.0" + eslint-plugin-react-refresh: "npm:0.4.12" + globals: "npm:15.0.0" lz-string: "npm:^1.5.0" moment: "npm:^2.30.1" postcss: "npm:^8.5.3" @@ -10067,6 +10809,7 @@ __metadata: sass: "npm:^1.85.1" tamagui: "npm:^1.125.22" typescript: "npm:^5.8.2" + typescript-eslint: "npm:8.0.0" viem: "npm:^1.21.4" vite: "npm:6.3.5" wagmi: "npm:^1.4.13" @@ -10477,6 +11220,13 @@ __metadata: languageName: node linkType: hard +"es-module-lexer@npm:^1.7.0": + version: 1.7.0 + resolution: "es-module-lexer@npm:1.7.0" + checksum: 10c0/4c935affcbfeba7fb4533e1da10fa8568043df1e3574b869385980de9e2d475ddc36769891936dbb07036edb3c3786a8b78ccf44964cd130dedc1f2c984b6c7b + languageName: node + linkType: hard + "es-object-atoms@npm:^1.0.0": version: 1.0.1 resolution: "es-object-atoms@npm:1.0.1" @@ -10734,7 +11484,96 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 10c0/5767b72da46da3cfec51661647ec850ddbf8a8d0662771139f10ef0692a8831396a0004b2be7966cecdb08264fb16bdc16290dcecd92396fac5f12d722fa013d + checksum: 10c0/5767b72da46da3cfec51661647ec850ddbf8a8d0662771139f10ef0692a8831396a0004b2be7966cecdb08264fb16bdc16290dcecd92396fac5f12d722fa013d + languageName: node + linkType: hard + +"esbuild@npm:^0.27.0": + version: 0.27.3 + resolution: "esbuild@npm:0.27.3" + dependencies: + "@esbuild/aix-ppc64": "npm:0.27.3" + "@esbuild/android-arm": "npm:0.27.3" + "@esbuild/android-arm64": "npm:0.27.3" + "@esbuild/android-x64": "npm:0.27.3" + "@esbuild/darwin-arm64": "npm:0.27.3" + "@esbuild/darwin-x64": "npm:0.27.3" + "@esbuild/freebsd-arm64": "npm:0.27.3" + "@esbuild/freebsd-x64": "npm:0.27.3" + "@esbuild/linux-arm": "npm:0.27.3" + "@esbuild/linux-arm64": "npm:0.27.3" + "@esbuild/linux-ia32": "npm:0.27.3" + "@esbuild/linux-loong64": "npm:0.27.3" + "@esbuild/linux-mips64el": "npm:0.27.3" + "@esbuild/linux-ppc64": "npm:0.27.3" + "@esbuild/linux-riscv64": "npm:0.27.3" + "@esbuild/linux-s390x": "npm:0.27.3" + "@esbuild/linux-x64": "npm:0.27.3" + "@esbuild/netbsd-arm64": "npm:0.27.3" + "@esbuild/netbsd-x64": "npm:0.27.3" + "@esbuild/openbsd-arm64": "npm:0.27.3" + "@esbuild/openbsd-x64": "npm:0.27.3" + "@esbuild/openharmony-arm64": "npm:0.27.3" + "@esbuild/sunos-x64": "npm:0.27.3" + "@esbuild/win32-arm64": "npm:0.27.3" + "@esbuild/win32-ia32": "npm:0.27.3" + "@esbuild/win32-x64": "npm:0.27.3" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/openharmony-arm64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/fdc3f87a3f08b3ef98362f37377136c389a0d180fda4b8d073b26ba930cf245521db0a368f119cc7624bc619248fff1439f5811f062d853576f8ffa3df8ee5f1 languageName: node linkType: hard @@ -10816,6 +11655,15 @@ __metadata: languageName: node linkType: hard +"eslint-plugin-react-refresh@npm:0.4.12": + version: 0.4.12 + resolution: "eslint-plugin-react-refresh@npm:0.4.12" + peerDependencies: + eslint: ">=7" + checksum: 10c0/33dd82450f7c5fa884c5c84ffaf9d9a8b363bc155432807dc09904c7db6ba724888fac4562b058268259aa7c9270b622ef411488011b3469a2add275ed5c2273 + languageName: node + linkType: hard + "eslint-plugin-react-refresh@npm:^0.4.16": version: 0.4.19 resolution: "eslint-plugin-react-refresh@npm:0.4.19" @@ -11094,6 +11942,15 @@ __metadata: languageName: node linkType: hard +"estree-walker@npm:^3.0.3": + version: 3.0.3 + resolution: "estree-walker@npm:3.0.3" + dependencies: + "@types/estree": "npm:^1.0.0" + checksum: 10c0/c12e3c2b2642d2bcae7d5aa495c60fa2f299160946535763969a1c83fc74518ffa9c2cd3a8b69ac56aea547df6a8aac25f729a342992ef0bbac5f1c73e78995d + languageName: node + linkType: hard + "esutils@npm:^2.0.2": version: 2.0.3 resolution: "esutils@npm:2.0.3" @@ -11331,6 +12188,13 @@ __metadata: languageName: node linkType: hard +"expect-type@npm:^1.2.2": + version: 1.3.0 + resolution: "expect-type@npm:1.3.0" + checksum: 10c0/8412b3fe4f392c420ab41dae220b09700e4e47c639a29ba7ba2e83cc6cffd2b4926f7ac9e47d7e277e8f4f02acda76fd6931cb81fd2b382fa9477ef9ada953fd + languageName: node + linkType: hard + "exponential-backoff@npm:^3.1.1": version: 3.1.1 resolution: "exponential-backoff@npm:3.1.1" @@ -11508,6 +12372,18 @@ __metadata: languageName: node linkType: hard +"fdir@npm:^6.5.0": + version: 6.5.0 + resolution: "fdir@npm:6.5.0" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10c0/e345083c4306b3aed6cb8ec551e26c36bab5c511e99ea4576a16750ddc8d3240e63826cc624f5ae17ad4dc82e68a253213b60d556c11bfad064b7607847ed07f + languageName: node + linkType: hard + "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" @@ -12037,6 +12913,13 @@ __metadata: languageName: node linkType: hard +"globals@npm:15.0.0": + version: 15.0.0 + resolution: "globals@npm:15.0.0" + checksum: 10c0/b93e356a7bd4562d73a9defa95a0ff5e8a0b7726a4e2af16bd8ad019e14cd21d85e0a27b46e7e270d34e25df0bc0f9473ca21b47266c406c0e40973956085777 + languageName: node + linkType: hard + "globals@npm:^11.1.0": version: 11.12.0 resolution: "globals@npm:11.12.0" @@ -12141,6 +13024,24 @@ __metadata: languageName: node linkType: hard +"graphql-request@npm:^7.1.2": + version: 7.4.0 + resolution: "graphql-request@npm:7.4.0" + dependencies: + "@graphql-typed-document-node/core": "npm:^3.2.0" + peerDependencies: + graphql: 14 - 16 + checksum: 10c0/066531d70b9c4656251e51c950ce9bee3df05291e12e2b983608d75ac3a70d700efd6488d273b043235dc125658973db38b21eba59e808863831a5a19a6a7b41 + languageName: node + linkType: hard + +"graphql@npm:^16.9.0": + version: 16.12.0 + resolution: "graphql@npm:16.12.0" + checksum: 10c0/b6fffa4e8a4e4a9933ebe85e7470b346dbf49050c1a482fac5e03e4a1a7bed2ecd3a4c97e29f04457af929464bc5e4f2aac991090c2f320111eef26e902a5c75 + languageName: node + linkType: hard + "h3@npm:^1.13.0": version: 1.13.1 resolution: "h3@npm:1.13.1" @@ -12390,6 +13291,13 @@ __metadata: languageName: node linkType: hard +"html-escaper@npm:^2.0.0": + version: 2.0.2 + resolution: "html-escaper@npm:2.0.2" + checksum: 10c0/208e8a12de1a6569edbb14544f4567e6ce8ecc30b9394fcaa4e7bb1e60c12a7c9a1ed27e31290817157e8626f3a4f29e76c8747030822eb84a6abb15c255f0a0 + languageName: node + linkType: hard + "http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" @@ -13001,6 +13909,34 @@ __metadata: languageName: node linkType: hard +"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.2": + version: 3.2.2 + resolution: "istanbul-lib-coverage@npm:3.2.2" + checksum: 10c0/6c7ff2106769e5f592ded1fb418f9f73b4411fd5a084387a5410538332b6567cd1763ff6b6cadca9b9eb2c443cce2f7ea7d7f1b8d315f9ce58539793b1e0922b + languageName: node + linkType: hard + +"istanbul-lib-report@npm:^3.0.0, istanbul-lib-report@npm:^3.0.1": + version: 3.0.1 + resolution: "istanbul-lib-report@npm:3.0.1" + dependencies: + istanbul-lib-coverage: "npm:^3.0.0" + make-dir: "npm:^4.0.0" + supports-color: "npm:^7.1.0" + checksum: 10c0/84323afb14392de8b6a5714bd7e9af845cfbd56cfe71ed276cda2f5f1201aea673c7111901227ee33e68e4364e288d73861eb2ed48f6679d1e69a43b6d9b3ba7 + languageName: node + linkType: hard + +"istanbul-reports@npm:^3.2.0": + version: 3.2.0 + resolution: "istanbul-reports@npm:3.2.0" + dependencies: + html-escaper: "npm:^2.0.0" + istanbul-lib-report: "npm:^3.0.0" + checksum: 10c0/d596317cfd9c22e1394f22a8d8ba0303d2074fe2e971887b32d870e4b33f8464b10f8ccbe6847808f7db485f084eba09e6c2ed706b3a978e4b52f07085b8f9bc + languageName: node + linkType: hard + "iterate-object@npm:^1.3.4": version: 1.3.5 resolution: "iterate-object@npm:1.3.5" @@ -13065,6 +14001,13 @@ __metadata: languageName: node linkType: hard +"js-tokens@npm:^10.0.0": + version: 10.0.0 + resolution: "js-tokens@npm:10.0.0" + checksum: 10c0/a93498747812ba3e0c8626f95f75ab29319f2a13613a0de9e610700405760931624433a0de59eb7c27ff8836e526768fb20783861b86ef89be96676f2c996b64 + languageName: node + linkType: hard + "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -13555,6 +14498,26 @@ __metadata: languageName: node linkType: hard +"magic-string@npm:^0.30.21": + version: 0.30.21 + resolution: "magic-string@npm:0.30.21" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.5.5" + checksum: 10c0/299378e38f9a270069fc62358522ddfb44e94244baa0d6a8980ab2a9b2490a1d03b236b447eee309e17eb3bddfa482c61259d47960eb018a904f0ded52780c4a + languageName: node + linkType: hard + +"magicast@npm:^0.5.1": + version: 0.5.2 + resolution: "magicast@npm:0.5.2" + dependencies: + "@babel/parser": "npm:^7.29.0" + "@babel/types": "npm:^7.29.0" + source-map-js: "npm:^1.2.1" + checksum: 10c0/924af677643c5a0a7d6cdb3247c0eb96fa7611b2ba6a5e720d35d81c503d3d9f5948eb5227f80f90f82ea3e7d38cffd10bb988f3fc09020db428e14f26e960d7 + languageName: node + linkType: hard + "make-dir@npm:^3.0.2": version: 3.1.0 resolution: "make-dir@npm:3.1.0" @@ -13564,6 +14527,15 @@ __metadata: languageName: node linkType: hard +"make-dir@npm:^4.0.0": + version: 4.0.0 + resolution: "make-dir@npm:4.0.0" + dependencies: + semver: "npm:^7.5.3" + checksum: 10c0/69b98a6c0b8e5c4fe9acb61608a9fbcfca1756d910f51e5dbe7a9e5cfb74fca9b8a0c8a0ffdf1294a740826c1ab4871d5bf3f62f72a3049e5eac6541ddffed68 + languageName: node + linkType: hard + "make-error@npm:^1.1.1": version: 1.3.6 resolution: "make-error@npm:1.3.6" @@ -13957,6 +14929,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:^3.3.11": + version: 3.3.11 + resolution: "nanoid@npm:3.3.11" + bin: + nanoid: bin/nanoid.cjs + checksum: 10c0/40e7f70b3d15f725ca072dfc4f74e81fcf1fbb02e491cf58ac0c79093adc9b0a73b152bcde57df4b79cd097e13023d7504acb38404a4da7bc1cd8e887b82fe0b + languageName: node + linkType: hard + "nanoid@npm:^3.3.8": version: 3.3.8 resolution: "nanoid@npm:3.3.8" @@ -14268,6 +15249,13 @@ __metadata: languageName: node linkType: hard +"obug@npm:^2.1.1": + version: 2.1.1 + resolution: "obug@npm:2.1.1" + checksum: 10c0/59dccd7de72a047e08f8649e94c1015ec72f94eefb6ddb57fb4812c4b425a813bc7e7cd30c9aca20db3c59abc3c85cc7a62bb656a968741d770f4e8e02bc2e78 + languageName: node + linkType: hard + "ofetch@npm:^1.4.1": version: 1.4.1 resolution: "ofetch@npm:1.4.1" @@ -14551,6 +15539,13 @@ __metadata: languageName: node linkType: hard +"pathe@npm:^2.0.3": + version: 2.0.3 + resolution: "pathe@npm:2.0.3" + checksum: 10c0/c118dc5a8b5c4166011b2b70608762e260085180bb9e33e80a50dcdb1e78c010b1624f4280c492c92b05fc276715a4c357d1f9edc570f8f1b3d90b6839ebaca1 + languageName: node + linkType: hard + "pathval@npm:^1.1.1": version: 1.1.1 resolution: "pathval@npm:1.1.1" @@ -14592,6 +15587,13 @@ __metadata: languageName: node linkType: hard +"picomatch@npm:^4.0.3": + version: 4.0.3 + resolution: "picomatch@npm:4.0.3" + checksum: 10c0/9582c951e95eebee5434f59e426cddd228a7b97a0161a375aed4be244bd3fe8e3a31b846808ea14ef2c8a2527a6eeab7b3946a67d5979e81694654f939473ae2 + languageName: node + linkType: hard + "pify@npm:^2.3.0": version: 2.3.0 resolution: "pify@npm:2.3.0" @@ -14822,6 +15824,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.5.6": + version: 8.5.6 + resolution: "postcss@npm:8.5.6" + dependencies: + nanoid: "npm:^3.3.11" + picocolors: "npm:^1.1.1" + source-map-js: "npm:^1.2.1" + checksum: 10c0/5127cc7c91ed7a133a1b7318012d8bfa112da9ef092dddf369ae699a1f10ebbd89b1b9f25f3228795b84585c72aabd5ced5fc11f2ba467eedf7b081a66fad024 + languageName: node + linkType: hard + "preact@npm:^10.16.0, preact@npm:^10.24.2": version: 10.25.4 resolution: "preact@npm:10.25.4" @@ -15766,6 +16779,96 @@ __metadata: languageName: node linkType: hard +"rollup@npm:^4.43.0": + version: 4.57.1 + resolution: "rollup@npm:4.57.1" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.57.1" + "@rollup/rollup-android-arm64": "npm:4.57.1" + "@rollup/rollup-darwin-arm64": "npm:4.57.1" + "@rollup/rollup-darwin-x64": "npm:4.57.1" + "@rollup/rollup-freebsd-arm64": "npm:4.57.1" + "@rollup/rollup-freebsd-x64": "npm:4.57.1" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.57.1" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.57.1" + "@rollup/rollup-linux-arm64-gnu": "npm:4.57.1" + "@rollup/rollup-linux-arm64-musl": "npm:4.57.1" + "@rollup/rollup-linux-loong64-gnu": "npm:4.57.1" + "@rollup/rollup-linux-loong64-musl": "npm:4.57.1" + "@rollup/rollup-linux-ppc64-gnu": "npm:4.57.1" + "@rollup/rollup-linux-ppc64-musl": "npm:4.57.1" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.57.1" + "@rollup/rollup-linux-riscv64-musl": "npm:4.57.1" + "@rollup/rollup-linux-s390x-gnu": "npm:4.57.1" + "@rollup/rollup-linux-x64-gnu": "npm:4.57.1" + "@rollup/rollup-linux-x64-musl": "npm:4.57.1" + "@rollup/rollup-openbsd-x64": "npm:4.57.1" + "@rollup/rollup-openharmony-arm64": "npm:4.57.1" + "@rollup/rollup-win32-arm64-msvc": "npm:4.57.1" + "@rollup/rollup-win32-ia32-msvc": "npm:4.57.1" + "@rollup/rollup-win32-x64-gnu": "npm:4.57.1" + "@rollup/rollup-win32-x64-msvc": "npm:4.57.1" + "@types/estree": "npm:1.0.8" + fsevents: "npm:~2.3.2" + dependenciesMeta: + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-freebsd-arm64": + optional: true + "@rollup/rollup-freebsd-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm-musleabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-loong64-gnu": + optional: true + "@rollup/rollup-linux-loong64-musl": + optional: true + "@rollup/rollup-linux-ppc64-gnu": + optional: true + "@rollup/rollup-linux-ppc64-musl": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-riscv64-musl": + optional: true + "@rollup/rollup-linux-s390x-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-openbsd-x64": + optional: true + "@rollup/rollup-openharmony-arm64": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-gnu": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 10c0/a90aaf1166fc495920e44e52dced0b12283aaceb0924abd6f863102128dd428bbcbf85970f792c06bc63d2a2168e7f073b73e05f6f8d76fdae17b7ac6cacba06 + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -15942,6 +17045,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.5.3": + version: 7.7.4 + resolution: "semver@npm:7.7.4" + bin: + semver: bin/semver.js + checksum: 10c0/5215ad0234e2845d4ea5bb9d836d42b03499546ddafb12075566899fc617f68794bb6f146076b6881d755de17d6c6cc73372555879ec7dce2c2feee947866ad2 + languageName: node + linkType: hard + "serialize-javascript@npm:^6.0.2": version: 6.0.2 resolution: "serialize-javascript@npm:6.0.2" @@ -16125,6 +17237,13 @@ __metadata: languageName: node linkType: hard +"siginfo@npm:^2.0.0": + version: 2.0.0 + resolution: "siginfo@npm:2.0.0" + checksum: 10c0/3def8f8e516fbb34cb6ae415b07ccc5d9c018d85b4b8611e3dc6f8be6d1899f693a4382913c9ed51a06babb5201639d76453ab297d1c54a456544acf5c892e34 + languageName: node + linkType: hard + "signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" @@ -16382,6 +17501,13 @@ __metadata: languageName: node linkType: hard +"stackback@npm:0.0.2": + version: 0.0.2 + resolution: "stackback@npm:0.0.2" + checksum: 10c0/89a1416668f950236dd5ac9f9a6b2588e1b9b62b1b6ad8dff1bfc5d1a15dbf0aafc9b52d2226d00c28dffff212da464eaeebfc6b7578b9d180cef3e3782c5983 + languageName: node + linkType: hard + "stacktrace-parser@npm:^0.1.10": version: 0.1.10 resolution: "stacktrace-parser@npm:0.1.10" @@ -16398,6 +17524,13 @@ __metadata: languageName: node linkType: hard +"std-env@npm:^3.10.0": + version: 3.10.0 + resolution: "std-env@npm:3.10.0" + checksum: 10c0/1814927a45004d36dde6707eaf17552a546769bc79a6421be2c16ce77d238158dfe5de30910b78ec30d95135cc1c59ea73ee22d2ca170f8b9753f84da34c427f + languageName: node + linkType: hard + "stream-shift@npm:^1.0.2": version: 1.0.3 resolution: "stream-shift@npm:1.0.3" @@ -16869,6 +18002,13 @@ __metadata: languageName: node linkType: hard +"tinybench@npm:^2.9.0": + version: 2.9.0 + resolution: "tinybench@npm:2.9.0" + checksum: 10c0/c3500b0f60d2eb8db65250afe750b66d51623057ee88720b7f064894a6cb7eb93360ca824a60a31ab16dab30c7b1f06efe0795b352e37914a9d4bad86386a20c + languageName: node + linkType: hard + "tinyexec@npm:^0.3.2": version: 0.3.2 resolution: "tinyexec@npm:0.3.2" @@ -16876,6 +18016,13 @@ __metadata: languageName: node linkType: hard +"tinyexec@npm:^1.0.2": + version: 1.0.2 + resolution: "tinyexec@npm:1.0.2" + checksum: 10c0/1261a8e34c9b539a9aae3b7f0bb5372045ff28ee1eba035a2a059e532198fe1a182ec61ac60fa0b4a4129f0c4c4b1d2d57355b5cb9aa2d17ac9454ecace502ee + languageName: node + linkType: hard + "tinyglobby@npm:^0.2.11": version: 0.2.12 resolution: "tinyglobby@npm:0.2.12" @@ -16896,6 +18043,16 @@ __metadata: languageName: node linkType: hard +"tinyglobby@npm:^0.2.15": + version: 0.2.15 + resolution: "tinyglobby@npm:0.2.15" + dependencies: + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.3" + checksum: 10c0/869c31490d0d88eedb8305d178d4c75e7463e820df5a9b9d388291daf93e8b1eb5de1dad1c1e139767e4269fe75f3b10d5009b2cc14db96ff98986920a186844 + languageName: node + linkType: hard + "tinyglobby@npm:^0.2.6": version: 0.2.10 resolution: "tinyglobby@npm:0.2.10" @@ -16906,6 +18063,13 @@ __metadata: languageName: node linkType: hard +"tinyrainbow@npm:^3.0.3": + version: 3.0.3 + resolution: "tinyrainbow@npm:3.0.3" + checksum: 10c0/1e799d35cd23cabe02e22550985a3051dc88814a979be02dc632a159c393a998628eacfc558e4c746b3006606d54b00bcdea0c39301133956d10a27aa27e988c + languageName: node + linkType: hard + "tmp@npm:0.0.33": version: 0.0.33 resolution: "tmp@npm:0.0.33" @@ -16956,6 +18120,15 @@ __metadata: languageName: node linkType: hard +"ts-api-utils@npm:^1.3.0": + version: 1.4.3 + resolution: "ts-api-utils@npm:1.4.3" + peerDependencies: + typescript: ">=4.2.0" + checksum: 10c0/e65dc6e7e8141140c23e1dc94984bf995d4f6801919c71d6dc27cf0cd51b100a91ffcfe5217626193e5bea9d46831e8586febdc7e172df3f1091a7384299e23a + languageName: node + linkType: hard + "ts-api-utils@npm:^2.0.1": version: 2.0.1 resolution: "ts-api-utils@npm:2.0.1" @@ -17333,6 +18506,20 @@ __metadata: languageName: node linkType: hard +"typescript-eslint@npm:8.0.0": + version: 8.0.0 + resolution: "typescript-eslint@npm:8.0.0" + dependencies: + "@typescript-eslint/eslint-plugin": "npm:8.0.0" + "@typescript-eslint/parser": "npm:8.0.0" + "@typescript-eslint/utils": "npm:8.0.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/138ba4767e16bcb1bde3e6becbe92a548091d27c5584cf60d5c78d599085e06172791ab297447b9245f5387c9777b76683c2afd0e0234ed20d67a1de1192a7c9 + languageName: node + linkType: hard + "typescript-eslint@npm:^8.15.0, typescript-eslint@npm:^8.18.2": version: 8.26.0 resolution: "typescript-eslint@npm:8.26.0" @@ -17517,6 +18704,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~7.16.0": + version: 7.16.0 + resolution: "undici-types@npm:7.16.0" + checksum: 10c0/3033e2f2b5c9f1504bdc5934646cb54e37ecaca0f9249c983f7b1fc2e87c6d18399ebb05dc7fd5419e02b2e915f734d872a65da2e3eeed1813951c427d33cc9a + languageName: node + linkType: hard + "undici@npm:^5.14.0": version: 5.28.4 resolution: "undici@npm:5.28.4" @@ -17942,6 +19136,120 @@ __metadata: languageName: node linkType: hard +"vite@npm:^6.0.0 || ^7.0.0": + version: 7.3.1 + resolution: "vite@npm:7.3.1" + dependencies: + esbuild: "npm:^0.27.0" + fdir: "npm:^6.5.0" + fsevents: "npm:~2.3.3" + picomatch: "npm:^4.0.3" + postcss: "npm:^8.5.6" + rollup: "npm:^4.43.0" + tinyglobby: "npm:^0.2.15" + 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 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/5c7548f5f43a23533e53324304db4ad85f1896b1bfd3ee32ae9b866bac2933782c77b350eb2b52a02c625c8ad1ddd4c000df077419410650c982cd97fde8d014 + languageName: node + linkType: hard + +"vitest@npm:^4.0.18": + version: 4.0.18 + resolution: "vitest@npm:4.0.18" + dependencies: + "@vitest/expect": "npm:4.0.18" + "@vitest/mocker": "npm:4.0.18" + "@vitest/pretty-format": "npm:4.0.18" + "@vitest/runner": "npm:4.0.18" + "@vitest/snapshot": "npm:4.0.18" + "@vitest/spy": "npm:4.0.18" + "@vitest/utils": "npm:4.0.18" + es-module-lexer: "npm:^1.7.0" + expect-type: "npm:^1.2.2" + magic-string: "npm:^0.30.21" + obug: "npm:^2.1.1" + pathe: "npm:^2.0.3" + picomatch: "npm:^4.0.3" + std-env: "npm:^3.10.0" + tinybench: "npm:^2.9.0" + tinyexec: "npm:^1.0.2" + tinyglobby: "npm:^0.2.15" + tinyrainbow: "npm:^3.0.3" + vite: "npm:^6.0.0 || ^7.0.0" + why-is-node-running: "npm:^2.3.0" + peerDependencies: + "@edge-runtime/vm": "*" + "@opentelemetry/api": ^1.9.0 + "@types/node": ^20.0.0 || ^22.0.0 || >=24.0.0 + "@vitest/browser-playwright": 4.0.18 + "@vitest/browser-preview": 4.0.18 + "@vitest/browser-webdriverio": 4.0.18 + "@vitest/ui": 4.0.18 + happy-dom: "*" + jsdom: "*" + peerDependenciesMeta: + "@edge-runtime/vm": + optional: true + "@opentelemetry/api": + optional: true + "@types/node": + optional: true + "@vitest/browser-playwright": + optional: true + "@vitest/browser-preview": + optional: true + "@vitest/browser-webdriverio": + optional: true + "@vitest/ui": + optional: true + happy-dom: + optional: true + jsdom: + optional: true + bin: + vitest: vitest.mjs + checksum: 10c0/b913cd32032c95f29ff08c931f4b4c6fd6d2da498908d6770952c561a1b8d75c62499a1f04cadf82fb89cc0f9a33f29fb5dfdb899f6dbb27686a9d91571be5fa + languageName: node + linkType: hard + "w-json@npm:1.3.10": version: 1.3.10 resolution: "w-json@npm:1.3.10" @@ -18140,6 +19448,18 @@ __metadata: languageName: node linkType: hard +"why-is-node-running@npm:^2.3.0": + version: 2.3.0 + resolution: "why-is-node-running@npm:2.3.0" + dependencies: + siginfo: "npm:^2.0.0" + stackback: "npm:0.0.2" + bin: + why-is-node-running: cli.js + checksum: 10c0/1cde0b01b827d2cf4cb11db962f3958b9175d5d9e7ac7361d1a7b0e2dc6069a263e69118bd974c4f6d0a890ef4eedfe34cf3d5167ec14203dbc9a18620537054 + languageName: node + linkType: hard + "widest-line@npm:^3.1.0": version: 3.1.0 resolution: "widest-line@npm:3.1.0"