npm.io
2.3.0 • Published 4h ago

@pastweb/tools

Licence
MIT
Version
2.3.0
Deps
0
Size
2.0 MB
Vulns
0
Weekly
558

@pastweb/tools

A collection of production-ready, tree-shakeable utility functions for common tasks in modern JavaScript and TypeScript applications.

Features

  • Broad coverage — Async operations, browser utilities, a full reactivity system, client-side routing, DOM helpers, object utilities, string transforms, SCSS tools, and more.
  • Lightweight & optimized — Fully tree-shakeable with minimal dependencies.
  • TypeScript-first — Excellent type definitions, generics, and editor support out of the box.
  • Framework-agnostic — Works in any environment. Includes first-class support for mediator patterns and global context (useful in React, Vue, Svelte, Solid, etc.).
  • Well documented — Every utility comes with clear syntax, parameters, return types, real-world examples, use cases, notes, and edge cases.
  • Modern architecture — Built around reactivity (reactive, ref, computed, effect), async-first stores, unique ID generation, and clean separation of concerns.

Installation

npm i -S @pastweb/tools
# or
pnpm i -S @pastweb/tools
# or
yarn add -S @pastweb/tools

Documentation Overview

The documentation is organized into the following major categories. Each section provides complete TypeScript definitions, practical examples, real-world use cases, and notes on edge cases.

  • Async functions — Tools for working with promises, API clients (createApiAgent, useQuery, useMutation), async stores, event emitters, debouncing, and throttling.
  • Browser functions — Client-side utilities including device detection, color scheme management, persistent storage, and the complete routing system.
  • Date and Time — Helpers for comparing dates and converting duration strings to milliseconds.
  • Element functions — DOM and UI utilities (class name composition, portals, anchor generation, element sizing).
  • Object functions — General-purpose object utilities (deep merging, property assignment, type checking, immutability helpers, and more).
  • Reactivity — A complete reactivity system (reactive, ref, computed, effect) together with supporting utilities and the Global Context pattern.
  • Routing — The full createViewRouter solution, including route definition, matching, navigation, and mediator hooks for framework integration.
  • SSR utilities (experimental) — Server-render coordination helpers (registerAsyncTask, resolveAsyncTasks, runSSRCycle, and SSR tracker utilities).
  • String functions — String transformation utilities (camelCase, kebab-case, friendly ID generation).
  • Styles — SCSS mixins and tools for responsive design, theming, and layout utilities.
  • Utility functions — General-purpose helpers (memoization, SSR detection, no-op, and similar tools).

This project is distributed under the MIT licence.

Summary


Async functions

createApiAgent

Creates a configured Axios-based API client ("agent") with optional caching, pagination support, request interceptors for auth, and SSR-friendly query collection.

All functions and types have comprehensive TSDoc (including internal types).

Syntax
function createApiAgent(options?: AgentOptions): Agent;

Parameters

  • options: AgentOptions (optional) The options for the API agent.
    • cache: boolean (optional) (default: false)
      • @deprecated Use queryCache instead.
    • queryCache: QueryCache (optional)
      • Pass createQueryCache() to enable caching for GETs + SSR support (dehydrate/hydrate).
      • This is the recommended way. The agent will use the provided instance for caching.
    • headers: Record<string, any> (optional) (default: {})
      • Default headers to include on every request.
    • withCredentials: boolean (optional) (default: false)
      • Indicates whether cross-site Access-Control requests should be made using credentials.
    • pagination: boolean | PaginationConfig (optional) (default: true)
      • Enables pagination parsing from Content-Range header.
    • exclude: string | RegExp | Array<string | RegExp> (optional)
      • URLs or patterns to exclude from request interception.
    • onGetValidToken: () => ValidTokenResponse | Promise<ValidTokenResponse> (optional)
      • Function to get a valid token for authorization.
    • onUnauthorizedResponse: () => void | Promise<void> (optional)
      • Callback for unauthorized responses.

Returns

  • Agent
    • The configured API agent.

See the source TSDoc (now present on all functions and types, including in types.ts) for full details on methods like get, dehydrate/hydrate (on QueryCache), etc.

Methods

  • setAgentOptions(options: AgentOptions): void
    • Sets the agent configuration.
  • mergeAgentConfig(newSettings: AxiosRequestConfig): void
    • Merges new settings into the existing agent configuration.
  • getPageLimit(limit?: PageLimit): number
    • Returns the page limit as a number (default: 100).
  • getPageNumber(page?: PageNumber): number
    • Returns the page number as a number (default: 1).
  • pageToOffset(page?: PageNumber, limit?: PageLimit): number
    • Converts a page number to an offset for pagination.
  • delete<T = any>(url: string, options: MutationOptions): Promise<AxiosResponse<T>>
    • Sends a DELETE request. Supports AxiosRequestConfig and onSuccess/onError callbacks when a queryCache is provided on the agent.
  • get<T = any>(url: string, options: QueryOptions): Promise<AxiosResponse<T>>
    • Sends a GET request. Supports AxiosRequestConfig and caching with queryKey (array or string), expireIn, and cache lifecycle options when a queryCache is provided on the agent. Present queryKey → its serialized value is the cache key. Omitted → URL is the cache key. select projects response.data for the current caller without changing the raw cached response. ssrMode and ssrRevalitate report hybrid SSR behavior to the active SSR tracker when rendering on the server. toon: true adds text/toon to the request Accept header; text/toon responses are decoded into JavaScript values with @toon-format/toon. Using these without queryCache will log a console.error.
  • patch<T = any>(url: string, data?: unknown, options: MutationOptions): Promise<AxiosResponse<T>>
    • Sends a PATCH request.
  • post<T = any>(url: string, data?: unknown, options: MutationOptions): Promise<AxiosResponse<T>>
    • Sends a POST request.
  • put<T = any>(url: string, data?: unknown, options: MutationOptions): Promise<AxiosResponse<T>>
    • Sends a PUT request.
  • upload(url: string, data: FormData, onUploadProgress?: (e: AxiosProgressEvent) => void): Promise<AxiosResponse>
    • Uploads a file using a POST request with multipart/form-data for file uploads.
  • download(url: string, fileName: string, domElement?: HTMLElement): Promise<AxiosResponse>
    • Downloads a file using a GET request and triggers a download in the browser.

Cache Caching is enabled by passing a queryCache to createApiAgent({ queryCache }) (the old cache: true boolean is deprecated). GET responses are stored under a key that is either:

  • the serialized queryKey you provide (array recommended: ['users', id], or legacy string), or
  • the full request URL when no queryKey is given.

