npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@guidelytech/sdk-core

v0.9.0

Published

Framework-independent Guidely browser SDK

Readme

@guidelytech/sdk-core

Framework-independent Guidely browser SDK: loads the published configuration for an environment, identifies users, tracks events, evaluates targeting/trigger/frequency rules and renders guides through @guidelytech/guide-runtime. Framework adapters (@guidelytech/react, @guidelytech/nextjs, @guidelytech/svelte, @guidelytech/sveltekit) and the script-tag build (@guidelytech/embed) wrap this package.

pnpm add @guidelytech/sdk-core
import { createGuidely } from "@guidelytech/sdk-core";

const guidely = createGuidely({
  projectId: "pk_dev_demo_react",      // environment public key (not a secret)
  apiUrl: "http://localhost:3000",     // defaults to https://api.guidely.app
  environment: "development",
});

await guidely.ready();
await guidely.identify("user_123", { role: "admin", plan: "pro" }, { id: "acme", traits: { seats: 12 } });
guidely.track("contact_created", { source: "import" });
guidely.on("guide:complete", (guide) => console.log("completed", guide.key));

The module is safe to import during SSR (nothing touches window at module scope). createGuidely() throws a friendly error when called on the server.

Options

| Option | Default | Description | |---|---|---| | projectId | — | Environment public key (pk_dev_…, pk_stag_…, pk_prod_…). Required. | | environment | inferred from key prefix | development | staging | production; exposed to targeting (environment field). | | apiUrl | https://api.guidely.app | Base URL of the Guidely API. Demos use http://localhost:3000. | | debug | false | Log trigger decisions and requests with console.debug("[Guidely]", …). | | analytics | true | Send analytics events. Guides still render when false. | | consent | "granted" | "pending" buffers events (persisted) until setConsent("granted"); "denied" disables analytics. | | storage | "local" | "local" | "session" | "memory" for identity, cached config, state and queue. | | pollIntervalMs | 1000 | Idle re-check interval (min 250ms): re-arms element_visible triggers whose target appears without navigation and drains the guide queue. | | configRefreshMs | 300000 | Periodic config refresh (If-None-Match → 304 when unchanged). | | maskSelectors | [] | Elements whose text is never captured: authoring picks, click text, session replays (merged with the project setting, [data-guidely-mask] and .guidely-mask). | | autocapture | project setting | false or { pageviews?, pageLeave?, clicks?, rageClicks?, heatmaps?, mouseMovement? }: switch product-analytics autocapture off for this installation (options can only turn things off). | | release | — | App version or commit SHA (≤100 chars) sent with every exception; error stacks are resolved with the source maps uploaded for that release (@guidelytech/sourcemaps). | | captureErrors | project setting | false stops reporting uncaught errors / unhandled rejections (captureException still works while the project has error tracking on). | | sessionReplay | project setting | false never records; { maskAllText?, blockSelectors?, recordNetwork?: false, recordNetworkPayloads?: false, maskNetworkRequest? } makes recording stricter (see "Network recording"). | | onError | — | Receives every error the SDK swallows (network, rendering). | | theme | — | Overrides for the project theme (primaryColor, backgroundColor, textColor, borderRadius, fontFamily). | | user | — | { id, traits?, account? } to identify immediately. | | fetch | global | Custom fetch (tests, polyfills). | | disableAuthoring | false | Ignore ?guidely_authoring= in the URL. | | flags | on | Feature flags (same key and API URL). false turns them off; an object passes flags client options, e.g. { payload } (server-rendered bootstrap), { attributes }, { remoteEval: true }, { pollingMode: "stream" }. |

API

All methods are safe to call at any time; failures are reported through onError / the error event, never thrown into the host application.

