Skip to main content

@xaiku/shared

Low-level utilities and helpers used by all other Xaiku packages. This package provides storage adapters, event listeners, performance observers, function proxying, GUID generation, and environment detection.

tip

Most users will never import from @xaiku/shared directly. These utilities are consumed internally by @xaiku/core, @xaiku/browser, and other packages. This reference is intended for contributors and advanced integrations.

Installation

npm install @xaiku/shared

Exports

makeStorage(sdk)

Creates a storage adapter based on the SDK's options.store configuration.

import { makeStorage } from '@xaiku/shared';

const storage = makeStorage(sdk);
storage.set('key', { data: true });
storage.get('key'); // { data: true }
storage.delete('key');

Resolution order:

  1. If store.custom is a valid store (has get, set, delete), use it.
  2. Create a store from store.name — one of 'cookie', 'localStorage', 'sessionStorage', or 'memory'.
  3. If the store is not supported in the environment, fall back to 'memory'.

Also exported:

ExportDescription
storesObject containing all store factory functions: cookie, localStorage, sessionStorage, memory.
storeNamesObject mapping store keys to their internal name strings.

makeListeners()

Creates an event emitter instance.

import { makeListeners } from '@xaiku/shared';

const listeners = makeListeners();

Returns:

MethodSignatureDescription
on(eventName, handler) => offSubscribe to an event. Returns an unsubscribe function with a chainable .on() method.
once(eventName, handler) => offSubscribe once — the handler is removed after the first call.
off(eventName, handler) => voidRemove a specific handler.
trigger(eventName, ...args) => voidEmit an event to all subscribers.
offAll() => voidRemove all listeners for all events.

makeTrack(sdk)

Creates a tracking system that buffers metrics and flushes them to the API periodically or on page unload.

import { makeTrack } from '@xaiku/shared';

const track = makeTrack(sdk);

Returns:

PropertyTypeDescription
addMetric(metric) => voidAdd a metric to the buffer. Triggers a flush when the batch size is reached.
flush(isUnload?) => voidFlush all buffered metrics. Uses navigator.sendBeacon when isUnload is true.
destroy() => voidFlush remaining metrics, remove listeners, and stop the flush timer.
eventsobjectTracking event helpers. See Track Events.

Behavior:

  • Buffers metrics and flushes every 5 seconds or when 5 metrics accumulate.
  • Listens to the SDK metric:report event to enrich metrics with client attributes, pathname, referrer, and configured search params.
  • Flushes via sendBeacon on page unload.

Track Events

The events object provides convenience methods for common tracking scenarios:

MethodSignatureDescription
trackView(extraData?) => voidTrack a variant impression.
trackClick(extraData?) => voidTrack a click on a variant.
trackConversion(extraData?) => voidTrack a conversion event.
trackTimeToConversion(impressionTimestamp, extraData?) => voidTrack time from impression to conversion.
trackDwellTime(extraData?) => { start, stop }Returns start/stop functions for measuring time on page.
trackScrollDepth(extraData?) => voidTrack maximum scroll depth (reported on beforeunload).
trackBounce(extraData?) => voidDetect and report bounces (under 5 seconds).
trackHover(element, extraData?) => voidTrack hover duration on a DOM element.
trackFeedback(rating, extraData?) => voidTrack user feedback/sentiment.
trackPerformanceMetrics(extraData?) => voidCapture page load timing.
trackError(error, extraData?) => voidTrack a JavaScript error.
trackNumericMetric(metricName, value, extraData?) => voidRecord an arbitrary numeric value.

makeExperiments(sdk)

Asynchronous function that augments the SDK instance with experiment and variant management.

import { makeExperiments } from '@xaiku/shared';

await makeExperiments(sdk);

const experiments = await sdk.getExperiments();
const variant = sdk.getVariant('experiment_id');

Methods added to sdk:

MethodSignatureDescription
setExperiments(experiments) => voidPersist experiments to storage.
getExperiments(ids?, options?) => Promise<object>Fetch experiments from the API or storage. Pass { force: true } to bypass cache.
getVariants() => object | nullGet all selected variants.
setVariants(variants) => voidPersist variant selections.
selectVariants(experiments, options?) => objectRun variant selection for each experiment. Pass { force: true } to re-select.
getVariant(experimentId, options?) => object | nullGet the selected (or control) variant for a experiment. Pass { control: true } for the control variant.
getVariantId(experimentId) => string | nullGet the selected variant ID for a experiment.
getVariantText(experimentId, partId, options?) => string | nullGet text content for a specific part of a variant. Pass { control: true } for the control version.

makePerformanceObserver(trigger, options?)

Creates a wrapper around the browser PerformanceObserver API that connects to all supported entry types and emits events through the provided trigger function.

import { makePerformanceObserver } from '@xaiku/shared';

const pos = makePerformanceObserver(sdk.trigger);
pos.connect();

pos.disconnect();

Returns:

MethodSignatureDescription
connect(key?) => voidStart observing. Pass a specific entry type or omit to observe all supported types.
disconnect(key?) => voidStop observing. Pass a specific entry type or omit to disconnect all.
get(key) => PerformanceObserverGet the observer for a specific entry type.
allobjectMap of entry type to PerformanceObserver instance.

Supported entry types: element, event, first-input, largest-contentful-paint, layout-shift, longtask, mark, measure, navigation, paint, resource.

makeFnProxy(trigger)

Creates utilities for wrapping functions with a Proxy that emits lifecycle events (start, end, error) through the provided trigger function.

import { makeFnProxy } from '@xaiku/shared';

const { proxy, proxyFn } = makeFnProxy(trigger);

proxy(document, ['addEventListener', 'removeEventListener'], callback, 'dom');

Returns:

PropertyTypeDescription
canProxyFn(fn) => booleanCheck if a function can be proxied (not already proxied, is a function).
proxy(obj, fnNames, callback?, context?) => voidProxy one or more methods on an object.
proxyFn(name, fn, callback?, context?, obj?) => ProxyProxy a single function.
xaikuFnSymbolSymbolSymbol used to mark already-proxied functions.
proxyStatesobject{ start: 'start', end: 'end', error: 'error' }

getGuid(sdk)

Retrieves or generates a GUID (UUID v4 format) for the current visitor. If a GUID exists in storage, it is reused. Otherwise, a new one is generated from sdk.options.guid (deterministic hash) or randomly.

import { getGuid } from '@xaiku/shared';

const guid = getGuid(sdk);

Also exported:

ExportDescription
guidStorageKeyThe storage key string ('__xaiku__guid__') used to persist the GUID.

parsePublicKey(sdk)

Validates the public key on the SDK instance and resolves the API URL.

import { parsePublicKey } from '@xaiku/shared';

const { apiUrl, token } = parsePublicKey(sdk);

Throws an error if pkey is missing or does not start with pk_.

Returns:

PropertyTypeDescription
apiUrlstringResolved API URL based on dev and proxyApiUrl options.
tokenstringThe key with the pk_ prefix removed.

isTestMode()

Returns true when localStorage contains xaiku_test set to 'true'.

import { isTestMode } from '@xaiku/shared';

if (isTestMode()) {
console.log('Running in test mode');
}

isBrowser()

Returns true when running in a browser environment (window and document both exist).

import { isBrowser } from '@xaiku/shared';

if (isBrowser()) {
...
}

hasDocument()

Returns true when document is defined.

import { hasDocument } from '@xaiku/shared';

if (hasDocument()) {
...
}