Skip to content

Sourcerer 3.0 Migration ​

Sourcerer 3.0 narrows the package to reactive collection sources. It replaces package-defined query fields with consumer-owned params, standardizes source state and commands, and removes the query-cache subpath.

Replace snapshots with state ​

Read state.items and state.loading. Async commands now return Promise<void> instead of returning a snapshot.

ts
// Before
const snapshot = await source.load();
console.log(snapshot.data, snapshot.isFetching);

// After
await source.reload();
console.log(source.state.items, source.state.loading);

Subscriptions receive the same state object exposed by source.state.

Replace query patches with params ​

Move search, filters, sort values, and other loader inputs into one application-owned value. setParams() replaces that value and resets pagination.

ts
// Before
const source = createPageSource<User, RoleFilter, UserSort>({
  initialQuery: { filter: { role: 'admin' }, search: 'ada', sort: { field: 'name' } },
  load: async ({ query, signal }) => loadUsers(query, signal),
});
await source.updateQuery({ search: 'grace' });

// After
type Params = { filter: { role: string }; search: string; sort: { field: string } };
const source = createPageSource<User, Params>({
  load: async ({ page, pageSize, params, signal }) => loadUsers({ page, pageSize, ...params }, signal),
  params: { filter: { role: 'admin' }, search: 'ada', sort: { field: 'name' } },
});
await source.setParams({ ...source.state.params, search: 'grace' });

When TParams excludes undefined, params is required at construction. While replacement params load, committed items remain paired with state.params; the requested value appears in state.pendingParams.

Flatten navigation commands ​

Navigation methods now live directly on each source.

ts
// Before
await source.page.goTo(2);
await source.page.next();

// After
await source.goTo(2);
await source.next();

Use first(), goTo(), last(), next(), and previous() on page sources. Cursor sources expose next() and previous().

Rename configuration and result fields ​

ts
// Before
const source = createInfiniteSource({
  initialQuery: { pageSize: 20 },
  load: async ({ query }) => ({ data: await loadPage(query.page), total: 100 }),
});

// After
const source = createInfiniteSource({
  load: async ({ page }) => ({ items: await loadPage(page), totalItems: 100 }),
  pageSize: 20,
});
BeforeAfter
initialQuery.pageCall goTo(page) after construction
initialQuery.pageSizepageSize
initialQuery.search/filter/sortparams
loader query.pageloader page
loader query.pageSizeloader pageSize
loader result dataloader result items
loader result totalloader result totalItems
snapshot.pendingQuerystate.pendingParams
setData()setItems()

Pagination no longer includes a kind discriminant. Each factory returns one concrete pagination type.

Handle superseded commands ​

Superseded and disposed in-flight work now settles without committing. It no longer waits for a newer command and returns that newer command’s snapshot. Current loader failures still reject and update state.error.

ts
const first = source.setParams({ search: 'a' });
const second = source.setParams({ search: 'ada' });
await Promise.all([first, second]);
console.log(source.state.params); // { search: 'ada' }

Remove the query-cache subpath ​

@vielzeug/sourcerer/query, createQuerySource(), query handles, keyed invalidation, retries, optimistic mutations, and cache garbage collection are removed. No replacement lives in Sourcerer 3.0.

Use a dedicated server-state cache when shared keyed caching is required. Keep simple uncached reads in Sourcerer loaders and keep HTTP behavior in Courier.

Remove generic base exports ​

Disposable, Source, SourceSnapshot, AnyPagination, query types, and the old query patch/result types are no longer public. Import concrete source, state, context, result, and pagination types instead.