The cache supports:

  • get(key: string): QueryData
    • Retrieves a cached response.
  • getAll(): [string, QueryData][]
    • Returns all cache entries.
  • has(key: string): boolean
    • Checks if a key exists in the cache.
  • set(key: string, data: QueryData): Map<string, QueryData>
    • Sets a cache entry with a response, timestamp, and expiration.
  • delete(key: string): boolean
    • Removes a cache entry.
  • invalidateQuery(queryKey?: unknown | unknown[]): void
    • Invalidates cache entries by key or prefix. Accepts the same value you passed as queryKey (string, array, or URL). Works for both URL keys and structured queryKeys.

Cache entries (when a queryCache is enabled) expire based on the expireIn option (e.g., '1s', '5m') using the isDateYoungerOf utility. Using expireIn or queryKey without enabling queryCache on the agent will log a console error.

GET options (passed to agent.get via QueryOptions):

Option Type Behavior
select (data, response) => any Projects response.data for the current agent.get call. The cache stores the raw response; onData updates are projected with the same selector.
toon boolean Adds text/toon to the GET request Accept header. If the server responds with content-type containing text/toon, the response body is decoded with @toon-format/toon.
fetchOnExpired true | string Replaces the former callOnExpired. true = passive (refetch on next get only if stale). string = active timer that auto-refetches when expireIn is exceeded.
fetchOnInvalidate true | string After invalidateQuery, immediately refetch (true) or wait the duration string before refetching.
removeOnExpired boolean Remove the entry when expireIn is exceeded (instead of refetching).
removeOnInvalidate boolean Remove the entry immediately when invalidated.
ssrMode 'auto' | 'static' | 'dynamic' | 'no-store' Server-rendering mode for hybrid static/dynamic pages. dynamic and no-store mark the active SSR tracker dynamic.
ssrRevalitate string | false Static page revalidation hint for SSR dependencies. Strings are converted to milliseconds with stringToMs; false disables time-based revalidation.
// Select a view of a wrapped API response while keeping the raw response in the cache.
// Example API response:
// {
//   items: [{ id: 1, name: 'Ada' }],
//   meta: { total: 1 }
// }
const usersResponse = await agent.get('/api/users', {
  queryKey: ['users'],
  select: data => data.items,
});
console.log(usersResponse.data); // [{ id: 1, name: 'Ada' }]

// Another caller can reuse the same cache entry and select a different view.
const usersTotalResponse = await agent.get('/api/users', {
  queryKey: ['users'],
  select: data => data.meta.total,
});
console.log(usersTotalResponse.data); // 1

// Auto-refetch 1s after expiration window
await agent.get('/api/users', { expireIn: '5m', fetchOnExpired: '1s' });

// Passive: only refetch on next get if stale
await agent.get('/api/users', { expireIn: '5m', fetchOnExpired: true });

// Refetch immediately after invalidation
await agent.get('/api/users', { expireIn: '5m', fetchOnInvalidate: true });
agent.cache.invalidateQuery('/api/users');

// Drop stale entries instead of refetching
await agent.get('/api/users', { expireIn: '5m', removeOnExpired: true });

// Request TOON and receive decoded JavaScript data when the server returns text/toon
await agent.get('/api/users', { toon: true });

Pagination When pagination is enabled, the successResponseInterceptor processes responses with a Content-Range header (e.g., 0-1/20 where 0-1 is start and end index adn 20 is the items total number). For application/json responses contains the pagination additional info:

{
  data: any, // Original response data
  pagination: {
    start: number, // Start index
    end: number, // End index
    total: number, // Total items
    size: number, // Page size
    current: number, // Current page
    of: number // Total pages
  }
}

The Content-Range header is parsed to extract start, end, and total. The limit query parameter (or defaultPageLimit) determines the page size.

Example:

import { createApiAgent } from '@pastweb/tools';

const apiAgent = createApiAgent({
  withCredentials: true,
  headers: {
    Accept: 'application/json',
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token'
  },
  onGetValidToken: async () => ({ Authorization: 'Bearer newToken' }),
  onUnauthorizedResponse: () => {
    console.log('Unauthorized! Redirecting to login...');
  },
});

// Making a GET request
apiAgent.get('/api/data').then(response => {
  console.log('Data:', response.data);
});

// Uploading a file
const formData = new FormData();
formData.append('file', fileInput.files[0]);
apiAgent.upload('/api/upload', formData, (event) => {
  console.log('Upload progress:', Math.round((event.loaded * 100) / event.total));
});

// Downloading a file
apiAgent.download('/api/download', 'file.txt');

// Cached GET Request using structured queryKey (array) — the recommended form.
// The key stored in cache will be JSON.stringify(['users', 123]) i.e. '["users",123]'
const queryCache = createQueryCache();
const agent = createApiAgent({ queryCache });
await agent.get('/api/users/123', { queryKey: ['users', 123], expireIn: '5m' });
const same = await agent.get('/api/users/123', { queryKey: ['users', 123] }); // cache hit

// URL-only key (no queryKey) — cache key becomes the literal URL string
await agent.get('/api/users?status=active');

// Invalidate using the same queryKey shape you used when fetching (recommended)
agent.cache.invalidateQuery(['users', 123]);

// Or using a URL key / prefix (still fully supported)
agent.cache.invalidateQuery('/api/users');

// You can also pass the serialized string form if you prefer
agent.cache.invalidateQuery(JSON.stringify(['users', 123]));


// Paginated GET Request
const agent = createApiAgent({ pagination: { defaultPageLimit: 10, header: 'Content-Range' } });
const response = await agent.get('/api/users?offset0&limit=10');
// Response: { data: [...], info: { start: 0, end: 9, total: 50, size: 10, current: 1, of: 5 } }

createQueryCache

Creates a reusable QueryCache instance that can be passed to one or more createApiAgent({ queryCache }) calls. Primarily useful to share cache state (and registered SSR prefetch functions) across agents, e.g. in SSR entry points for coordinated data collection before render.

Syntax
function createQueryCache(options?: CacheOptions): QueryCache

CacheOptions:

  • refetchOnWindowFocus?: boolean (default: false) — When true and running in a browser, re-runs every registered expiration checker in the cache when the window/tab regains focus.
  • refetchOnReconnect?: boolean (default: false) — When true and running in a browser, re-runs every registered expiration checker when the browser fires the online event.

The returned cache has:

  • get(key), has(key), set(...) (internal), delete, invalidateQuery(key?), invalidateQueries(keys), getAll(), dehydrate(), hydrate(data), resetForSSR() (see above for hybrid key support)

Cache keys are either serialized queryKey values (when you pass queryKey: [...] to get/useQuery) or raw URLs. invalidateQuery matches against whatever keys are stored (prefix rules apply to the final string keys). invalidateQueries accepts an array of keys and delegates to invalidateQuery for each one.

dehydrate() executes any prefetch functions registered during SSR "dry runs", populates the cache, and returns a JSON string snapshot.

hydrate(json) can be used to restore the cache from a string previously returned by dehydrate().

