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

arky-sdk

v0.15.0

Published

Official TypeScript SDK for Arky - website backend for custom frontends

Readme

arky-sdk

Official TypeScript SDK for Arky, the website backend and Admin client for custom frontends.

Installation

npm install arky-sdk

Storefront quick start

The current browser contract is [email protected]. Pin that exact version during the coordinated prelaunch cutover so the Server, App, and storefront route/header contracts move together:

npm install --save-exact [email protected]

Copy the Store publishable key from Developer and initialize one client:

import { initialize } from "arky-sdk/storefront";

export const arky = initialize(import.meta.env.PUBLIC_ARKY_PUBLISHABLE_KEY);

initialize is synchronous. It makes no request and creates no visitor. Production requests use https://api.arky.io by default.

For local development or an explicit initial context:

export const arky = initialize("arky_pk_...", {
  apiUrl: "http://localhost:8000",
  locale: "it",
  market: "ita",
});

The SDK accepts only an arky_pk_... publishable key. A personal arky_api_... token or an arky_vst_... visitor session is rejected at initialization. Publishable keys identify a Store; they grant no Admin access and are safe to include in browser code.

Read content and submit forms

Anonymous CMS and catalog reads do not create a visitor:

const page = await arky.cms.entry.get({
  collection_id: "pages",
  key: "homepage",
});

const titleBlock = page.blocks.find((block) => block.key === "title");
const title = arky.utils.getBlockTextValue(titleBlock, arky.getLocale());

Stateful operations identify the visitor lazily. Concurrent first operations share one identify request:

await arky.cms.form.submitByKey({
  key: "contact",
  values: {
    email: "[email protected]",
    message: "Hello from the storefront",
  },
});

The browser persists only the arky_vst_... visitor-session token. Storage is isolated by API endpoint and a fingerprint of the publishable key.

Products, services, and checkout

const { items: products } = await arky.eshop.product.list({ limit: 20 });
const product = await arky.eshop.product.get({ id: products[0].id });

await arky.eshop.cart.addProduct(product, product.variants[0], 2);
await arky.eshop.cart.quote();

const order = await arky.eshop.cart.checkout({
  payment_method_key: "cash",
});

Scheduled services use the same cart:

const { items: services } = await arky.eshop.service.list({ limit: 20 });

await arky.eshop.service.initialize();
await arky.eshop.service.select(services[0]);
arky.eshop.service.findFirstAvailable();

const state = arky.eshop.service.state.get();
if (state.slots[0]) {
  arky.eshop.service.selectTimeSlot(state.slots[0]);
  arky.eshop.service.nextStep();
  await arky.eshop.service.addToCart();
}

Nano Stores expose reactive module state:

const unsubscribe = arky.eshop.cart.snapshot.subscribe((snapshot) => {
  console.log(snapshot.item_count, snapshot.cart?.id);
});

await arky.eshop.cart.load();
unsubscribe();

Locale and market context

Locale and market are independent. Neither is inferred from the other, browser language, IP address, or geolocation:

arky.setContext({ locale: "bs" });
arky.setContext({ market: "bih" });

Use an isolated scoped client for SSR, static generation, or parallel contexts:

const italian = arky.withContext({ locale: "it", market: "ita" });
const page = await italian.cms.entry.get({
  collection_id: "pages",
  key: "homepage",
});

Changing the scoped client does not mutate the original client. A market change while the cart contains items throws CART_MARKET_LOCKED; the SDK never silently clears or reprices the cart.

Embedded card checkout

Store setup is fetched lazily and deduplicated:

const setup = await arky.store.load();
console.log(setup.languages.default, setup.markets.default);

Payment configuration belongs to Arky. A card checkout returns a short-lived embedded Stripe action. The SDK mounts that exact Checkout Session inside the merchant page; it never redirects the ordinary purchase to a Stripe-hosted Checkout page and it never exposes secret credentials:

import { mountCheckoutAction } from "arky-sdk";

const result = await arky.eshop.cart.checkout({
  payment_method_key: "credit_card",
  return_url: window.location.href,
});

const mounted = await mountCheckoutAction(
  result.payment_action,
  "#payment",
  { onComplete: () => arky.eshop.order.get({ id: result.order_id }) },
);