| Method | Description | |---|---| | init(): Promise<void> / ready(): Promise<void> | Load config (cache first, then network), wire listeners, evaluate triggers. Idempotent. Resolves even when the API is unreachable (cached config is used; an error event is emitted). | | identify(userId, traits?, account?) | POST /sdk/identify; merges server state (audiences, guide history, checklist progress, submitted surveys). Local state wins when both exist. Re-evaluates triggers (segment_enter, targeting). | | setAccount({ id, traits? }) | Sets the account for the current user and re-identifies. | | updateTraits(traits) | Merges traits and re-identifies. | | reset() | New anonymous id, cleared user and local state, new session; triggers re-evaluated. | | track(name, properties?) | Custom event. Drives event triggers, advanceOn: "event" steps and checklist tasks (completion: "event"). Keys matching SENSITIVE_PROPERTY_PATTERN are stripped. | | page(url?) | Records a page_view and re-evaluates triggers. Called automatically on SPA navigation (History API patch + popstate + hashchange); adapters call it explicitly too. | | setConsent("granted" \| "denied" \| "pending") | Runtime consent changes (anything but granted stops and discards a session recording). | | captureException(error, properties?) | Reports a caught error as an exception event (source: "manual", handled: true). A no-op when the project has error tracking off; captureErrors: false does not affect it. | | startSessionRecording() / stopSessionRecording() | Force recording on for this session (ignores sampling; needs replay enabled on the project) / stop it for the rest of the session. | | isRecording() / getSessionId() | Whether the session is being recorded; the current analytics session id (also the replay id). | | startGuide(idOrKey, { stepId? }) | Starts a guide regardless of targeting/frequency, replacing any active guide. Checklists open their panel. Returns false for unknown guides. | | stopGuide() | Removes the active guide silently (no dismiss event). | | dismissGuide() | Dismisses the active guide (records guide_dismissed). | | getActiveGuide(): PublishedGuide \| null | The exclusive guide currently showing. | | getGuides(): readonly PublishedGuide[] | Published guides for this environment. | | isGuideCompleted(idOrKey) | From merged local + server state. | | getChecklistProgress(idOrKey) | { completedTaskIds, completedAt, dismissedAt }. | | completeChecklistTask(idOrKey, taskId) | Manual completion; syncs to /sdk/checklists/:id/progress. | | openChecklist(idOrKey) / closeChecklist(idOrKey) | Show/hide the checklist panel (re-renders a dismissed checklist). | | openResourceCenter() / closeResourceCenter() | Toggle the resource center panel. | | refresh() | Re-fetch config now. | | on(event, cb): () => void / off(event, cb) | Subscribe to events (below). | | getContext() | The TargetingContext used for rule evaluation (debugging). | | getState() | Debug snapshot: ready flag, ids, config version, active/queued guides, widgets, state, pending events. | | destroy() | Full cleanup: guides, widgets, listeners, timers, the #guidely-root host; flushes pending events via sendBeacon. A new client can be created afterwards. | | GuidelyClient.init(options) / GuidelyClient.get() | Singleton helpers used by the embed (window.Guidely.init/get). | | isFeatureEnabled(key, fallback = false) | Boolean flag for the current user (fallback until loaded, when missing or not a boolean). | | getFeatureValue(key, fallback) / getFeatureDetails(key, fallback) | Flag value / value plus reason, rule, variation and experiment. | | onFlagsChange(cb): () => void | Called whenever flag values may have changed (new rules, identify, navigation, overrides). | | flags | The flags client: isEnabled, getValue, getAll, setAttributes, setOverrides, ready, subscribe… |

Events

ready(config), config(config), guide:start(guide), guide:step({ guide, stepId, stepIndex }), guide:complete(guide), guide:dismiss(guide), guide:skip(guide), checklist:task({ guide, task, progress }), checklist:complete(guide), survey:submit({ guide, answers, responseId }), resource-center:open(), error(Error), track(sdkEvent), identify({ userId, anonymousId }), authoring({ active }), flags:ready(), flags:change().

Feature flags

if (guidely.isFeatureEnabled("new-checkout")) showNewCheckout();
const color = guidely.getFeatureValue("checkout-button-color", "blue");
guidely.onFlagsChange(() => rerender());
guidely.flags.updateAttributes({ cohort: "beta" }); // extra attributes, kept across identify()

Flags load in parallel with the guide config, with the same public key, and the last payload is cached in storage (guidely:flags:<key>). Attributes are maintained for you: id (the identified user id, else the anonymous id), anonymousId, every identify trait, account ({ id, …traits }), $audiences (audience ids from identify), url, path, deviceType, browser, os, locale, environment and platform: "web"; they update on identify, updateTraits, setAccount, reset and navigation. Experiment exposures are tracked as experiment_viewed events (experimentKey, variationId, variationName, flagKey, hashAttribute) once per session, through the event queue, so consent and analytics: false apply.

Behaviour

  • Config: GET /api/v1/sdk/config?key=… with If-None-Match; the last good response is cached in storage (guidely:config:<key>) and used when offline. Refreshed every configRefreshMs and on refresh().
  • Events: queued in memory + storage (max 200, oldest dropped), deduped by eventId, flushed in batches of ≤50 every 2s, on navigation and on visibilitychange/pagehide (via sendBeacon, falling back to fetch). Failed batches retry with exponential backoff (1s → 30s, 5 attempts) and stay queued afterwards.
  • Triggers: page_load, url_match, event, element_click (delegated capture listener), element_visible (IntersectionObserver), delay, after_guide, segment_enter, manual. Filtered by urlPatterns, trigger.urlPattern, targeting rules (rolloutSalt = guide.id), frequency (once, once_per_session, every_time, interval, maxImpressions, cooldownHours, reshowAfterDismiss) and survey startAt/endAt.
  • Exclusivity: one flow/modal/tooltip/survey at a time, lowest priority first, mutuallyExclusiveWith respected; banners, checklists, hotspot-only guides and the resource center coexist. Remaining eligible guides queue and show after the active one ends (page-scoped triggers are dropped if the page changed).
  • Multi-page flows: step.url steps wait for navigation; the active step is persisted in sessionStorage (guidely:active) so full page loads resume the guide.
  • Server sync (fire-and-forget with retries): POST /sdk/guide-state, POST /sdk/checklists/:id/progress, POST /sdk/surveys/:id/responses, GET /sdk/resource-center/search?q=.

