Skip to content

API Overview

SymbolPurposeExecution modeCommon gotcha
createLocalSource()Filter and paginate in-memory itemsSyncparams replacement uses Object.is
createPageSource()Load numbered pagesAsyncConstruction is inert
createCursorSource()Load server-issued cursor pagesAsyncafter and before cannot coexist initially
createInfiniteSource()Append numbered pagesAsyncloadMore() is a no-op while loading or exhausted
SourcererErrorBase package errorSyncLoader errors retain their original type

Package Entry Point

ImportPurpose
@vielzeug/sourcererAll factories, states, pagination types, loader contexts, and errors

Source Factories

createLocalSource()

ts
function createLocalSource<T>(items: readonly T[]): LocalSource<T>
function createLocalSource<T, TParams = undefined>(
  items: readonly T[],
  config: LocalSourceConfig<T, TParams>,
): LocalSource<T, TParams>

Returns a synchronous in-memory source.

ParameterTypeDescription
itemsreadonly T[]Initial collection
config.filter(item: T, params: TParams) => booleanOptional inclusion predicate
config.pageSizenumberPositive page size; defaults to 20
config.paramsTParamsInitial params; required when TParams excludes undefined

Returns: LocalSource<T, TParams>.

ts
import { createLocalSource } from '@vielzeug/sourcerer';

const source = createLocalSource(['Ada', 'Grace'], {
  filter: (name, search: string) => name.includes(search),
  params: '',
});
source.setParams('Ada');
MethodReturnsDescription
first()voidMove to page one
goTo(page)voidMove to a positive page, clamped to the known range
last()voidMove to the final page
next()voidMove forward when available
previous()voidMove backward when available
setItems(items)voidReplace the collection and recompute state
setPageSize(pageSize)voidReplace page size and reset to page one
setParams(params)voidReplace params and reset to page one
subscribe(listener)() => voidRegister a state listener and return its unsubscribe function
dispose()voidStop notifications and reject later mutations

createPageSource()

ts
function createPageSource<T, TParams = undefined>(
  config: PageSourceConfig<T, TParams>,
): PageSource<T, TParams>

Returns an inert numbered-page source.

ParameterTypeDescription
config.load(context: PageLoadContext<TParams>) => Promise<PageResult<T>>Page loader
config.pageSizenumberPositive page size; defaults to 20
config.paramsTParamsInitial params; required when TParams excludes undefined

Returns: PageSource<T, TParams>.

ts
import { createPageSource } from '@vielzeug/sourcerer';

const source = createPageSource({
  load: async ({ page }) => ({ items: [page], totalItems: 10 }),
});
await source.reload();
MethodReturnsDescription
first()Promise<void>Load page one
goTo(page)Promise<void>Load a positive page
last()Promise<void>Load the last known page
next()Promise<void>Load the next known page
previous()Promise<void>Load the previous page
reload()Promise<void>Reload the requested page and params
setPageSize(pageSize)Promise<void>Reset to page one and load with a new size
setParams(params)Promise<void>Reset to page one and load replacement params
subscribe(listener)() => voidRegister a state listener and return its unsubscribe function
dispose()voidAbort work and stop notifications

createCursorSource()

ts
function createCursorSource<T, TParams = undefined, TCursor = string>(
  config: CursorSourceConfig<T, TParams, TCursor>,
): CursorSource<T, TParams, TCursor>

Returns an inert cursor source.

ParameterTypeDescription
config.afterTCursorOptional initial forward cursor
config.beforeTCursorOptional initial backward cursor
config.load(context: CursorLoadContext<TParams, TCursor>) => Promise<CursorResult<T, TCursor>>Cursor loader
config.pageSizenumberPositive page size; defaults to 20
config.paramsTParamsInitial params; required when TParams excludes undefined

Returns: CursorSource<T, TParams, TCursor>.

ts
import { createCursorSource } from '@vielzeug/sourcerer';

