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

@arcenciel/sdk

v1.1.0

Published

Official TypeScript SDK for the Arc en Ciel Developer API

Readme

@arcenciel/sdk 1.1.0

Official TypeScript/JavaScript client for the immutable Arc en Ciel Developer API 1.10.0 contract. The package is tested for Node.js 20.20+ and modern browsers. npm publishes it from the signed public source tag through OIDC Trusted Publishing with provenance.

Install

npm install @arcenciel/[email protected]

OAuth Authorization Code with PKCE

const client = new ArcEnCielClient()
const authorization = await client.oauth.createAuthorization({
  clientId: 'aec_client_...',
  redirectUri: 'https://your-app.example/oauth/callback',
  scopes: ['models:read', 'offline_access'],
})

sessionStorage.setItem('arcenciel-oauth', JSON.stringify(authorization))
window.location.assign(authorization.url)

// On the callback route, restore `authorization` from server-side/session storage.
const tokens = await client.oauth.exchangeCallback({ callbackUrl: window.location.href, authorization })

The helper verifies both callback state and Arc en Ciel iss before the single-use exchange. Persist a returned rotated refreshToken atomically and never retry an ambiguous token request. Confidential clients pass clientSecret only from a server.

Search and inspect a model

import { ArcEnCielClient, paginatePages } from '@arcenciel/sdk'

const client = new ArcEnCielClient({
  apiKey: process.env.ARCENCIEL_API_KEY,
  timeoutMs: 30_000,
})

const firstPage = await client.call(() =>
  client.models.searchModels({
    search: 'landscape',
    page: 1,
    limit: 20,
  })
)

const model = firstPage.data?.[0]
if (model?.id) {
  const detail = await client.call(() => client.models.getModel({ id: model.id }))
  console.log(detail.name)
}

for await (const item of paginatePages(page => client.models.searchModels({ search: 'landscape', page, limit: 50 }))) {
  console.log(item.name)
}

Public catalogue reads do not require credentials. Configure apiKey for account-specific filters and attribution. Use one key per integration and grant only the documented scope.

Page and cursor pagination

The facade keeps the two API pagination styles explicit while hiding their continuation loops. paginatePages accepts one-based page/limit responses with totalPages or hasMore; the existing paginate export remains a compatible alias. paginateCursor treats continuation values as opaque and stops only when nextCursor is absent:

import { paginateCursor, paginatePages } from '@arcenciel/sdk'

for await (const model of paginatePages(page => client.models.searchModels({ page, limit: 50 }))) {
  console.log(model.id)
}

for await (const notification of paginateCursor(cursor => client.notifications.listNotifications({ cursor, limit: 50 }))) {
  console.log(notification.id)
}

Both helpers reject invalid starts or repeated continuation tokens with ArcEnCielError code INVALID_PAGINATION instead of looping forever.

Test safely in the sandbox

All 265 stable methods can target deterministic or stateful isolated fixtures without changing generated code:

const sandbox = new ArcEnCielClient({
  baseUrl: 'https://arcenciel.io/developers/sandbox',
  apiKey: 'aec_test_public',
})

const page = await sandbox.call(() => sandbox.models.searchModels({ limit: 5 }))

The sandbox rejects live API keys, bearer credentials, and cookies. It supports documented error scenarios, binary ranges, redirects, and streams for integration and retry tests. See the sandbox guide.

Stream and verify a download

import { ArcEnCielClient, readAndVerifySha256 } from '@arcenciel/sdk'

const client = new ArcEnCielClient({ apiKey: process.env.ARCENCIEL_API_KEY })
const info = await client.call(() =>
  client.downloads.getModelVersionDownloadInfo({
    modelId: 42,
    versionId: 81,
  })
)

const response = await client.downloadModelVersionStream(42, 81, {
  filename: info.fileName,
  range: 'bytes=0-',
})

if (!response.body) throw new Error('The runtime did not provide a response stream')
if (!info.sha256) throw new Error('The API did not provide a SHA-256 for this file')

// For small files, this portable helper reads the response and verifies it with Web Crypto.
// Large Node.js downloads should pipe response.body to disk while calculating an incremental SHA-256.
const bytes = await readAndVerifySha256(response, info.sha256)
console.log(`Verified ${bytes.byteLength} bytes`)

downloadModelVersionStream follows HTTPS redirects, preserves optional Range requests, accepts 200 and 206, and normalizes failures to ArcEnCielError. Always compare the full SHA-256 returned by getModelVersionDownloadInfo when it is available.