Authoring mode

When the URL contains ?guidely_authoring=<token> the SDK:

  1. Strips the parameter (history.replaceState) and stores { token } in sessionStorage["guidely:authoring"] so SPA navigations keep authoring active.
  2. Calls GET /sdk/authoring/session?token= and, when valid, starts the picker from @guidelytech/guide-runtime (toolbar, hover outline, click-to-pick) and a 15s heartbeat (POST /sdk/authoring/session/heartbeat?token=).
  3. Sends each pick as window.opener.postMessage({ type: "guidely:pick", sessionId, pick }, dashboardOrigin) and POST /sdk/authoring/session/picks?token=.

Messages accepted from dashboardOrigin (event.origin checked):

| type | Payload | Effect | |---|---|---| | guidely:preview-step | { step, theme?, appearance? } | Renders the draft step live (target fallback → modal). | | guidely:preview-guide | { guide: { id?, key?, name?, kind?, version?, config } } | Runs the draft guide. | | guidely:highlight | { selectors } | Outlines matches, replies guidely:highlight-result { counts, resolved }. | | guidely:stop-preview | — | Removes the preview. | | guidely:ping | — | Replies guidely:pong { url }. | | guidely:exit | — | Leaves authoring mode. |

Messages posted to the opener: guidely:authoring-ready { sessionId, url }, guidely:pick, guidely:highlight-result, guidely:preview-ended { reason }, guidely:pong, guidely:exit.

Storage keys

guidely:anon, guidely:seen, guidely:config:<key>, guidely:etag:<key>, guidely:state:<key>, guidely:user:<key>, guidely:queue:<key>, guidely:flags:<key> (local/session storage per storage option); guidely:session, guidely:active, guidely:authoring (sessionStorage).

Network recording (session replay)

When the project enables replay with Record network (replay.recordNetwork), recordings include the page's fetch/XMLHttpRequest calls, WebSocket connections and frames, and resource timing (scripts, stylesheets, images, fonts, media, the document) as rrweb plugin events (guidely/network@1, payload NetworkPluginPayload from @guidelytech/shared). It lives in the lazily loaded recorder chunk; the base bundle does not grow.

  • fetch, XMLHttpRequest and WebSocket are wrapped only while recording and restored on stop (left as a passthrough if another library wrapped them afterwards). Callers get the original promise / XHR / socket; bodies are read from response.clone() (≤ 2s, never the app's stream). instanceof WebSocket, constants, onmessage and addEventListener behave as before; sockets created while recording keep working after it stops.
  • Requests to Guidely's SDK API, data:/blob: URLs and the recorder script are never recorded. URLs are absolute, with sensitive-looking query parameter values replaced by [redacted].
  • Headers and bodies only with Record payloads (replay.recordNetworkPayloads, off by default): header names lower-cased, credentials/cookies [redacted]; bodies only for text, JSON and form-urlencoded content, ≤ 10KB ([truncated]), [binary] otherwise; sensitive JSON/form keys masked. WebSocket frame data ≤ 2KB ([binary <n> B] for binary frames). Frame sizes and totals are always recorded; at most 200 frames per socket and 500 requests per page load.
  • Batched every second or 20 items, and flushed when the page is hidden.
  • sessionReplay: { recordNetwork: false } / { recordNetworkPayloads: false } turn this off for one installation. maskNetworkRequest(request) gets each request after redaction: return a modified copy, or null to drop it (and, for a WebSocket, all its frames); a throwing hook drops it.

Bundle size

pnpm --filter @guidelytech/sdk-core size builds a minified ESM bundle and prints gzip sizes:

| Bundle | minified | min+gz | |---|---|---| | sdk-core + guide-runtime + floating-ui + @guidelytech/flags (everything an app ships, excluding @guidelytech/shared) | 195 kB | 61.4 kB | | Scout messenger chunk (@guidelytech/guide-runtime/messenger, loaded on demand) | 26 kB | 8.8 kB | | Session-replay chunk (./recorder: rrweb + replay controller + network recording, loaded only when a session is recorded) | 94 kB | 30 kB | | script-tag build guidely.js (authoring schemas and messenger inlined) | 244 kB | ~71 kB |

The SDK imports only the zod-free @guidelytech/shared/runtime entry; the Zod schemas are loaded lazily and only when the page enters authoring mode. Feature flags (client + evaluator) add ~13 kB min+gz. Budget: 45 kB min+gz for the npm runtime and 15 kB for the messenger chunk, 80 kB for the session-replay chunk.