const source = createCursorSource({ load: async () => ({ items: ['first'], nextCursor: 'next' }) });
await source.reload();
await source.next();
MethodReturnsDescription
next()Promise<void>Load state.pagination.nextCursor when available
previous()Promise<void>Load state.pagination.previousCursor when available
reload()Promise<void>Reload the requested cursor and params
setPageSize(pageSize)Promise<void>Clear cursors and reload with a new size
setParams(params)Promise<void>Clear cursors and load replacement params
subscribe(listener)() => voidRegister a state listener and return its unsubscribe function
dispose()voidAbort work and stop notifications

createInfiniteSource()

ts
function createInfiniteSource<T, TParams = undefined>(
  config: InfiniteSourceConfig<T, TParams>,
): InfiniteSource<T, TParams>

Returns an inert source that appends numbered pages.

ParameterTypeDescription
config.load(context: InfiniteLoadContext<TParams>) => Promise<PageResult<T>>Page loader
config.pageSizenumberPositive page size; defaults to 20
config.paramsTParamsInitial params; required when TParams excludes undefined

Returns: InfiniteSource<T, TParams>.

ts
import { createInfiniteSource } from '@vielzeug/sourcerer';

const source = createInfiniteSource({ load: async () => ({ items: ['post'], totalItems: 1 }) });
await source.reload();
await source.loadMore();
MethodReturnsDescription
loadMore()Promise<void>Append the next page when available
reload()Promise<void>Replace items from page one
setPageSize(pageSize)Promise<void>Replace items from page one with a new size
setParams(params)Promise<void>Replace items from page one with new params
subscribe(listener)() => voidRegister a state listener and return its unsubscribe function
dispose()voidAbort work and stop notifications

Types

Page source types

ts
type PagePagination = Readonly<{
  hasNext: boolean;
  hasPrevious: boolean;
  page: number;
  pageCount: number;
  pageSize: number;
  totalItems: number;
}>;

type PageSourceState<T, TParams = undefined> = Readonly<{
  error: Error | null;
  items: readonly T[];
  loading: boolean;
  pagination: PagePagination;
  params: TParams;
  pendingParams?: TParams;
}>;

type PageLoadContext<TParams = undefined> = Readonly<{
  page: number;
  pageSize: number;
  params: TParams;
  signal: AbortSignal;
}>;

type PageResult<T> = Readonly<{
  items: readonly T[];
  totalItems: number;
}>;

type PageSourceConfig<T, TParams = undefined> = Readonly<{
  load(context: PageLoadContext<TParams>): Promise<PageResult<T>>;
  pageSize?: number;
  } & (undefined extends TParams ? { params?: TParams } : { params: TParams })
>;

type PageSource<T, TParams = undefined> = {
  [Symbol.dispose](): void;
  readonly disposalSignal: AbortSignal;
  dispose(): void;
  readonly disposed: boolean;
  first(): Promise<void>;
  goTo(page: number): Promise<void>;
  last(): Promise<void>;
  next(): Promise<void>;
  previous(): Promise<void>;
  reload(): Promise<void>;
  setPageSize(pageSize: number): Promise<void>;
  setParams(params: TParams): Promise<void>;
  readonly state: PageSourceState<T, TParams>;
  subscribe(listener: (state: PageSourceState<T, TParams>) => void): () => void;
};

The conditional config field makes params optional only when TParams includes undefined.

Local source types

ts
type LocalSourceState<T, TParams = undefined> = Readonly<{
  error: null;
  items: readonly T[];
  loading: false;
  pagination: PagePagination;
  params: TParams;
}>;

type LocalSourceConfig<T, TParams = undefined> = Readonly<{
  filter?: (item: T, params: TParams) => boolean;
  pageSize?: number;
  } & (undefined extends TParams ? { params?: TParams } : { params: TParams })
>;

