{"_id":"@doeixd/create-infinite-resource-solid","_rev":"4-c8c81a2a712d3117e357c971a1a18bf1","name":"@doeixd/create-infinite-resource-solid","dist-tags":{"latest":"0.1.1"},"versions":{"0.0.2":{"name":"@doeixd/create-infinite-resource-solid","version":"0.0.2","keywords":["pridepack","solid-js","solidjs","create-infinite-resource","createResource","infinite"],"author":{"name":"Patrick Glenn"},"license":"MIT","_id":"@doeixd/create-infinite-resource-solid@0.0.2","maintainers":[{"name":"doeixd","email":"doeixd@gmail.com"}],"homepage":"https://github.com/doeixd/create-infinite-resource-solid#readme","bugs":{"url":"https://github.com/doeixd/create-infinite-resource-solid/issues"},"dist":{"shasum":"56343484091541330c97eb5e413d32cee8717561","tarball":"https://registry.npmjs.org/@doeixd/create-infinite-resource-solid/-/create-infinite-resource-solid-0.0.2.tgz","fileCount":13,"integrity":"sha512-wcRnTI8uPO+vmDjhfcmV+R7yEGvZq3fgEoKkz5fvxhXF7L1nLVQF3yNHgqcx41dR5eQhYfUopT2d1zn4OwV9cw==","signatures":[{"sig":"MEQCIHs3XpQzYjo36ZobmuMoonZbEGkfri5LUfUtWjX3OWLSAiA5llfD2C/h62faYHh12a+2oIVlf9PRAC6Otms/Ne1d/Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":45683},"main":"./dist/cjs/production/index.jsx","types":"./dist/types/index.d.ts","module":"./dist/esm/production/index.jsx","engines":{"node":">=16"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/production/index.jsx","require":"./dist/cjs/production/index.jsx","development":{"import":"./dist/esm/development/index.jsx","require":"./dist/cjs/development/index.jsx"}}},"gitHead":"2e7068cc7ea9ec6e936439bdf6d339ddf96f5533","private":false,"scripts":{"dev":"pridepack dev","build":"pridepack build","clean":"pridepack clean","start":"pridepack start","watch":"pridepack watch","release":"standard-version && git push --follow-tags origin main","type-check":"pridepack check","release:major":"npm version major && git push --follow-tags origin main","release:minor":"npm version minor && git push --follow-tags origin main","release:patch":"npm version patch && git push --follow-tags origin main","prepublishOnly":"pridepack clean && pridepack build"},"_npmUser":{"name":"doeixd","email":"doeixd@gmail.com"},"repository":{"url":"git+https://github.com/doeixd/create-infinite-resource-solid.git","type":"git"},"_npmVersion":"8.19.4","description":"A createInfiniteResource helper for solid js","directories":{},"_nodeVersion":"16.20.2","dependencies":{"@solid-primitives/utils":"^6.3.0"},"publishConfig":{"access":"public"},"typesVersions":{"*":{}},"_hasShrinkwrap":false,"devDependencies":{"tslib":"^2.8.1","vitest":"3.0.4","solid-js":"^1.9.4","pridepack":"2.6.4","typescript":"^5.7.2","@types/node":"^22.10.2","standard-version":"9.5.0"},"peerDependencies":{"solid-js":"^1.9"},"_npmOperationalInternal":{"tmp":"tmp/create-infinite-resource-solid_0.0.2_1738075991736_0.3354515398857638","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"@doeixd/create-infinite-resource-solid","version":"0.0.3","keywords":["pridepack","solid-js","solidjs","create-infinite-resource","createResource","infinite"],"author":{"name":"Patrick Glenn"},"license":"MIT","_id":"@doeixd/create-infinite-resource-solid@0.0.3","maintainers":[{"name":"doeixd","email":"doeixd@gmail.com"}],"homepage":"https://github.com/doeixd/create-infinite-resource-solid#readme","bugs":{"url":"https://github.com/doeixd/create-infinite-resource-solid/issues"},"dist":{"shasum":"35731b07b998abd30e364e0dfad299fa919d0506","tarball":"https://registry.npmjs.org/@doeixd/create-infinite-resource-solid/-/create-infinite-resource-solid-0.0.3.tgz","fileCount":12,"integrity":"sha512-n0QPwQYe7FYYtplwEr2X+PTpWaiaCtifgHF1TvBm/WctNHP6ic4HxDfgYFf5Q/fJqXBrUhvFIRarfV6zmdpvQA==","signatures":[{"sig":"MEYCIQCT8TnLGZqSWWCcmeKk2UWzWVwCudAPpWGhvVFulqY2NAIhAOIOAI0OBr4CfunVnqaHo6ZcnBirqY2OaW1rVLY5A1AE","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":48861},"main":"./dist/cjs/production/index.jsx","types":"./dist/types/index.d.ts","module":"./dist/esm/production/index.jsx","engines":{"node":">=16"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/production/index.jsx","require":"./dist/cjs/production/index.jsx","development":{"import":"./dist/esm/development/index.jsx","require":"./dist/cjs/development/index.jsx"}}},"gitHead":"ecc9950e0e4fd3e2797d585a697f3e515ceb49f1","private":false,"scripts":{"dev":"pridepack dev","build":"pridepack build","clean":"pridepack clean","start":"pridepack start","watch":"pridepack watch","release":"standard-version && git push --follow-tags origin main","type-check":"pridepack check","release:major":"npm version major && git push --follow-tags origin main","release:minor":"npm version minor && git push --follow-tags origin main","release:patch":"npm version patch && git push --follow-tags origin main","prepublishOnly":"pridepack clean && pridepack build"},"_npmUser":{"name":"doeixd","email":"doeixd@gmail.com"},"repository":{"url":"git+https://github.com/doeixd/create-infinite-resource-solid.git","type":"git"},"_npmVersion":"8.19.4","description":"A createInfiniteResource helper for solid js","directories":{},"_nodeVersion":"16.20.2","dependencies":{"@solid-primitives/utils":"^6.3.0"},"publishConfig":{"access":"public"},"typesVersions":{"*":{}},"_hasShrinkwrap":false,"devDependencies":{"tslib":"^2.8.1","vitest":"3.0.4","solid-js":"^1.9.4","pridepack":"2.6.4","typescript":"^5.7.2","@types/node":"^22.10.2","standard-version":"9.5.0"},"peerDependencies":{"solid-js":"^1.9"},"_npmOperationalInternal":{"tmp":"tmp/create-infinite-resource-solid_0.0.3_1738078018551_0.03335637704264394","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@doeixd/create-infinite-resource-solid","version":"0.1.0","keywords":["pridepack","solid-js","solidjs","create-infinite-resource","createResource","infinite"],"author":{"name":"Patrick Glenn"},"license":"MIT","_id":"@doeixd/create-infinite-resource-solid@0.1.0","maintainers":[{"name":"doeixd","email":"doeixd@gmail.com"}],"homepage":"https://github.com/doeixd/create-infinite-resource-solid#readme","bugs":{"url":"https://github.com/doeixd/create-infinite-resource-solid/issues"},"dist":{"shasum":"96d533ab3878f7dcbaab0f6c1b903307edbed43c","tarball":"https://registry.npmjs.org/@doeixd/create-infinite-resource-solid/-/create-infinite-resource-solid-0.1.0.tgz","fileCount":12,"integrity":"sha512-WaazxtQRX/3JKWhIJQfUj3PMv76wWRKvdZZvT/7j7W3hEQPYtRjH9Lk/Q8LEdgBiNRBMe515BBx9xtXjB8q7hA==","signatures":[{"sig":"MEUCIEKT8heZTdQWXbVE2pTp8Q+DSA0CQHGMJT9rlFJSDCecAiEA0ChHBLG8ewK8FY5R3AXJAXYZZCAGmZgrCF4riuRgYi4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":70209},"main":"./dist/cjs/production/index.jsx","types":"./dist/types/index.d.ts","module":"./dist/esm/production/index.jsx","engines":{"node":">=16"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/production/index.jsx","require":"./dist/cjs/production/index.jsx","development":{"import":"./dist/esm/development/index.jsx","require":"./dist/cjs/development/index.jsx"}}},"gitHead":"60590417ec22036c9b3817d988989e4c9bf987a4","private":false,"scripts":{"dev":"pridepack dev","build":"pridepack build","clean":"pridepack clean","start":"pridepack start","watch":"pridepack watch","release":"standard-version && git push --follow-tags origin main","type-check":"pridepack check","release:major":"npm version major && git push --follow-tags origin main","release:minor":"npm version minor && git push --follow-tags origin main","release:patch":"npm version patch && git push --follow-tags origin main","prepublishOnly":"pridepack clean && pridepack build"},"_npmUser":{"name":"doeixd","email":"doeixd@gmail.com"},"repository":{"url":"git+https://github.com/doeixd/create-infinite-resource-solid.git","type":"git"},"_npmVersion":"8.19.4","description":"A createInfiniteResource helper for solid js","directories":{},"_nodeVersion":"16.20.2","dependencies":{"@solid-primitives/utils":"^6.3.0"},"publishConfig":{"access":"public"},"typesVersions":{"*":{}},"_hasShrinkwrap":false,"devDependencies":{"tslib":"^2.8.1","vitest":"3.0.4","solid-js":"^1.9.4","pridepack":"2.6.4","typescript":"^5.7.2","@types/node":"^22.10.2","standard-version":"9.5.0"},"peerDependencies":{"solid-js":"^1.9"},"_npmOperationalInternal":{"tmp":"tmp/create-infinite-resource-solid_0.1.0_1766168979090_0.8909695631305778","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@doeixd/create-infinite-resource-solid","version":"0.1.1","engines":{"node":">=16"},"license":"MIT","keywords":["pridepack","solid-js","solidjs","create-infinite-resource","createResource","infinite"],"devDependencies":{"@types/node":"^22.10.2","pridepack":"2.6.4","solid-js":"^1.9.4","standard-version":"9.5.0","tslib":"^2.8.1","typescript":"^5.7.2","vitest":"3.0.4"},"peerDependencies":{"solid-js":"^1.9"},"scripts":{"release":"standard-version && git push --follow-tags origin main","release:patch":"npm version patch && git push --follow-tags origin main","release:minor":"npm version minor && git push --follow-tags origin main","release:major":"npm version major && git push --follow-tags origin main","prepublishOnly":"pridepack clean && pridepack build","build":"pridepack build","type-check":"pridepack check","clean":"pridepack clean","watch":"pridepack watch","start":"pridepack start","dev":"pridepack dev"},"private":false,"description":"A createInfiniteResource helper for solid js","repository":{"url":"git+https://github.com/doeixd/create-infinite-resource-solid.git","type":"git"},"homepage":"https://github.com/doeixd/create-infinite-resource-solid#readme","bugs":{"url":"https://github.com/doeixd/create-infinite-resource-solid/issues"},"author":{"name":"Patrick Glenn"},"publishConfig":{"access":"public"},"dependencies":{"@solid-primitives/utils":"^6.3.0"},"types":"./dist/types/index.d.ts","main":"./dist/cjs/production/index.jsx","module":"./dist/esm/production/index.jsx","exports":{".":{"types":"./dist/types/index.d.ts","development":{"require":"./dist/cjs/development/index.jsx","import":"./dist/esm/development/index.jsx"},"require":"./dist/cjs/production/index.jsx","import":"./dist/esm/production/index.jsx"}},"typesVersions":{"*":{}},"gitHead":"4ebf8bf2b7934b381eb10d5b9381dfeaef197871","_id":"@doeixd/create-infinite-resource-solid@0.1.1","_nodeVersion":"16.20.2","_npmVersion":"8.19.4","dist":{"integrity":"sha512-V66xylCFZto/bDCT6WK3xMZtgKpKNKzaNErBUuDEYFUvoq53+vKjLAykEYiKnPepdZCU8t08kD8/tfu35K4tOg==","shasum":"04814cea866ef649b8baed0f27d2675ab94c791b","tarball":"https://registry.npmjs.org/@doeixd/create-infinite-resource-solid/-/create-infinite-resource-solid-0.1.1.tgz","fileCount":12,"unpackedSize":70209,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIDKZdtAt7UK8vHVA0D2Zfz25EdWyk7dwfZVe+BaMMkByAiEA98jzpL1qeIUCigkvvFO5u0lqikwHbDbYyiDTNVkn+Og="}]},"_npmUser":{"name":"doeixd","email":"doeixd@gmail.com"},"directories":{},"maintainers":[{"name":"doeixd","email":"doeixd@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/create-infinite-resource-solid_0.1.1_1766169097555_0.12735631785920565"},"_hasShrinkwrap":false}},"time":{"created":"2025-01-28T14:53:11.640Z","modified":"2025-12-19T18:31:38.456Z","0.0.2":"2025-01-28T14:53:11.909Z","0.0.3":"2025-01-28T15:26:58.781Z","0.1.0":"2025-12-19T18:29:39.247Z","0.1.1":"2025-12-19T18:31:37.712Z"},"bugs":{"url":"https://github.com/doeixd/create-infinite-resource-solid/issues"},"author":{"name":"Patrick Glenn"},"license":"MIT","homepage":"https://github.com/doeixd/create-infinite-resource-solid#readme","keywords":["pridepack","solid-js","solidjs","create-infinite-resource","createResource","infinite"],"repository":{"url":"git+https://github.com/doeixd/create-infinite-resource-solid.git","type":"git"},"description":"A createInfiniteResource helper for solid js","maintainers":[{"name":"doeixd","email":"doeixd@gmail.com"}],"readme":"# `createInfiniteResource`\n\nA SolidJS primitive for managing paginated data fetching with built-in memory management and intersection observer support.\n\n### Features\n - Creates refetchOnView directive for simple use-cases with reactive conditions\n - Handles pagination and memory management with maxPages option\n - Exposes loading states and error handling per page\n - Preserves raw page data while providing merged access\n - Zero-config array flattening with customizable merge strategies\n - Built-in deduplication with `uniqueBy` for cursor-based pagination\n - Bi-directional pagination support (forward/backward)\n - AbortSignal integration to prevent race conditions\n - Refetch current page without duplicating data\n\n## Instalation \n\n```bash\nnpm install @doeixd/create-infinite-resource-solid\n```\n\n## Why is this useful?\n\nManaging infinite scroll in SolidJS typically involves coordinating several primitives (resources, signals, effects) while handling pagination state, memory cleanup, and intersection observers. This primitive handles these concerns while remaining flexible enough for cursor-based pagination, complex data structures, and memory constraints.\n\n\n## Core Concepts\n\nThis primitive wraps `createResource` with some key differences:\n\n1. The fetcher receives a context object for pagination control:\n```ts\ntype FetcherContext<P> = {\n  setPageKey: Setter<P>;   // Set next page/cursor\n  hasReachedEnd: Accessor<boolean>; // Check if at end\n  setHasReachedEnd: Setter<boolean>; // Mark as complete\n  signal: AbortSignal;     // For cancelling stale requests\n  direction: 'forward' | 'backward'; // Pagination direction\n}\n\n// Usage\nconst resource = createInfiniteResource(\n  async (page, { setPageKey, setHasReachedEnd, signal }) => {\n    const data = await fetch(`/api?page=${page}`, { signal });\n    const json = await data.json();\n\n    // Either set next page\n    if (json.nextPage) {\n      setPageKey(json.nextPage);\n    } else {\n      // Or mark as complete\n      setHasReachedEnd(true);\n    }\n\n    return json.items;\n  }\n);\n```\n\n2. Pages are accumulated rather than replaced:\n```ts\n// Default behavior: Flattens arrays\nconst { data } = createInfiniteResource<string[]>();\ndata(); // [\"item1\", \"item2\", \"item3\"] (from all pages)\n\n// Custom merging: Preserve page structure\nconst { data } = createInfiniteResource<Response>({\n  mergeData: (prev, next) => [...prev, next]\n});\ndata(); // [page1, page2, page3]\n```\n\n### Important Details\n\n1. **Memory Management**\n   ```ts\n   createInfiniteResource(fetcher, {\n     maxPages: 5 // Only keep last 5 pages\n   });\n   ```\n   When maxPages is hit, oldest pages are removed. This affects what's returned from `data()` but doesn't refetch dropped pages on scroll up.\n\n2. **Loading States**\n   ```ts\n   const { pageData, data } = createInfiniteResource();\n   pageData.loading; // Current page loading\n   data(); // All accumulated data (even during loads)\n   ```\n   Unlike regular resources, you get both the current page's loading state and accumulated data.\n\n3. **Intersection Observer**\n   ```ts\n   // Basic\n   <div use:refetchOnView={[true, getNextPage]}>\n   \n   // With conditions\n   <div use:refetchOnView={[\n     () => !isError() && !hasReachedEnd(),\n     getNextPage\n   ]}>\n   ```\n   The directive automatically cleans up observers and respects loading states.\n\n### Common Patterns\n\n1. **Cursor-based Pagination**\n```ts\ntype Response = { items: Item[], nextCursor: string | null }\n\ncreateInfiniteResource<Response, string>(\n  async (cursor, { setNextPageKey, setHasReachedEnd }) => {\n    const data = await fetch(`/api?cursor=${cursor}`);\n    \n    if (data.nextCursor) {\n      setNextPageKey(data.nextCursor);\n    } else {\n      setHasReachedEnd(true);\n    }\n    return data;\n  },\n  {\n    initialPageKey: 'initial',\n    mergeData: (prev, next) => [...prev, next] // Keep cursor info\n  }\n);\n```\n\n2. **Error Handling with Retries**\n```ts\ncreateInfiniteResource(fetcher, {\n  onError: (error) => {\n    if (error.status === 429) { // Rate limit\n      setTimeout(getNextPage, 1000);\n    }\n  }\n});\n```\n\n3. **Virtual Lists**\n```ts\n// Keep limited window of data in memory\ncreateInfiniteResource(fetcher, {\n  maxPages: 3,\n  mergeData: (prev, next) => {\n    const window = [...prev, next].slice(-3);\n    virtualizer.setItemCount(totalCount);\n    return window;\n  }\n});\n```\n\n### New Features\n\n#### 1. **Deduplication with `uniqueBy`**\n\nPerfect for cursor-based pagination where items can appear across page boundaries:\n\n```ts\ntype Post = { id: string; title: string; timestamp: number };\n\nconst { data } = createInfiniteResource<Post[], string>(\n  async (cursor, { setPageKey, signal }) => {\n    const response = await fetch(`/api/posts?cursor=${cursor}`, { signal });\n    const json = await response.json();\n\n    if (json.nextCursor) setPageKey(json.nextCursor);\n    return json.posts;\n  },\n  {\n    initialPageKey: 'initial',\n    uniqueBy: (post) => post.id // Automatically deduplicates by ID\n  }\n);\n\n// data() now contains unique posts only, even if they appear in multiple pages\n```\n\n#### 2. **Bi-directional Pagination**\n\nLoad data in both directions - perfect for chat applications:\n\n```ts\nconst { getNextPage, getPreviousPage, data } = createInfiniteResource(\n  async (messageId, { setPageKey, direction }) => {\n    const endpoint = direction === 'backward'\n      ? `/api/messages/before/${messageId}`\n      : `/api/messages/after/${messageId}`;\n\n    const response = await fetch(endpoint);\n    const messages = await response.json();\n\n    if (messages.length > 0) {\n      const nextId = direction === 'backward'\n        ? messages[0].id\n        : messages[messages.length - 1].id;\n      setPageKey(nextId);\n    }\n\n    return messages;\n  },\n  { initialPageKey: 'latest' }\n);\n\n// Load newer messages (default forward direction)\ngetNextPage();\n\n// Load older messages\ngetPreviousPage(); // or getNextPage('backward')\n```\n\n#### 3. **AbortSignal for Race Conditions**\n\nPrevent stale data when users rapidly trigger pagination:\n\n```ts\nconst { getNextPage } = createInfiniteResource(\n  async (page, { setPageKey, signal }) => {\n    try {\n      // Pass signal to fetch - it will auto-cancel on new requests\n      const response = await fetch(`/api?page=${page}`, { signal });\n      const data = await response.json();\n\n      setPageKey(page + 1);\n      return data;\n    } catch (err) {\n      if (err.name === 'AbortError') {\n        // Request was cancelled - this is expected\n        return [];\n      }\n      throw err;\n    }\n  },\n  { initialPageKey: 1 }\n);\n\n// Rapid calls won't cause race conditions\ngetNextPage();\ngetNextPage(); // First request auto-cancelled\ngetNextPage(); // Second request auto-cancelled\n// Only the last request completes\n```\n\n#### 4. **Refetch Current Page**\n\nRefresh data without duplicating:\n\n```ts\nconst { refetchCurrentPage, getNextPage } = createInfiniteResource(\n  fetcher,\n  { initialPageKey: 1 }\n);\n\n// User adds new item, refresh current page\nonItemAdded(() => {\n  refetchCurrentPage(); // Replaces last page data, doesn't append\n});\n\n// Load next page as usual\ngetNextPage(); // Appends as expected\n```\n\n#### 5. **Reactive Directive Conditions**\n\nThe `refetchOnView` directive now reacts to condition changes:\n\n```tsx\nconst [isPaused, setIsPaused] = createSignal(false);\n\n// Observer dynamically connects/disconnects based on condition\n<div use:refetchOnView={[\n  () => !isPaused() && !hasError(), // Reactive!\n  getNextPage\n]}>\n  {isPaused() ? 'Paused' : 'Loading...'}\n</div>\n\n// Pause infinite scroll\nsetIsPaused(true); // Observer disconnects automatically\n\n// Resume\nsetIsPaused(false); // Observer reconnects\n```\n\n## Gotchas\n\n1. `maxPages` drops old data but doesn't refetch - consider UX implications\n2. Default array flattening assumes uniform page data\n3. Page keys must be managed manually through `setPageKey`\n4. The directive assumes element visibility means \"load more\"\n5. `uniqueBy` only works with default array flattening, not with custom `mergeData`\n6. AbortSignal cancels the fetch request but doesn't prevent the fetcher function from running\n\n## Type Details\n\n```ts\ncreateInfiniteResource<\n  T, // Response type (e.g., Product[])\n  P = number | string // Page key type\n>\n\n// For complex data:\ncreateInfiniteResource<Response, Cursor>\n// Response = { items: Product[], cursor: string }\n// Cursor = string\n```\n\n\n### Custom Data Structures\n\nFor non-array responses, each page's data is preserved:\n\n```tsx\ntype ThreadPage = {\n  messages: Message[];\n  participants: User[];\n  cursor: string;\n};\n\nconst { data } = createInfiniteResource<ThreadPage, string>(\n  async (cursor) => {\n    const response = await fetch(`/api/thread?cursor=${cursor}`);\n    return response.json();\n  },\n  {\n    initialPageKey: 'initial',\n    // Each page is preserved as an array element\n    mergeData: (prevPages, newPage) => [...prevPages, newPage]\n  }\n);\n\n// Access individual pages\ndata().map(page => ({\n  messages: page.messages,\n  participants: page.participants\n}));\n```\n\n\n## API Reference\n\n### Function Signature\n\n```ts\nfunction createInfiniteResource<T, P = number | string>(\n  fetcher: (\n    pageKey: P, \n    context: FetcherContext<P>\n  ) => Promise<T>,\n  options?: InfiniteResourceOptions<T, P>\n): InfiniteResourceReturn<T, P>\n```\n\n### Types\n\n#### Options\n```ts\ntype InfiniteResourceOptions<T, P> = {\n  // Initial page key passed to fetcher\n  initialPageKey: P;\n\n  // Maximum number of pages to keep in memory\n  maxPages?: number;\n\n  // Custom function to merge pages\n  mergeData?: (prevPages: T[], newPage: T) => T[];\n\n  // Extract unique key from items for deduplication (NEW)\n  // Only applies when T is an array type\n  uniqueBy?: T extends readonly (infer Item)[]\n    ? (item: Item) => string | number\n    : never;\n\n  // Called when fetcher throws\n  onError?: (error: Error) => void;\n\n  // All createResource options\n  initialValue?: T;\n  name?: string;\n  deferStream?: boolean;\n  storage?: () => Signal<T | undefined>;\n} & ResourceOptions<T>\n```\n\n#### Fetcher Context\n```ts\ntype FetcherContext<P> = {\n  // Set the next page key\n  setPageKey: Setter<P>;\n\n  // Check if at end\n  hasReachedEnd: Accessor<boolean>;\n\n  // Mark as complete\n  setHasReachedEnd: Setter<boolean>;\n\n  // AbortSignal for cancelling requests (NEW)\n  signal: AbortSignal;\n\n  // Current pagination direction (NEW)\n  direction: 'forward' | 'backward';\n}\n```\n\n#### Return Value\n```ts\ntype InfiniteResourceReturn<T, P> = {\n  // Merged data from all pages\n  // If T is an array type, flattens by default\n  data: Accessor<T extends Array<infer U> ? U[] : T[]>;\n\n  // Raw page responses\n  allData: Accessor<T[]>;\n\n  // Current page resource\n  pageData: Resource<T>;\n\n  // Trigger next page load (NEW: now accepts direction)\n  getNextPage: (direction?: 'forward' | 'backward') => void;\n\n  // Convenience for backward pagination (NEW)\n  getPreviousPage: () => void;\n\n  // Refetch current page without duplicating (NEW)\n  refetchCurrentPage: () => void;\n\n  // Get/set page key\n  pageKey: Accessor<P>;\n  setPageKey: Setter<P>;\n\n  // End of data tracking\n  hasReachedEnd: Accessor<boolean>;\n  setHasReachedEnd: Setter<boolean>;\n\n  // Intersection observer directive (NEW: now reactive)\n  refetchOnView: Directive<RefetchDirectiveArgs>;\n\n  // Underlying resource\n  resource: ResourceReturn<T>;\n}\n\n// Directive arguments\ntype RefetchDirectiveArgs = [\n  boolean | (() => boolean), // Condition\n  () => void                 // Callback\n] | (() => [\n  boolean | (() => boolean),\n  () => void\n])\n```\n\n### Methods\n\n#### getNextPage\nTriggers the next page load using the current page key.\n```ts\nconst { getNextPage } = createInfiniteResource(fetcher);\ngetNextPage(); // Loads next page if !hasReachedEnd\n```\n\n#### refetchOnView\nDirective for viewport-based loading.\n```ts\n// Attach to element\n<div use:refetchOnView={[condition, callback]} />\n\n// With reactive condition\n<div use:refetchOnView={[\n  () => canLoadMore(),\n  () => loadMore()\n]} />\n```\n\n### Properties\n\n#### data\nReturns merged data from all pages. By default, flattens arrays:\n```ts\n// With array responses\ntype T = Product[]\nconst { data } = createInfiniteResource<T>();\ndata(); // Product[] (flattened from all pages)\n\n// With custom merging\nconst { data } = createInfiniteResource<T>({\n  mergeData: (prev, next) => [...prev, next]\n});\ndata(); // Product[][] (array of pages)\n```\n\n#### pageData\nResource for current page with loading states:\n```ts\nconst { pageData } = createInfiniteResource();\npageData.loading;  // Current page loading\npageData.error;    // Current page error\npageData();        // Current page data\n```\n\n#### hasReachedEnd\nTracks if all data has been loaded:\n```ts\nconst { hasReachedEnd } = createInfiniteResource();\nhasReachedEnd(); // boolean\n\n// Common pattern\n<Show \n  when={!hasReachedEnd()} \n  fallback=\"No more items\"\n>\n  <LoadMoreButton />\n</Show>\n```\n\n### Resource Access\n\nThe underlying resource is exposed for advanced cases:\n```ts\nconst { resource } = createInfiniteResource(fetcher);\nconst [data, { refetch }] = resource;\n\n// Manual refetch with context\nrefetch({\n  setNextPageKey,\n  hasReachedEnd,\n  setHasReachedEnd\n});\n```","readmeFilename":"README.md"}