// Call mounted?.destroy() when the checkout view is disposed.

Embedded Checkout completion and a browser return are navigation signals only. Authoritative Arky state, advanced by a signed Stripe event or an exact provider read, settles the payment.

SSR and static generation

Anonymous reads work without browser storage. Stateful SSR requires an explicit request-local adapter so a server module cannot retain one visitor across requests:

const arky = initialize(process.env.ARKY_PUBLISHABLE_KEY!, {
  locale: requestLocale,
  market: requestMarket,
  sessionStorage: {
    getItem: (key) => requestSession.get(key) ?? null,
    setItem: (key, value) => requestSession.set(key, value),
    removeItem: (key) => requestSession.delete(key),
  },
});

Create one client per request. withContext also creates an isolated visitor session; when used during SSR it reuses the request-local adapter under a separate scoped storage key. The SDK does not ship framework-specific cookie adapters.

Low-level storefront client

The module facade exposes its low-level client as arky.client:

await arky.client.eshop.product.find({ limit: 20 });
const cart = await arky.client.eshop.cart.current();
await arky.client.eshop.cart.get({ id: cart.id, token: cart.token });
await arky.client.cms.entry.find({
  collection_id: "pages",
  key: "homepage",
  limit: 1,
});

Low-level requests use Store-ID-free /v1/storefront routes and send connection context as headers:

X-Arky-Publishable-Key: arky_pk_...
X-Arky-Locale: it
X-Arky-Market: ita
Authorization: Bearer arky_vst_...

For cart recovery, cart.get({ id, token }) sends the recovery credential as X-Arky-Cart-Token. It is never placed in the request URL or body, and the corresponding response is private and non-cacheable.

Locale and market headers are omitted when no explicit context is set, allowing the server to use Store defaults.

Configuration

initialize(publishableKey: string, {
  apiUrl?: string,
  locale?: string,
  market?: string,
  sessionStorage?: StorefrontSessionStorage,
});

One storefront client always represents one publishable key and one Store. To connect to another Store, initialize a second explicit client with its publishable key.

Releasing

SDK packages are released only by tagging the current protected master commit with the exact v<package.json version> tag. The Publish SDK workflow reruns npm test and publishes through npm trusted publishing with provenance; configure that workflow as the package's trusted publisher instead of storing a long-lived npm token.

Admin client

Private operator integrations use the separate Admin client. Personal API tokens must never be exposed in browser code:

import { createAdmin } from "arky-sdk/admin";

const admin = createAdmin({
  baseUrl: "https://api.arky.io",
  storeId: "internal-store-id",
  apiToken: process.env.ARKY_PERSONAL_API_TOKEN,
});

Store connection management is available through the Admin surface:

const store = await admin.store.regeneratePublishableKey({
  store_id: "internal-store-id",
});

await admin.store.update({
  id: store.id,
  default_market_id: "market-id",
});

TypeScript

import {
  initialize,
  type ArkyStore,
  type StorefrontDto,
  type StorefrontSetup,
} from "arky-sdk/storefront";
import type { Block, Cart, Order, Price, Product, Service } from "arky-sdk";

type StorefrontProduct = StorefrontDto<Product>;
type StorefrontCart = StorefrontDto<Cart>;

Storefront request types intentionally contain no Store routing ID. Admin request types remain Store-explicit.

Verification

Run the complete SDK package contract with one command:

npm test

It builds the distributable package and runs every SDK contract case. App alone owns cross-repository Server compatibility against the exact immutable test Server image digest. Each storefront owns a hermetic repo-local build/preview Playwright smoke through its own npm test; storefronts never pull or run the shared test Server image.

Adding an endpoint

When adding SDK methods:

  1. Mirror server response DTOs in src/types/index.ts or the relevant API module.
  2. Keep Admin inputs Store-explicit, but omit store_id from every storefront input, URL, and body.
  3. Use /v1/storefront/... keyless routes and let the shared client attach publishable-key, locale, market, and visitor headers.
  4. Mark customer mutations as stateful so they call the deduplicated visitor-session lifecycle.
  5. Add explicit response generics to every HTTP call and re-export consumer-facing types.