Example (SSR collection sketch):

import { createApiAgent, createQueryCache } from '@pastweb/tools';

const queryCache = createQueryCache();
const api = createApiAgent({ queryCache });
// ... later during collection pass, renders call useQuery which trigger agent.get that register
const snapshot = await queryCache.dehydrate();
// snapshot is JSON — write it to .pastweb/ssr-manifest.json etc.

queryCache.hydrate(savedSnapshot); // restore before real render

Use queryCache.resetForSSR() at the start of each SSR request (or rely on runSSRCycle, which calls it automatically) to avoid cross-request cache leakage.

For partial hydration, use sliceDehydratedState(snapshot, queryKeys) to extract island-scoped cache JSON before client hydrateRoot.

See AgentOptions.queryCache and QueryCache for details. (dehydrate() is exposed on QueryCache.)

useQuery

Creates a reactive query Object that fetches data using the provided function and updates based on reactive dependencies

Syntax
function useQuery<T>(config: QueryConfig<T>): QueryInfo<T>

Parameters

  • config: QueryConfig<T> The configuration for the query.
    • fn: () => Promise<AxiosResponse<T>> The function to fetch data, typically an agent.get call from createApiAgent.
    • source?: (() => any) | Ref<any> | Array<(() => any) | Ref<any>> (optional) Reactive dependencies to track (e.g., () => page.value). Required if fn uses reactive variables in its URL or parameters.
    • immediate?: boolean | Ref<boolean> (optional) (default: true) If true or a Ref with value: true, runs the query immediately. If a Ref<boolean>, triggers the query when value becomes true.
    • initialData?: T (optional) Initial data to set before the first fetch. Sets isPlaceholderData to true until a fetch completes.
    • retry?: boolean | number | ((failureCount: number, error: unknown) => boolean) (optional) (default: false) Retries failed query executions. true retries up to 3 times, a number retries that many times, and a function decides per failure.
    • retryDelay?: number | string | ((failureCount: number, error: unknown) => number) (optional) (default: 0) Delay before each retry. Numbers are milliseconds, strings use stringToMs duration syntax such as '1s', and functions return milliseconds.

Returns

  • QueryInfo<T> A reactive object with query state and methods.
    • status: 'pending' | 'success' | 'error' Query lifecycle status.
    • fetchStatus: 'idle' | 'fetching' Transport activity status.
    • responseStatus: number | null HTTP status from the last successful response or Axios error response.
    • data: T | null The response data or initialData.
    • pagination: Page<any>['pagination'] | null Pagination info if available
    • isPending: boolean True while no successful real response has been received yet. A disabled query can be pending while idle.
    • isLoading: boolean True only during the first fetch (status === 'pending' && fetchStatus === 'fetching').
    • isFetching: boolean True during any fetch.
    • isError: boolean True if an error occurred.
    • error: any The error object, if any.
    • isPlaceholderData: boolean True if data is initialData.
    • fetch: () => Promise<void> Manually triggers the query.

The useQuery function creates a reactive query that automatically fetches data when initialized (if immediate is true) or when reactive dependencies in source or immediate (if a Ref) change. It integrates with createApiAgent to handle reactive AxiosResponse objects, ensuring data updates with new responses or cache changes. The source parameter is required to track reactive variables used in fn (e.g., page.value in the URL).

When using agent.get inside fn, pass select in QueryOptions to project the response data for that query without changing the raw cached response. This is useful when an API returns wrapper objects but the consuming view only needs one nested value.

For SSR scenarios, create a queryCache via createQueryCache() and pass it as the queryCache option when creating agents (createApiAgent({ queryCache })). Agents and useQuery calls during a collection (dry) render will register prefetch functions. Call dehydrate() on the queryCache to execute the registered prefetches and obtain a JSON cache snapshot before the final render pass. See also createQueryCache.

(Note: agent.cache and the cache: true option are deprecated.)

Example:

import { createApiAgent, useQuery, ref } from '@pastweb/tools';

// Create an API agent with caching
const queryCache = createQueryCache();
const agent = createApiAgent({
  queryCache,
  pagination: true,
});

// Basic query with immediate fetch
const query = useQuery({
  fn: () => agent.get('/api/users?_page=1&_limit=10'),
});

console.log(query.data); // Initially null, updates to { data: [...], info: {...} }
console.log(query.isLoading); // true during the first fetch, then false
console.log(query.responseStatus); // HTTP status after a response, e.g. 200

// Query with reactive dependency
const page = ref(1);
const reactiveQuery = useQuery({
  fn: () => agent.get(`/api/users?_page=${page.value}&_limit=10`),
  source: page,
});

page.value = 2; // Triggers refetch with new URL

// Query with Ref<boolean> immediate
const immediate = ref(false);
const controlledQuery = useQuery({
  fn: () => agent.get('/api/users'),
  immediate,
});

immediate.value = true; // Triggers fetch

// With structured queryKey (recommended when using caching).
// The same key must be passed inside the agent.get call for the cache layer to use it.
// Internally the array is serialized (e.g. '["user",42]') and used as the storage key.
const userId = ref(42);
const userQuery = useQuery({
  fn: () => agent.get(`/api/users/${userId.value}`, {
    queryKey: ['user', userId.value],
    expireIn: '2m',
    select: data => data.user,
  }),
  source: userId,
});

// You can still omit queryKey entirely — the full URL (incl. querystring) becomes the cache key.
const listQuery = useQuery({
  fn: () => agent.get('/api/posts?published=true'),
  // cache key will be exactly "/api/posts?published=true"
});

// Retry failed requests. This retries twice after the first failed attempt.
const resilientUsersQuery = useQuery({
  fn: () => agent.get('/api/users', { queryKey: ['users'] }),
  retry: 2,
  retryDelay: 500,
});

// Retry can also be conditional.
const conditionalRetryQuery = useQuery({
  fn: () => agent.get('/api/admin'),
  retry: (failureCount, error: any) => {
    return failureCount < 2 && error?.response?.status !== 401;
  },
  retryDelay: '1s',
});

// Manual invalidation - pass the same queryKey you used (array or string/URL)
agent.cache.invalidateQuery(['user', 42]);

useInfiniteQuery

Creates a reactive infinite query for paginated or cursor-based lists.

Syntax
function useInfiniteQuery<TPage, TPageParam = unknown>(
  config: InfiniteQueryConfig<TPage, TPageParam>
): InfiniteQueryInfo<TPage, TPageParam>

Parameters

  • initialPageParam The first page/cursor param.
  • fn(pageParam) Fetches one page and returns an agent QueryResponse.
  • getNextPageParam Optional function returning the next page param. Return undefined, null, or false to stop.
  • getPreviousPageParam Optional function returning the previous page param. Return undefined, null, or false to stop.
  • source, immediate, initialData, retry, retryDelay follow the same ideas as useQuery.