Errors, timeouts, and retries

Generated methods throw their native transport error unless invoked through client.call(...). The facade converts failures to ArcEnCielError, which exposes:

  • status and API code;
  • requestId from X-Request-ID;
  • response headers;
  • retryAfterMs for rate-limit and transient-failure handling.

GET, HEAD, and OPTIONS requests retry 429, 502, 503, and 504 with bounded full jitter. A POST is retried only when the final generated request contains an Idempotency-Key; the same request and key are reused. Other writes are never retried automatically. Configure { retry: false } to disable retries or provide maxRetries, baseDelayMs, and maxDelayMs.

Create a comment with safe retries

const idempotencyKey = crypto.randomUUID()
const created = await client.call(() =>
  client.comments.createModelComment({
    modelId: 42,
    idempotencyKey,
    createArticleCommentRequest: { content: 'Useful training notes—thank you!' },
  })
)
console.log(created.comment.id)

Keep one key for the complete logical action. Matching retries replay the original successful response for 24 hours; changing the body while reusing a key returns 409.

Read conversations and send once

const inbox = await client.call(() => client.chat.listChatThreads({ folder: 'inbox', limit: 30 }))
for (const thread of inbox.data) console.log(thread.id, thread.title, thread.hasUnread)

const created = await client.call(() =>
  client.chat.createChatMessage({
    threadId: 81,
    idempotencyKey: crypto.randomUUID(),
    content: 'The release render is ready.',
  })
)
console.log(created.id)

Grant ChatRead for thread, message, presence, unread, and preview reads. Add ChatWrite only for requests, groups, messages, reactions, read state, and group mutations. Keep one idempotency key across every retry of the same request, group, or message create.

Manage and verify Developer Webhooks

import { ArcEnCielClient, verifyWebhookSignature } from '@arcenciel/sdk'

const client = new ArcEnCielClient({ apiKey: process.env.ARCENCIEL_API_KEY })
const endpoints = await client.webhooks.listWebhookEndpoints()

const valid = await verifyWebhookSignature(rawBody, request.headers.get('x-aec-signature') ?? '', process.env.ARCENCIEL_WEBHOOK_SECRET!)

Grant WebhooksRead for event, endpoint, and delivery reads. Add WebhooksWrite for endpoint lifecycle, test delivery, secret rotation, and manual retry. Pass the exact unparsed request body to verifyWebhookSignature; its default replay window is five minutes. Run examples/webhook-workflow.ts with a WebhooksRead key for a non-mutating discovery and local signature-verification smoke workflow.

Stable namespaces

| Namespace | Stable operations | | ---------------------- | ------------------------------------------------------------------------------------- | | client.articles | Article discovery, drafts, media, scheduling, publication, updates, and deletion | | client.chat | Private threads, requests, groups, messages, reactions, presence, and read state | | client.collabs | Collaboration discovery, showcases, requests, participant media, and membership | | client.collections | Collection discovery, creation, collaborators, contribution review, items, and media | | client.comments | Typed article, image, model, and video comment reads and mutations | | client.downloads | Model download metadata, binary transfers, training TOML, archives, and registration | | client.emotes | Anonymous emote catalogue reads | | client.feedback | Caller-owned product feedback, attachments, and deletion | | client.generator | Image/video generation options, presets, uploads, jobs, events, outputs, and publish | | client.images | Image discovery, uploads, metadata, crossposts, publishing, and bulk transfer | | client.models | Models, versions, resumable uploads, managed media, resources, and publishing | | client.notifications | Cursor-paginated inbox, summary, and read-state updates | | client.profile | Own profile, uploads, export, history, pinned templates, links, and profile media | | client.social | Favorites, follows, image/video reactions, and their explicit removal operations | | client.tags | Anonymous tag-usage discovery | | client.trustSafety | Illegal-content notices, private evidence, review requests, and content reports | | client.users | Public profiles, creator statistics, search, and visible uploads | | client.videos | Video discovery, uploads, metadata, publishing, HLS, streaming, and downloads | | client.webhooks | Endpoint lifecycle, event catalog, delivery diagnostics, retries, and secret rotation |

The generated low-level APIs and models are also exported from the package root. Their method names come from the stable OpenAPI operationId values.

Contract and generation

Generated code is committed and deterministically regenerated from the immutable contract. The hand-written facade, tests, and this README are preserved across regeneration.

License

MIT