import { extractPageInfo } from 'houdini/runtime' import { cursorHandlers, offsetHandlers } from 'houdini/runtime' import type { GraphQLObject, QueryResult, CursorHandlers, OffsetHandlers, PageInfo, GraphQLVariables, } from 'houdini/runtime' import { get, derived } from 'svelte/store' import type { Subscriber } from 'svelte/store' import { getClient, initClient } from '../../client.js' import { getSession } from '../../session.js' import type { ClientFetchParams, LoadEventFetchParams, QueryStoreFetchParams, RequestEventFetchParams, } from '../../types.js' import { QueryStore } from '../query.js' export type CursorStoreResult< _Data extends GraphQLObject, _Input extends GraphQLVariables | null | undefined, > = QueryResult<_Data, _Input> & { pageInfo: PageInfo } // both cursor paginated stores add a page info to their subscribe export class QueryStoreCursor< _Data extends GraphQLObject, _Input extends GraphQLVariables | null | undefined, > extends QueryStore<_Data, _Input> { // all paginated stores need to have a flag to distinguish from other query stores paginated = true #_handlers: CursorHandlers<_Data, _Input> | null = null async #handlers(): Promise> { if (this.#_handlers) { return this.#_handlers } // initialize the client before we compute the handlers await initClient() // we're going to use a separate observer for the page loading const paginationObserver = getClient().observe<_Data, _Input>({ artifact: this.artifact, }) this.#_handlers = cursorHandlers<_Data, _Input>({ artifact: this.artifact, getState: () => get(this.observer).data, getVariables: () => get(this.observer).variables!, fetch: super.fetch.bind(this), getSession: getSession, fetchUpdate: async (args, updates) => { return paginationObserver.send({ ...args, cacheParams: { applyUpdates: updates, disableSubscriptions: true, ...args?.cacheParams, }, }) }, }) return this.#_handlers } fetch(params?: RequestEventFetchParams<_Data, _Input>): Promise> fetch(params?: LoadEventFetchParams<_Data, _Input>): Promise> fetch(params?: ClientFetchParams<_Data, _Input>): Promise> fetch(params?: QueryStoreFetchParams<_Data, _Input>): Promise> async fetch(args?: QueryStoreFetchParams<_Data, _Input>): Promise> { const handlers = await this.#handlers() return await handlers.fetch.call(this, args) } async loadPreviousPage( args?: Parameters>['loadPreviousPage']>[0] ): Promise> { const handlers = await this.#handlers() try { return await handlers.loadPreviousPage(args) } catch (e) { const err = e as Error // if the error is an abort error then we don't want to throw if (err.name === 'AbortError') { return get(this.observer) } else { throw err } } } async loadNextPage( args?: Parameters['loadNextPage']>[0] ): Promise> { const handlers = await this.#handlers() try { return await handlers.loadNextPage(args) } catch (e) { const err = e as Error // if the error is an abort error then we don't want to throw if (err.name === 'AbortError') { return get(this.observer) } else { throw err } } } subscribe( run: Subscriber>, invalidate?: ((value?: CursorStoreResult<_Data, _Input> | undefined) => void) | undefined ): () => void { const combined = derived([{ subscribe: super.subscribe.bind(this) }], ([$parent]) => { return { ...$parent, pageInfo: extractPageInfo($parent.data, this.artifact.refetch!.path), } }) return combined.subscribe(run, invalidate) } } export class QueryStoreOffset< _Data extends GraphQLObject, _Input extends GraphQLVariables | null | undefined, > extends QueryStore<_Data, _Input> { // all paginated stores need to have a flag to distinguish from other query stores paginated = true async loadNextPage(args?: Parameters['loadNextPage']>[0]) { const handlers = await this.#handlers() return await handlers.loadNextPage.call(this, args) } fetch(params?: RequestEventFetchParams<_Data, _Input>): Promise> fetch(params?: LoadEventFetchParams<_Data, _Input>): Promise> fetch(params?: ClientFetchParams<_Data, _Input>): Promise> fetch(params?: QueryStoreFetchParams<_Data, _Input>): Promise> async fetch(args?: QueryStoreFetchParams<_Data, _Input>): Promise> { const handlers = await this.#handlers() return await handlers.fetch.call(this, args) } #_handlers: OffsetHandlers<_Data, _Input> | null = null async #handlers(): Promise> { if (this.#_handlers) { return this.#_handlers } // initialize the client before we compute the handlers await initClient() // we're going to use a separate observer for the page loading const paginationObserver = getClient().observe<_Data, _Input>({ artifact: this.artifact, }) this.#_handlers = offsetHandlers<_Data, _Input>({ artifact: this.artifact, storeName: this.name, fetch: super.fetch.bind(this), getState: () => get(this.observer).data, getVariables: () => get(this.observer).variables!, getSession: getSession, fetchUpdate: async (args) => { await initClient() return paginationObserver.send({ ...args, variables: { ...args?.variables, }, cacheParams: { applyUpdates: ['append'], }, }) }, }) return this.#_handlers } }