Returns

  • pages / data Ordered page data.
  • pageParams Params used for each page.
  • status, fetchStatus, responseStatus Query lifecycle, transport, and HTTP status.
  • isPending, isLoading, isFetching, isFetchingNextPage, isFetchingPreviousPage, isError.
  • hasNextPage True when another page param is available.
  • hasPreviousPage True when a previous page param is available.
  • fetch() Resets to the first page.
  • fetchNextPage() Appends the next page.
  • fetchPreviousPage() Prepends the previous page.

Example:

import { createApiAgent, useInfiniteQuery } from '@pastweb/tools';

const agent = createApiAgent({ pagination: true });

const posts = useInfiniteQuery({
  initialPageParam: 1,
  fn: page => agent.get(`/api/posts?_page=${page}&_limit=20`, {
    queryKey: ['posts', page],
  }),
});

await posts.fetchNextPage();
await posts.fetchPreviousPage();

console.log(posts.pages);
console.log(posts.hasNextPage);
console.log(posts.hasPreviousPage);

If page param resolvers are omitted, useInfiniteQuery uses the pagination object attached by createApiAgent when available. For cursor APIs, pass custom resolvers:

const feed = useInfiniteQuery({
  initialPageParam: 'first',
  fn: cursor => agent.get(`/api/feed?cursor=${cursor}`),
  getNextPageParam: lastPage => lastPage.data.nextCursor,
  getPreviousPageParam: firstPage => firstPage.data.previousCursor,
});

useQueries

Creates multiple useQuery instances and returns a reactive aggregate object.

Syntax
function useQueries<T extends readonly QueryConfig<any>[]>(
  config: { queries: T } | T
): UseQueriesInfo<T>

Returns

  • queries Child QueryInfo objects in input order.
  • data Current child query data in input order.
  • status Aggregate lifecycle status (error wins, then pending, then success).
  • fetchStatus Aggregate fetch status (fetching if any child is fetching).
  • responseStatuses Current child HTTP response statuses in input order.
  • isPending True when at least one child query has not produced its first successful real response.
  • isLoading True when at least one child query is loading its first response.
  • isFetching True when at least one child query is fetching.
  • isError True when at least one child query is in an error state.
  • errors Current child query errors in input order.
  • isPlaceholderData True when at least one child query is using placeholder data.
  • fetch() Manually triggers all child queries.

Example:

import { createApiAgent, ref, useQueries } from '@pastweb/tools';

const agent = createApiAgent();
const page = ref(1);

const dashboard = useQueries({
  queries: [
    {
      fn: () => agent.get('/api/users', {
        queryKey: ['users'],
        select: data => data.items,
      }),
      retry: 2,
    },
    {
      fn: () => agent.get(`/api/posts?page=${page.value}`, {
        queryKey: ['posts', page.value],
      }),
      source: page,
    },
  ],
});

console.log(dashboard.isFetching);
console.log(dashboard.data[0]); // users
console.log(dashboard.data[1]); // posts for current page

await dashboard.fetch(); // refetches all child queries

useMutation

Creates a reactive mutation that executes the provided function and updates state with lifecycle hooks.

Syntax
function useMutation<T>(config: MutationConfig<T>): MutationInfo<T>

Parameters

  • config: MutationConfig<T> The configuration for the mutation.
  • fn: (...args: any[]) => Promise<AxiosResponse<T>> The mutation function, typically agent.post, agent.put, or agent.delete from createApiAgent.
  • onMutate?: (...args: any[]) => Promise<void> | void (optional) (default: noop) Called before the mutation executes, with the same arguments as mutate.
  • onSuccess?: (...args: any[]) => Promise<void> | void (optional) (default: noop) Called after a successful mutation or error in the finally block, with the same arguments as mutate.
  • onError?: (...args: any[]) => Promise<void> | void (optional) (default: noop) Called if the mutation fails, with the error object.
  • initialData?: T (optional) (default: null) Initial data to set before the first mutation. Sets isPlaceholderData to true until a mutation completes.
  • retry?: boolean | number | ((failureCount: number, error: unknown) => boolean) (optional) (default: false) Retries failed mutation executions. true retries up to 3 times, a number retries that many times, and a function decides per failure.
  • retryDelay?: number | string | ((failureCount: number, error: unknown) => number) (optional) (default: 0) Delay before each retry. Numbers are milliseconds, strings use stringToMs duration syntax such as '1s', and functions return milliseconds.

Returns

  • MutationInfo<T> A reactive object with mutation state and methods.
  • data: T | null The response data or initialData.
  • isPending: boolean True during mutation execution.
  • isMutating: boolean Alias for isPending.
  • isError: boolean True if an error occurred.
  • error: any The error object, if any.
  • isPlaceholderData: boolean True if data is initialData.
  • mutate: (...args: any[]) => Promise<void> Executes the mutation with provided arguments.

The useMutation function creates a reactive mutation object for operations like POST, PUT, or DELETE requests. It supports variadic arguments for mutate and fn, allowing flexible payloads. Lifecycle hooks (onMutate, onSuccess, onError) enable custom logic before and after mutations. The function integrates with createApiAgent’s reactive AxiosResponse, ensuring data updates with new responses or cache changes. Unlike useQuery, mutations are triggered manually via mutate, not reactively.

Example:

import { createApiAgent, useMutation } from '@pastweb/tools';

// Create an API agent
const agent = createApiAgent({
  headers: { 'Content-Type': 'application/json' },
});

// Basic mutation
const mutation = useMutation({
  fn: (data: any) => agent.post('/api/users', data),
});

await mutation.mutate({ name: 'John' });
console.log(mutation.data); // { id: 1, name: 'John' }
console.log(mutation.isMutating); // false

// Mutation with lifecycle hooks
const createUser = useMutation({
  fn: (data: any) => agent.post('/api/users', data),
  onMutate: async (data) => {
    console.log('Starting mutation with:', data);
  },
  onSuccess: async (data) => {
    console.log('Mutation succeeded with:', data);
  },
  onError: async (error) => {
    console.error('Mutation failed:', error);
  },
});

await createUser.mutate({ name: 'Jane' });

// Mutation with retry support
const saveUser = useMutation({
  fn: (data: any) => agent.post('/api/users', data),
  retry: 2,
  retryDelay: 500,
});

await saveUser.mutate({ name: 'Grace' });

// Mutation with initialData
const updateUser = useMutation({
  fn: (data: any) => agent.put('/api/users/1', data),
  initialData: { id: 1, name: 'Placeholder' },
});

console.log(updateUser.data); // { id: 1, name: 'Placeholder' }
console.log(updateUser.isPlaceholderData); // true
await updateUser.mutate({ name: 'Updated' });
console.log(updateUser.isPlaceholderData); // false 