type LocalSource<T, TParams = undefined> = {
  [Symbol.dispose](): void;
  readonly disposalSignal: AbortSignal;
  dispose(): void;
  readonly disposed: boolean;
  first(): void;
  goTo(page: number): void;
  last(): void;
  next(): void;
  previous(): void;
  setItems(items: readonly T[]): void;
  setPageSize(pageSize: number): void;
  setParams(params: TParams): void;
  readonly state: LocalSourceState<T, TParams>;
  subscribe(listener: (state: LocalSourceState<T, TParams>) => void): () => void;
};

Cursor source types

ts
type CursorPagination<TCursor = string> = Readonly<{
  nextCursor?: TCursor;
  pageSize: number;
  previousCursor?: TCursor;
  totalItems?: number;
}>;

type CursorLoadContext<TParams = undefined, TCursor = string> = Readonly<{
  after?: TCursor;
  before?: TCursor;
  pageSize: number;
  params: TParams;
  signal: AbortSignal;
}>;

type CursorResult<T, TCursor = string> = Readonly<{
  items: readonly T[];
  nextCursor?: TCursor;
  previousCursor?: TCursor;
  totalItems?: number;
}>;

type CursorSourceState<T, TParams = undefined, TCursor = string> = Readonly<{
  error: Error | null;
  items: readonly T[];
  loading: boolean;
  pagination: CursorPagination<TCursor>;
  params: TParams;
  pendingParams?: TParams;
}>;

type CursorSourceConfig<T, TParams = undefined, TCursor = string> = Readonly<{
  after?: TCursor;
  before?: TCursor;
  load(context: CursorLoadContext<TParams, TCursor>): Promise<CursorResult<T, TCursor>>;
  pageSize?: number;
  } & (undefined extends TParams ? { params?: TParams } : { params: TParams })
>;

type CursorSource<T, TParams = undefined, TCursor = string> = {
  [Symbol.dispose](): void;
  readonly disposalSignal: AbortSignal;
  dispose(): void;
  readonly disposed: boolean;
  next(): Promise<void>;
  previous(): Promise<void>;
  reload(): Promise<void>;
  setPageSize(pageSize: number): Promise<void>;
  setParams(params: TParams): Promise<void>;
  readonly state: CursorSourceState<T, TParams, TCursor>;
  subscribe(listener: (state: CursorSourceState<T, TParams, TCursor>) => void): () => void;
};

Infinite source types

ts
type InfinitePagination = Readonly<{
  hasMore: boolean;
  loadedItems: number;
  pageSize: number;
  totalItems: number;
}>;

type InfiniteLoadContext<TParams = undefined> = Readonly<{
  page: number;
  pageSize: number;
  params: TParams;
  signal: AbortSignal;
}>;

type InfiniteSourceState<T, TParams = undefined> = Readonly<{
  error: Error | null;
  items: readonly T[];
  loading: boolean;
  pagination: InfinitePagination;
  params: TParams;
  pendingParams?: TParams;
}>;

type InfiniteSourceConfig<T, TParams = undefined> = Readonly<{
  load(context: InfiniteLoadContext<TParams>): Promise<PageResult<T>>;
  pageSize?: number;
  } & (undefined extends TParams ? { params?: TParams } : { params: TParams })
>;

type InfiniteSource<T, TParams = undefined> = {
  [Symbol.dispose](): void;
  readonly disposalSignal: AbortSignal;
  dispose(): void;
  readonly disposed: boolean;
  loadMore(): Promise<void>;
  reload(): Promise<void>;
  setPageSize(pageSize: number): Promise<void>;
  setParams(params: TParams): Promise<void>;
  readonly state: InfiniteSourceState<T, TParams>;
  subscribe(listener: (state: InfiniteSourceState<T, TParams>) => void): () => void;
};

Errors

ErrorTriggerNotable properties
SourcererErrorBase class for package-originated configuration and lifecycle errorsinstanceof SourcererError narrows the hierarchy
SourcererConfigurationErrorInvalid page, page size, total, or initial cursor directionExtends SourcererError
SourcererDisposedErrorA source command runs after disposalExtends SourcererError

Loader failures are preserved when they are Error instances. Non-Error rejections become Error objects whose cause contains the original value.