{"_id":"@1001-digital/dapp-query-core","_rev":"3-89905df97aa5f8c392f29fc2211be230","name":"@1001-digital/dapp-query-core","dist-tags":{"latest":"1.1.0"},"versions":{"0.2.0":{"name":"@1001-digital/dapp-query-core","version":"0.2.0","license":"MIT","_id":"@1001-digital/dapp-query-core@0.2.0","maintainers":[{"name":"jwahdatehagh","email":"jalil@1001.digital"}],"dist":{"shasum":"f7e4a57b6844f580369ccb48c82709b02a1f8763","tarball":"https://registry.npmjs.org/@1001-digital/dapp-query-core/-/dapp-query-core-0.2.0.tgz","fileCount":12,"integrity":"sha512-iMgUZqXBXioD+6P7i4c1ziMJ+i+Dl7wNYo4bL8SojfKYaU7BLdI48wnNrUwlbeDQDpypo3iqAEk6VTGzJZJYFw==","signatures":[{"sig":"MEUCIQDDNtti1yPtImWjhu+0tq7tVjYn0bhwgPl/AxOFu9jMyAIgOf+ynbT+oINeq5UxuldJdaY2RSLiAEBoY8Hvw+9FLYA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":21075},"type":"module","_from":"file:1001-digital-dapp-query-core-0.2.0.tgz","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","development":"./src/index.ts"}},"scripts":{"test":"vitest run","build":"vite build","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"jwahdatehagh","email":"jalil@1001.digital"},"_resolved":"/tmp/9b3d08a2f0126d019c7d2094089b2a01/1001-digital-dapp-query-core-0.2.0.tgz","_integrity":"sha512-iMgUZqXBXioD+6P7i4c1ziMJ+i+Dl7wNYo4bL8SojfKYaU7BLdI48wnNrUwlbeDQDpypo3iqAEk6VTGzJZJYFw==","_npmVersion":"11.9.0","description":"Resilient on-chain data queries with multi-source fallback and local caching.","directories":{},"_nodeVersion":"24.14.0","_hasShrinkwrap":false,"devDependencies":{"viem":"^2.23.0","vite":"^6.0.0","vitest":"^3.0.0","typescript":"^5.7.0","fake-indexeddb":"^6.0.0","vite-plugin-dts":"^4.0.0"},"peerDependencies":{"viem":">=2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/dapp-query-core_0.2.0_1773827124689_0.028530214724305614","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@1001-digital/dapp-query-core","version":"1.0.0","license":"MIT","_id":"@1001-digital/dapp-query-core@1.0.0","maintainers":[{"name":"jwahdatehagh","email":"jalil@1001.digital"}],"dist":{"shasum":"242bae166f2a822eddb428aca938e8085138502e","tarball":"https://registry.npmjs.org/@1001-digital/dapp-query-core/-/dapp-query-core-1.0.0.tgz","fileCount":13,"integrity":"sha512-Do22LBOBEiJWeOeW+fpRkkTNIpojNstnF6BB1sSqGuAIPpC7JwykDxEWG52RB4/RXqwyh8dD/8qFegDkDkz/rw==","signatures":[{"sig":"MEQCICHyLk1ILyH275ANHfTy0VHckos2Ae98Pnm2BrXD1JyyAiA37F3gXR2BsSRB0SvN9Fz+lylPXmFwlcSLl1cwYMYX1Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":26578},"type":"module","_from":"file:1001-digital-dapp-query-core-1.0.0.tgz","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","development":"./src/index.ts"}},"scripts":{"test":"vitest run","build":"vite build","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"jwahdatehagh","email":"jalil@1001.digital"},"_resolved":"/tmp/addf6122304db0855dea819a4ce9bb71/1001-digital-dapp-query-core-1.0.0.tgz","_integrity":"sha512-Do22LBOBEiJWeOeW+fpRkkTNIpojNstnF6BB1sSqGuAIPpC7JwykDxEWG52RB4/RXqwyh8dD/8qFegDkDkz/rw==","_npmVersion":"11.9.0","description":"Resilient on-chain data queries with multi-source fallback and local caching.","directories":{},"_nodeVersion":"24.14.0","_hasShrinkwrap":false,"devDependencies":{"viem":"^2.23.0","vite":"^6.0.0","vitest":"^3.0.0","typescript":"^5.7.0","fake-indexeddb":"^6.0.0","vite-plugin-dts":"^4.0.0"},"peerDependencies":{"viem":">=2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/dapp-query-core_1.0.0_1773833545330_0.0229667835942704","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@1001-digital/dapp-query-core","version":"1.1.0","description":"Resilient on-chain data queries with multi-source fallback and local caching.","type":"module","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"peerDependencies":{"viem":">=2.0.0"},"devDependencies":{"viem":"^2.23.0","typescript":"^5.7.0","vite":"^6.0.0","vite-plugin-dts":"^4.0.0","vitest":"^3.0.0","fake-indexeddb":"^6.0.0"},"license":"MIT","scripts":{"build":"vite build","typecheck":"tsc --noEmit","test":"vitest run","test:watch":"vitest"},"_id":"@1001-digital/dapp-query-core@1.1.0","_integrity":"sha512-NIQk+WU3qaVO86tJfw4TmtWp+hp4BLnjZZXpASkLT4u5k0idQZglvHlb0T07AkL2a7OgHQoEjFZKN1HiyNSrng==","_resolved":"/tmp/c723a2fb5ccad6d2ee70fb80bf784131/1001-digital-dapp-query-core-1.1.0.tgz","_from":"file:1001-digital-dapp-query-core-1.1.0.tgz","_nodeVersion":"24.14.0","_npmVersion":"11.9.0","dist":{"integrity":"sha512-NIQk+WU3qaVO86tJfw4TmtWp+hp4BLnjZZXpASkLT4u5k0idQZglvHlb0T07AkL2a7OgHQoEjFZKN1HiyNSrng==","shasum":"d0f85b76335d48cf965d4fce3e0076ea0307f6c5","tarball":"https://registry.npmjs.org/@1001-digital/dapp-query-core/-/dapp-query-core-1.1.0.tgz","fileCount":13,"unpackedSize":26565,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBqRJv/t6+Ld7AOujOGnxhFSWhKSDGiW+rcDkJ/dhY7YAiEA9y6MbIlrwqPp1Z+kPp2vLR6+z0RDrDEFJObxEPluQlQ="}]},"_npmUser":{"name":"jwahdatehagh","email":"jalil@1001.digital"},"directories":{},"maintainers":[{"name":"jwahdatehagh","email":"jalil@1001.digital"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dapp-query-core_1.1.0_1773834331626_0.785989392979972"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-18T09:45:24.635Z","modified":"2026-03-18T11:45:31.912Z","0.2.0":"2026-03-18T09:45:24.823Z","1.0.0":"2026-03-18T11:32:25.468Z","1.1.0":"2026-03-18T11:45:31.755Z"},"license":"MIT","description":"Resilient on-chain data queries with multi-source fallback and local caching.","maintainers":[{"name":"jwahdatehagh","email":"jalil@1001.digital"}],"readme":"# @1001-digital/dapp-query-core\n\nResilient on-chain data queries with multi-source fallback and local caching.\n\n```\nnpm install @1001-digital/dapp-query-core\n```\n\n## What it does\n\nDefine data sources (RPC nodes, GraphQL indexers, REST APIs), and the query client handles resolution order, automatic fallback, request deduplication, caching, and live updates.\n\n## Quick Start\n\n```ts\nimport { createQueryClient, graphqlSource, rpcSource, idbCache } from '@1001-digital/dapp-query-core'\nimport { createPublicClient, http, parseAbiItem } from 'viem'\nimport { mainnet } from 'viem/chains'\n\nconst client = createQueryClient({\n  cache: idbCache('my-app'),\n})\n\nconst indexed = graphqlSource({\n  endpoints: ['https://indexer.example.com'],\n  query: `query($address: String!) { transfers(where: { from: $address }) { items { to value block } } }`,\n  variables: (address) => ({ address }),\n  transform: (data) => data.transfers.items,\n})\n\nconst viemClient = createPublicClient({ chain: mainnet, transport: http() })\n\nconst onchain = rpcSource({\n  client: viemClient,\n  event: parseAbiItem('event Transfer(address indexed from, address indexed to, uint256 value)'),\n  address: '0x...',\n  fromBlock: 18_000_000n,\n  transform: (logs) => logs.map((l) => ({ to: l.args.to, value: l.args.value, block: l.blockNumber })),\n})\n\nconst transfersQuery = {\n  key: (address: string) => `transfers:${address}`,\n  sources: [indexed, onchain],\n  staleTime: 5 * 60_000,\n}\n\nconst transfers = await client.fetch(transfersQuery, '0xabc...')\n```\n\n## Sources\n\nA source wraps a data backend. Each source transforms its raw response into a shared domain type.\n\n### `rpcSource`\n\nFetches event logs from an RPC node. Automatically chunks large block ranges to stay within provider limits.\n\n```ts\nrpcSource({\n  client: viemClient,\n  event: parseAbiItem('event Transfer(address indexed from, address indexed to, uint256 value)'),\n  address: '0x...',\n  fromBlock: 18_000_000n,\n  maxBlockRange: 2000,    // blocks per getLogs call (default: 2000)\n  filter: (address) => ({ from: address }),\n  transform: (logs) => logs.map(parseTransfer),\n})\n```\n\nSupports live updates via block polling (12s interval).\n\n### `graphqlSource`\n\nQueries a GraphQL indexer (Ponder, The Graph, etc.) with multi-endpoint failover.\n\n```ts\ngraphqlSource({\n  endpoints: [primaryIndexer, backupIndexer],\n  query: TRANSFERS_QUERY,\n  variables: (address) => ({ address }),\n  transform: (data) => data.transfers.items,\n})\n```\n\n### `httpSource`\n\nQueries a REST API. Supports live updates via Server-Sent Events.\n\n```ts\nhttpSource({\n  url: 'https://api.example.com/transfers',\n  request: (address) => ({ params: { address: address as string } }),\n  transform: (data) => data.map(parseTransfer),\n  sseUrl: 'https://api.example.com/transfers/stream',\n})\n```\n\n### `customSource`\n\nWraps any async function.\n\n```ts\ncustomSource({\n  id: 'my-source',\n  fetch: async (address) => {\n    const res = await fetch(`/api/transfers/${address}`)\n    return res.json()\n  },\n})\n```\n\n## Query Client\n\n```ts\nconst client = createQueryClient({\n  cache: idbCache('my-app'),   // or memoryCache(500)\n  defaultStaleTime: 5 * 60_000,\n  defaultStaleWhileRevalidate: true,\n})\n```\n\n### `client.fetch(query, ...args)`\n\nOne-shot fetch. Returns cached data if fresh, otherwise fetches from sources. Concurrent requests for the same cache key are deduplicated.\n\n### `client.subscribe(query, args, callback)`\n\nReactive subscription. Returns cached data immediately (if available), revalidates in the background, and re-fetches when live watchers fire. Returns an unsubscribe function.\n\n### `client.invalidate(query, ...args)`\n\nClears the cache entry and triggers revalidation for active subscribers.\n\n### `client.waitForChange(query, args, predicate, options?)`\n\nPolls sources until a predicate is satisfied or max attempts are exhausted. Useful for waiting until on-chain state reflects a recent transaction.\n\n```ts\nconst updated = await client.waitForChange(\n  transfersQuery,\n  ['0xabc...'],\n  (current, previous) => current.length > (previous?.length ?? 0),\n  { interval: 3000, maxAttempts: 10 },\n)\n```\n\n### `client.getSourceHealth(sourceId)`\n\nReturns latency and failure data for a source.\n\n```ts\nclient.getSourceHealth('graphql:https://indexer.example.com')\n// { failures: 0, lastFailure: 0, avgLatency: 120, samples: 15 }\n```\n\n### `client.reset()`\n\nClears all caches, resets health tracking, and tears down active subscriptions.\n\n## Query Definitions\n\nA query definition is a plain object:\n\n```ts\nconst transfersQuery = {\n  key: (address: string) => `transfers:${address}`,\n  sources: [indexed, onchain],\n  strategy: 'fallback' as const, // 'fallback' | 'race'\n  staleTime: 5 * 60_000,\n  staleWhileRevalidate: true,\n  transform: (transfers) => transfers.sort((a, b) => Number(b.block - a.block)),\n}\n```\n\n## Strategies\n\n**`fallback`** (default) — Try sources in order. Sources with 3+ consecutive failures are temporarily skipped (30s backoff).\n\n**`race`** — Fire all sources concurrently, use the first successful result.\n\n## Caching\n\n**`memoryCache(maxSize?)`** — In-memory LRU cache. Default: 500 entries.\n\n**`idbCache(dbName?)`** — IndexedDB-backed persistent cache. Handles BigInt serialization automatically.\n\nBoth implement the `Cache` interface:\n\n```ts\ninterface Cache {\n  get<T>(key: string): Promise<CacheEntry<T> | undefined>\n  set<T>(key: string, entry: CacheEntry<T>): Promise<void>\n  delete(key: string): Promise<void>\n  clear(): Promise<void>\n}\n```\n\n## Peer Dependencies\n\n- `viem` >= 2.0.0\n\n## License\n\nMIT\n","readmeFilename":"README.md"}