createAsyncStore

Creates an asynchronous store with the given options. Useful to be extended for async initialisation of application state manager like redux or pinia if needs to get initialisation data from async resources as indexedDB.

Syntax
function createAsyncStore<T>(options: AsyncStoreOptions): T;

Parameters

  • options: AsyncStoreOptions
    • The options for creating the asynchronous store.
    • storeName: string
      • The name of the store. This is required for better error debugging.
    • timeout: number (optional, default: 20000)
      • The timeout limit in milliseconds for initializing the store.

Returns

  • T
    • The created asynchronous store.

Throws

  • Will throw an error if the storeName option is not set.

Example:

import { createAsyncStore } from '@pastweb/tools';

const storeOptions = {
  storeName: 'myStore',
  timeout: 30000,
};

const myStore = createAsyncStore(storeOptions);

normalizeAsyncQueue

Normalizes an array of asynchronous operations into an array of promises.

Syntax
function normalizeAsyncQueue(wait: Wait | Wait[]): Promise<any>[];

Parameters

  • wait: Wait | Wait[]
    • A single asynchronous operation or an array of asynchronous operations. Each operation can be:
      • A promise
      • A function that returns a promise
      • An object representing an asynchronous store

Returns

  • Promise<any>[]
    • An array of promises.

Throws

  • Error
    • Throws an error if an invalid type is encountered in the wait array.

Example:

import { createAsyncStore, normalizeAsyncQueue } from '@pastweb/tools';
import type { AsyncStore } from '@pastweb/tools';

// Single promise
const singlePromise = Promise.resolve('done');
normalizeAsyncQueue(singlePromise); // [singlePromise]

// Array of promises and functions
const promise1 = Promise.resolve('done');
const promise2 = () => Promise.resolve('done');
normalizeAsyncQueue([promise1, promise2]); // [promise1, promise2()]

// Async store
const asyncStore = createAsyncStore<AsyncStore<any>>({
  name: 'UserSessionStore',
  async onInit() {
    await fetch('/api/session');
    asyncStore.setStoreReady();
  },
});

// Because asyncStore is not ready yet, normalizeAsyncQueue calls asyncStore.init()
// and returns asyncStore.isReady in the normalized promise list.
const queue = normalizeAsyncQueue([
  Promise.resolve('config-loaded'),
  () => fetch('/api/profile'),
  asyncStore,
]);

await Promise.all(queue);
console.log(asyncStore.isStoreReady); // true

Remarks The normalizeAsyncQueue function is designed to handle various asynchronous operations and normalize them into a uniform array of promises. This is particularly useful when dealing with mixed asynchronous workflows, ensuring that all operations can be awaited in a consistent manner.

This function supports:

  • Promises
  • Functions returning promises
  • Asynchronous stores

If an asynchronous store is passed in, the function will check if the store is ready. If it is not, the init method of the store will be called to prepare it.

SSR utilities (experimental)

All SSR coordination helpers are experimental and grouped under ssrUtils:

import {
  createSSRTracker,
  registerAsyncTask,
  resolveAsyncTasks,
  runSSRCycle,
} from '@pastweb/tools/ssrUtils';

They are also re-exported from the package root for convenience.


registerAsyncTask / resolveAsyncTasks

Utility for registering and later resolving asynchronous work during SSR.

These helpers are designed so that code running during the initial (collection) render pass can declare async work that should happen afterwards. The resolution step uses an iterative loop so that work discovered while executing earlier tasks (e.g. from nested async components) is also handled.

Syntax
function registerAsyncTask(fn: () => Promise<any>): void;
async function resolveAsyncTasks(): Promise<void>;

registerAsyncTask only has effect on the server. The provided function is queued and will be executed when resolveAsyncTasks is called.

resolveAsyncTasks drains the queue iteratively. Any errors thrown by individual tasks are caught, logged, and do not prevent the remaining tasks from running.

Example:

import { registerAsyncTask, resolveAsyncTasks } from '@pastweb/tools/ssrUtils';

// During a server-side collection / dry render
registerAsyncTask(async () => {
  const data = await fetchCriticalData();
  // side effects or store the result for later use
});

// Later, after the collection render
await resolveAsyncTasks();

This pattern is useful when you need to discover asynchronous work (such as data loading inside components) during render, then wait for it in a second phase without making the component function itself asynchronous.

runSSRCycle calls resolveAsyncTasks between two collection renders — see below.


runSSRCycle

Framework-agnostic SSR orchestrator for SSR router and other SSR entry points. Runs the full collection → prefetch → render cycle with SSR tracker integration and hybrid static downgrade.

Syntax
function runSSRCycle(options: RunSSRCycleOptions): Promise<SSRCycleResult>

Phase order: collect #1 → resolveAsyncTasks → collect #2 → dehydratehydrate → final render → (optional) dynamic downgrade rerender.

Key options:

  • render — async function receiving { isStatic, phase, apiDehydratedState }.
  • queryCache — shared cache; resetForSSR() runs before collection by default.
  • resolveAsyncTasks — loads async components registered during collection.
  • shouldAttemptStatic — pre-classify static intent; tracker may force downgrade.
  • onStaticProven / onDynamicDowngrade — hooks for SSR router manifest writes.

Example:

import { createQueryCache } from '@pastweb/tools';
import { runSSRCycle, resolveAsyncTasks } from '@pastweb/tools/ssrUtils';

const queryCache = createQueryCache();

const { html, snapshot, fingerprint, downgraded } = await runSSRCycle({
  route: '/shop',
  queryCache,
  resolveAsyncTasks,
  shouldAttemptStatic: true,
  render: async ({ isStatic, phase, apiDehydratedState }) => {
    return pageRender({ router, isStatic, phase, apiDehydratedState });
  },
  onStaticProven: ({ html, snapshot, fingerprint }) => {
    writeStaticHtml(html);
    persistManifest({ fingerprint, snapshot });
  },
});

See ../PLAN.md and QUERY.md for hybrid render + partial hydration architecture.


SSR tracker (createSSRTracker)

Standalone server-side tracker for hybrid render decisions. Installed during runSSRCycle; agent.get({ ssrMode }) reports via reportApiSSRToTracker.

Syntax
function createSSRTracker(options?: SSRTrackerOptions): SSRTracker
function setCurrentSSRTracker(tracker?: SSRTracker): void
function getCurrentSSRTracker(): SSRTracker | undefined
function clearCurrentSSRTracker(): void
function reportApiSSRToTracker(url: string, options?: { queryKey?; ssrMode?; ssrRevalitate? }): void
function createDependencyFingerprint(snapshot: SSRTrackerSnapshot): string

agent.get SSR option (ApiSSRMode): 'auto' | 'static' | 'dynamic' | 'no-store'

await agent.get('/api/posts', {
  queryKey: ['posts'],
  ssrMode: 'static',
  ssrRevalitate: '5m',
});
  • static / auto → registers a static-safe API dependency on the tracker.
  • dynamic / no-store → marks the render as dynamic (blocks static HTML persistence).

Use createDependencyFingerprint(tracker.snapshot()) to key persisted apiCache entries in SSR router.

Partial hydration: combine sliceDehydratedState(snapshot, islandQueryKeys) with framework Island components — hydrate the slice before island hydrateRoot.


createAsyncMicroStore

Create an async wrapper around creteMicroStoreCollector with an onInit async function to pass into the options, in case your store/s need to be initialized with data from async resources as example indexedDB.

Syntax
function createAsyncMicroStore(options: MicroCollectorStoreOptions): MicroAsyncStore

Returns

  • MicroAsyncStore
    • An object containing stores hooks to interact with the micro stores.

Example:

import { createAsyncMicroStore } from '@pastweb/tools';
import { useCounterStore, useUserStore, useThemeStore } from '.../somewhere';

const useAsyncStores = createAsyncMicroStore({
  name: 'appStores',
  stores: [useCounterStore, useUserStore, useThemeStore],
  timeout: 15000,
  onInit: async (collectedStores) => {
    console.log('Initializing stores...', Object.keys(collectedStores));
    // Example: load user data after stores are collected
    await fetchInitialData(collectedStores.user);
  },
});

// Usage in app bootstrap
await useAsyncStores.init();

// Now safe to use stores
const counter = useAsyncStores.store.counter();
const user = useAsyncStores.store.user();

// Or wait for readiness
await useAsyncStores.isReady;

createEventEmitter

Creates an event emitter that allows subscribing to events, emitting events, and removing event listeners. It allows you to create a custom event system where you can emit events, subscribe to events with callback functions, and remove event listeners. Each listener is assigned a unique key, which is used to manage and remove listeners efficiently.

Syntax
function createEventEmitter(): EventEmitter;

Returns

  • EventEmitter
    • An object containing methods to interact with the event emitter.

Methods

  • emit(eventName: string, ...args: any[]): void

    • Emits an event, calling all subscribed event listeners with the provided arguments.
    • eventName: string
      • The name of the event to emit.
    • ...args: any[]
      • Arguments to pass to the event listeners.
  • on(eventName: string, eventCallback: EventCallback): RemoveListener

    • Subscribes an event listener to a specific event.
    • eventName: string
      • The name of the event to subscribe to.
    • eventCallback: EventCallback
      • The callback function to execute when the event is emitted.
    • Returns: RemoveListener
      • An object with a removeListener method to unsubscribe from the event.
  • removeListener(eventCallbackKey: symbol): void Removes an event listener using its unique key.

    • eventCallbackKey: symbol
      • The unique key for the event callback to remove.

Example:

import { createEventEmitter } from '@pastweb/tools';

const emitter = createEventEmitter();
const listener = emitter.on('event', (data) => console.log(data));
emitter.emit('event', 'Hello, World!');
listener.removeListener();

createLangAsyncStore

Creates a language asynchronous store with i18next integration for managing translations. The createLangAsyncStore function provides a flexible way to manage multiple languages in your application using i18next with async initialisation, in case ,as example you need to initialise the store getting or setting data to an async resource indexedDB. It supports:

  • Initialization with an initial language.
  • Dynamic support for multiple languages.
  • Integration with translation resources.
  • Custom plugins for i18next. The store is asynchronous and ensures that the language settings and resources are ready before allowing operations like language switching. It is designed to work seamlessly with both synchronous and asynchronous workflows.
Syntax
function createLangAsyncStore(options: LangOptions): LangAsyncStore;

Parameters

  • options: LangOptions
    • Configuration options for the language store. This includes initial language settings, supported languages, translation resources, and additional i18next options.

Returns

  • LangAsyncStore
    • The created language store, which integrates i18next and provides methods for managing translations and changing the language.

Methods and Properties

  • store.i18n: i18n

    • The i18next instance used for managing translations.
  • store.supported: string[] | Promise<string[]>

    • An array of supported languages. If an asynchronous function is provided, it returns a promise that resolves with the supported languages.
  • store.current: Promise<string>

    • A promise that resolves with the current language.
  • store.t: TFunction

    • A translation function provided by i18next.
  • store.changeLanguage(lng: string | undefined, callback?: Callback): Promise<TFunction<'translation', undefined>>

    • Changes the current language of the store and triggers any specified callback or the store's onLangChange function.

    • lng: string | undefined

      • The language code to switch to.
    • callback: Callback

      • An optional callback function that is called after the language is changed.
    • Returns: Promise<TFunction<'translation', undefined>>

      • A promise that resolves with the i18next translation function after the language is changed.

Example:

$ npm i -S i18next
import { createLangAsyncStore } from '@pastweb/tools/createLangAsyncStore';

const langStore = createLangAsyncStore({
  initLang: 'en',
  supported: ['en', 'fr', 'es'],
  translations: { en: { translation: { key: 'value' } } },
  i18n: { fallbackLng: 'en' },
});

langStore.changeLanguage('fr').then((t) => {
  console.log(t('key')); // Outputs the translation for 'key' in French
});

createMatchSchemeAsyncStore
Syntax
function createMatchSchemeAsyncStore(options?: SchemeOptionsAsyncStore): ColorSchemeAsyncStore;

Description

Creates an asynchronous store for managing color schemes.
This function initializes an asynchronous store specifically for handling
color scheme preferences and system theme detection. It integrates with
createMatchScheme to track and manage color mode changes.

Parameters

options (optional)

Type: SchemeOptionsAsyncStore
Configuration options for the asynchronous store.

Property Type Default Description
storeName string "ColorSchemeStore" The name of the store.
datasetName string | false false The dataset attribute name for storing the color scheme. If false, it uses CSS class names instead.
defaultMode string "auto" The default mode ('auto', 'light', or 'dark').
initStore (matchScheme: MatchScheme) => Promise<void> noop An asynchronous function that runs during store initialization.

Returns

Type: ColorSchemeAsyncStore
An object that provides methods and properties for managing color schemes asynchronously.

Properties

matchScheme

Type: MatchScheme
Manages color scheme detection and provides methods to get or change the scheme.

init

Type: () => void
A no-op function for initialization.

setStoreReady

Type: () => void
Marks the store as ready after initialization.

Example Usage

const colorSchemeStore = createMatchSchemeAsyncStore({
  defaultMode: 'auto',
  datasetName: 'theme',
  initStore: async (matchScheme) => {
    console.log('Initializing with:', matchScheme.getInfo());
  }
});

debounce

Creates a debounced function that delays invoking fn until after timeout milliseconds have elapsed since the last time the debounced function was invoked. The debounced function includes methods cancel and flush to cancel delayed invocation and to immediately invoke them, respectively.

Syntax
function debounce(fn: DebouceCallback, timeout?: number): DebouceCallback;

Parameters

  • fn: DebouceCallback
    • The function to debounce.
  • timeout: number (optional, default: 300)
    • The number of milliseconds to delay.

Returns

  • DebouceCallback

Example:

import { debounce } from '@pastweb/tools';

const debouncedLog = debounce((msg: string) => console.log(msg), 500);

debouncedLog('Hello');  // Will log 'Hello' after 500 milliseconds if not called again within this time.
debouncedLog.cancel();  // Cancels the delayed invocation.
debouncedLog.flush();   // Immediately invokes the delayed function.

throttle

Returns a throttle function defined in the fn parameter, which is executed for each timeout passed as the second parameter. The returned throttle function includes two members:

  • cancel: A function to stop the throttling of the function.
  • flush: A function to flush the timeout.
Syntax
function throttle(fn: ThrottleCallback, timeout?: number): ThrottleCallback;

Parameters

  • fn: ThrottleCallback
    • The function to run.
  • timeout: number (optional, default: 300)
    • The timeout gap in milliseconds.

Returns

  • ThrottleCallback
    • The throttle callback function. Example:
import { throttle } from '@pastweb/tools';

const throttledLog = throttle((msg: string) => console.log(msg), 500);

throttledLog('Hello');  // Will log 'Hello' immediately.
throttledLog('World');  // Will not log 'World' if called within 500 milliseconds.
throttledLog.cancel();  // Cancels the throttling.
throttledLog.flush();   // Flushes the timeout, allowing the function to be invoked immediately.

Browser functions

createMatchDevice

Creates a utility for detecting and managing device types based on user agent strings and media queries. The createMatchDevice function is designed to help detect device types based on user agent strings and media queries. This utility is particularly useful for responsive design and ensuring that your application behaves differently depending on the device being used.

  • Device Detection: The utility supports both user agent string matching and media query matching to determine device types.
  • Server-Side Rendering (SSR): If server-side rendering is detected (use isServer from the envs constants; isSSR is deprecated), user agent-based detection is used, and media query-based detection is skipped.
  • Dynamic Updates: The utility can respond to changes in media query matches, allowing dynamic updates to the device state.
  • Event Emitter: The underlying event emitter allows you to listen for specific device match changes, enabling reactive design and behavior changes.
Syntax
function createMatchDevice(config: DevicesConfig = {}): MatchDevice;

Parameters

  • config: DevicesConfig
    • An optional configuration object that maps device names to their detection criteria. Each device's configuration can include a user agent test and/or a media query.

Returns

  • MatchDevice
    • An object with methods for getting the current matched devices, setting change listeners, and listening for specific device match events.

Methods

  • getDevices(): MatchDevicesResult

    • Returns an object representing the current state of device matches. Each key in the object corresponds to a device name, and the value is a boolean indicating whether the device matches the criteria.
  • onChange(fn: (devices: MatchDevicesResult) => void): void

    • Sets a callback function to be executed whenever the device match state changes. The callback receives an updated MatchDevicesResult object.
    • fn: (devices: MatchDevicesResult) => void
      • The callback function to be called on device state change.
  • onMatch(isDeviceName: string, fn: (result: boolean, deviceName: string) => void): void

    • Sets a listener for a specific device match event. The callback is triggered whenever the specified device's match state changes.
    • deviceName: string
      • The name of the device to listen for.
    • fn: (result: boolean, isDeviceName: string) => void
      • The callback function to be called when the device match event occurs.

Example:

import { createMatchDevice } from '@pastweb/tools';

const deviceConfig = {
  mobile: {
    userAgent: /Mobile|Android/i,
    mediaQuery: '(max-width: 767px)',
  },
  tablet: {
    mediaQuery: '(min-width: 768px) and (max-width: 1024px)',
  },
};

const matchDevice = createMatchDevice(deviceConfig);

matchDevice.onChange((devices) => {
  console.log('Device states updated:', devices);
});

matchDevice.onMatch('mobile', (deviceName) => {
  console.log('Mobile device match changed:', deviceName);
});

const currentDevices = matchDevice.getDevices();
console.log('Current matched devices:', currentDevices);

Here is the Markdown documentation in the same style as the "isType" function documentation:


createMatchScheme
Syntax
function createMatchScheme(options?: SchemeOptions): MatchScheme;

Description

Creates a match scheme manager that allows setting and tracking the color scheme mode.
It detects system preferences, provides methods to update the mode, and notifies listeners of changes.

Parameters

options (optional)

Type: SchemeOptions
An object containing configuration options for the match scheme.

Property Type Default Description
defaultMode string "auto" The initial color mode: 'auto', 'light', or 'dark'.
datasetName string | false false The dataset attribute name used to store the color scheme in the root element. If false, it uses CSS class names instead.

Returns

Type: MatchScheme
An object with methods to manage and listen to scheme changes.

Methods

getInfo
getInfo(): { mode: string; system: string; selected: string };

Description:
Retrieves the current color scheme information.

Returns:

Property Type Description
mode string The currently set mode ('auto', 'light', or 'dark').
system string The system's detected color scheme ('light' or 'dark').
selected string The active mode (either mode or the detected system scheme if mode is 'auto').

Example:

const scheme = createMatchScheme();
console.log(scheme.getInfo()); 
// { mode: 'auto', system: 'light', selected: 'light' }
setMode
setMode(mode: string): void;

Description:
Updates the color mode. If 'auto' is selected, the mode will follow the system's preference.

Parameters:

Name Type Description
mode string The new mode: 'auto', 'light', or 'dark'.

Example:

scheme.setMode('dark'); 
console.log(scheme.getInfo()); 
// { mode: 'dark', system: 'light', selected: 'dark' }
onModeChange
onModeChange(fn: (mode: string) => void): void;

Description:
Registers a callback that is triggered when the mode changes.

Parameters:

Name Type Description
fn (mode: string) => void A callback function that receives the new mode.

Example:

scheme.onModeChange((mode) => {
  console.log(`Mode changed to: ${mode}`);
});
scheme.setMode('light'); 
// Logs: "Mode changed to: light"
onSysSchemeChange
onSysSchemeChange(fn: (mode: string) => void): void;

Description:
Registers a callback that triggers when the system's preferred color scheme changes.

Parameters:

Name Type Description
fn (mode: string) => void A callback function that receives the new system scheme ('light' or 'dark').

Example:

scheme.onSysSchemeChange((systemMode) => {
  console.log(`System scheme changed to: ${systemMode}`);
});

Example Usage

const scheme = createMatchScheme({ defaultMode: 'auto', datasetName: 'theme' });

console.log(scheme.getInfo()); 
// { mode: 'auto', system: 'dark', selected: 'dark' }

scheme.onModeChange((mode) => {
  console.log(`Color mode changed to: ${mode}`);
});

scheme.setMode('light');
// Logs: "Color mode changed to: light"

useColorScheme
Syntax
function useColorScheme(options?: SchemeOptions, matchScheme?: MatchScheme): [ColorSchemeInfo, (mode: string) => void];

Description

Hook that returns a reactive [ColorSchemeInfo, setMode] tuple. It uses the library's reactivity primitives so the info stays up-to-date when the scheme changes (observable via effect or computed).

If matchScheme is provided it is used directly; otherwise createMatchScheme(options) is called internally to create one.

Parameters

options (optional)

Type: SchemeOptions
Same options as createMatchScheme (used only when no matchScheme is passed).

matchScheme (optional)

Type: MatchScheme
An existing MatchScheme instance. If omitted, one is created for you.

Returns

Type: [ColorSchemeInfo, (mode: string) => void]
A tuple where:

  • The first element is a reactive ColorSchemeInfo (its properties update automatically).
  • The second element is the setMode function (delegated to the underlying scheme).

Example:

const [scheme, setMode] = useColorScheme({ defaultMode: 'auto' });

effect(() => {
  console.log('Selected color scheme:', scheme.selected);
});

setMode('dark');
// scheme.selected will now be 'dark' (and effects re-run)

createStorage

Creates a versatile storage utility that supports both IndexedDB and localStorage. This utility allows for custom storage handling, default settings, and hooks for various operations.

Syntax
function createStorage(config: StorageConfig = {}): Storage;

Parameters

  • config: StorageConfig
    • An object containing configuration options for the storage utility. The available options include:
    • dbName: string (optional)
      • The name of the database when using IndexedDB. Default is 'storage'.
    • storeName: string (optional)
      • The name of the object store within the database when using IndexedDB. Default is 'storage'.
    • type: 'indexedDB' | 'localStorage' (optional)
      • The type of storage to use. Defaults to 'indexedDB' if supported; otherwise, it falls back to 'localStorage'.
    • defaultSettings: Record<string, any> (optional)
      • An object representing default settings to be applied when the store is first created.
    • onSet: Record<string, (storage: Storage, value: any, store: boolean) => Promise<any>> (optional)
      • Hooks to run custom logic when a value is set in the store.
    • onGet: Record<string, (storage: Storage, value: any) => Promise<any>> (optional)
      • Hooks to run custom logic when a value is retrieved from the store.
    • onRemove: Record<string, (storage: Storage, path: string, justLocalStorage: boolean) => Promise<void>> (optional)
      • Hooks to run custom logic when a value is removed from the store.

Returns

  • Storage
    • An object with methods for interacting with the storage, including getting, setting, and removing data.

Methods

  • storage.get(path: string): Promise<any> Retrieves a value from the storage.

    • path: string
      • The path to the value in the storage.
    • Returns: Promise<any>
      • A promise that resolves to the stored value.
  • storage.set(path: string, value: any, store = false): Promise<void> Sets a value in the storage.

    • path: string
      • The path to store the value at.
    • value: any
      • The value to store.
    • store: boolean (optional)
      • Whether to store the value in the underlying storage (e.g., IndexedDB or localStorage). Default is false.
    • Returns: Promise<void>
      • A promise that resolves once the value is set.
  • storage.remove(path: string, justLocalStorage = false): Promise<void> Removes a value from the storage.

    • path: string
      • The path to remove.
    • justLocalStorage: boolean (optional)
      • Whether to only remove the value from local storage. Default is false.
    • Returns: Promise<void>
      • A promise that resolves once the value is removed.
  • storage.isStored(path: string): boolean Checks if a specific path is stored in the storage.

    • path: string
      • The path to check.
    • Returns: boolean
      • True if the path is stored, false otherwise.
  • storage.isStoreReady: Promise<true>

    • A promise that resolves when the storage is fully initialized and ready to be used.

Example:

import { } from '@pastweb/tools';

const storage = createStorage({
  dbName: 'myDatabase',
  storeName: 'myStore',
  type: 'indexedDB',
  defaultSettings: { theme: 'dark' },
 });
 
 // Set a value in storage
 await storage.set('theme', 'light');
 
 // Get a value from storage
 const theme = await storage.get('theme');
 
 // Remove a value from storage
 await storage.remove('theme');

Date and Time

isDateYoungerOf

The isDateYoungerOf function checks whether a given date is younger (i.e., more recent) than a specified duration. The duration is provided as a string composed of multiple time components such as years, months, days, hours, minutes, and seconds.

Syntax
function isDateYoungerOf(date: Date, duration: string): boolean;

Parameters

  • date: Date
    • The date object to be checked against the specified duration.
  • duration: string
    • A string representing the duration composed of various time units:
      • Y for years
      • M for months
      • D for days
      • h for hours
      • m for minutes
      • s for seconds
  • The string can contain multiple components, e.g., "2Y3M1D" for 2 years, 3 months, and 1 day.

Returns

  • boolean:
    • Returns true if the given date is younger than the specified duration relative to the current date and time. Returns false otherwise.

Example:

import { isDateYoungerOf } from '@pastweb/tools';

const date = new Date();
date.setDate(date.getDate() - 1); // 1 day ago
console.log(isDateYoungerOf(date, '1D')); // Output: true
console.log(isDateYoungerOf(date, '2D')); // Output: true
console.log(isDateYoungerOf(date, '12h')); // Output: false

Edge Cases

  • Past and Future Dates: The function checks the date against the current date and time, so it works for both past and future dates relative to now.
  • Zero or Negative Durations: If the duration components result in zero or negative values, the function will consider the date as not younger and will return false.

stringToMs

The stringToMs function converts a duration string into milliseconds. It accepts the same unit format as isDateYoungerOf, making it useful for timers, cache delays, and any logic that needs a numeric duration rather than a date comparison.

Syntax
function stringToMs(duration: string): number;

Parameters

  • duration: string
    • A string representing the duration composed of various time units:
      • Y for years (average: 365.25 days)
      • M for months (average: 30.436875 days)
      • D for days
      • h for hours (case-insensitive)
      • m for minutes
      • s for seconds
    • Components can appear in any order and be combined, e.g. "2Y3M1D2h30m45s" or "5m".

Returns

  • number:
    • The total duration in milliseconds. The result is floored to an integer. Returns 0 for empty, whitespace-only, or unparseable strings.

Example:

import { stringToMs } from '@pastweb/tools';

console.log(stringToMs('1s'));              // 1000
console.log(stringToMs('5m'));              // 300000
console.log(stringToMs('1D'));              // 86400000
console.log(stringToMs('2Y3M1D2h30m45s'));  // combined total in ms
console.log(stringToMs(''));                // 0

Notes *

Keywords