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

@akin-travel/sdk-embed

v0.1.0

Published

Framework-agnostic embeddable Akin loyalty + auth widget. Drop-in Web Components (<akin-login>, <akin-loyalty-card>, <akin-tier-progress>) over @akin-travel/sdk-core — integrate on Vue, Angular, Svelte, plain HTML, or any server-rendered stack with a sing

Readme

@akin-travel/sdk-embed

Framework-agnostic embeddable Akin loyalty + auth widgets — drop-in Web Components built on @akin-travel/sdk-core.

This is Phase 2 of the multi-platform Partner SDK (epic akin-travel/issues#2042): the zero-framework reach. Partners on Vue, Angular, Svelte, plain HTML, or any server-rendered stack integrate loyalty + auth with a single <script> tag and custom elements — no per-framework SDK, no build step.

Looking for React? Use @akin-travel/partner-sdk (providers, hooks, headless components). The embed widget is the answer for everything else.

Quick start (the <script> drop-in)

<script
  src="https://cdn.jsdelivr.net/npm/@akin-travel/sdk-embed/dist/akin-embed.global.js"
  data-partner-id="YOUR_PARTNER_ID"
  data-api-key="YOUR_PARTNER_API_KEY"
  data-environment="production"
  data-firebase-api-key="…"
  data-firebase-auth-domain="…"
  data-firebase-project-id="…"
  data-firebase-app-id="…"
  data-verify-callback-url="https://your-site.example/loyalty"></script>

<akin-login></akin-login>
<akin-loyalty-card></akin-loyalty-card>
<akin-tier-progress
  data-tiers='[{"displayName":"Silver","points":1000},{"displayName":"Gold","points":5000}]'></akin-tier-progress>

The script auto-initialises from its own data-* attributes and registers the custom elements. A member signs in via <akin-login> (magic link, passkey, or Google), and <akin-loyalty-card> / <akin-tier-progress> render their tier and points. Opt out of auto-init with data-auto-init="false" and call AkinEmbed.init({...}) yourself.

The elements

| Tag | Renders | |---|---| | <akin-login> | Email magic-link form, plus passkey + Google buttons when available; signed-in summary with sign-out | | <akin-loyalty-card> | The member's AKIN points + spendable balance + tier badge | | <akin-tier-progress> | Current tier and a progress bar toward the next (data-tiers supplies the ladder) | | <akin-book-search> | A booking search bar: your properties, check-in and check-out dates, and guests. Submit opens your AKIN booking site |

Each element is a thin view over a shared controller, so they stay in sync — sign in once and every widget on the page updates. <akin-book-search> is the exception: it needs no sign-in and no init() (see below)

Booking search (<akin-book-search>)

<akin-book-search partner="your-slug"> puts a search bar for your own booking site on any page you own. It lists your properties, takes check-in and check-out dates and guests, and on Search sends the guest — in the top window, never an iframe — to your booking site:

https://book-<your-slug>.akintravel.com/<locale>/book?checkIn=2030-01-10&checkOut=2030-01-13&adults=2&children=0&rooms=1&property=<property-slug>

<locale> is the language the bar shows its copy in (see locale below), so the guest stays in that language on the booking site

The element never takes payment details. Everything after the search happens on the booking site, so your page stays out of card-data scope

It needs no partner id, API key or Firebase config: the script defines it as soon as it loads, and the property list is a public query

| Attribute | Required | Description | |---|---|---| | partner | ✓ | Your AKIN partner slug — the <your-slug> in book-<your-slug>.akintravel.com | | environment | | production (default), staging, or development. staging sends guests to staging-book-<your-slug>.akintravel.com | | api-url | | Override the GraphQL endpoint (defaults per environment) | | data-theme | | Per-element theme override (JSON), as for every element | | locale | | The language of the bar's labels, placeholders and messages, and of the booking site it opens: en, es, pt, fr or de. A region suffix is dropped (es-CO is es). Without the attribute the bar uses the page's <html lang>, and follows it when the page changes it. Any other language shows English |

Before the widget goes live, AKIN adds the partner site's origin (apex + www, for example https://your-site.example and https://www.your-site.example) to the API CORS_ORIGINS (infrastructure/env/prod.yaml and staging.yaml). Until then the browser refuses the property list. AKIN also switches on your booking site

Not on npm yet. Neither @akin-travel/sdk-embed nor @akin-travel/sdk-core has been published, so the jsdelivr URL below returns 404 until a maintainer makes the first publish by hand (npm trusted publishing needs an existing package — see .github/workflows/sdk-release.yml). That first publish is a prerequisite for the snippet

WordPress page (copy-paste)

In the block editor, add a Custom HTML block where the bar should appear and paste this (replace your-slug). The element works in a classic-editor page or a theme template too

<script src="https://cdn.jsdelivr.net/npm/@akin-travel/sdk-embed/dist/akin-embed.global.js" defer></script>
<akin-book-search partner="your-slug"
  style="--akin-primary: #1f4d3a; --akin-radius: 0.5rem; max-width: 48rem;"></akin-book-search>

Put the <script> tag on the page once, even with several bars. On a page that also carries the loyalty elements, keep the one <script> with its data-* config

WordPress saves a <script> tag only for a user who may post unfiltered HTML — an administrator or editor on a single site, a super admin on a multisite network. If the script disappears when you save, ask one of them to add the block, or load the script from your theme's header instead

Event

Before it navigates the element emits a composed, cancelable akin:book-search event with detail.url (the booking-site URL) and detail.values. A listener that only records the search needs nothing else — the element navigates afterwards:

document.querySelector('akin-book-search').addEventListener('akin:book-search', (e) => {
  analytics.track('booking_search', e.detail.values);
});

preventDefault() stops the navigation. The guest then stays on your page until your own code sends them to e.detail.url — for example after an async analytics call resolves:

bar.addEventListener('akin:book-search', async (e) => {
  e.preventDefault();
  await analytics.track('booking_search', e.detail.values);
  window.top.location.href = e.detail.url;
});

Events

The elements emit composed CustomEvents that cross the Shadow DOM boundary, so the host page can react:

document.querySelector('akin-login')
  .addEventListener('akin:authenticated', (e) => console.log(e.detail.member));

akin:magic-link-sent, akin:authenticated, akin:signed-out, akin:error.

Style isolation

Every widget renders inside an open Shadow DOM, so host-page CSS can't reach in and the widget's own CSS can't leak out — the isolation acceptance criterion.

Theming

CSS custom properties inherit through the Shadow DOM boundary, so the widgets honour the partner theme-token contract used by white-label automatically:

  • Set the namespaced --akin-* tokens (highest precedence), or
  • expose the white-label/shadcn tokens (--primary, --background, --foreground, --secondary, --muted, --border, --radius) — the widgets fall back to them, then to neutral defaults.
:root {
  --akin-primary: oklch(0.62 0.19 28); /* OKLCH, hex, rgb — passed through verbatim */
  --akin-radius: 0.75rem;
}

Per-widget override with data-theme (JSON) or programmatically:

<akin-loyalty-card data-theme='{"primary":"#0f766e","radius":"1rem"}'></akin-loyalty-card>

npm / bundler usage (Vue, Angular, Svelte build pipelines)

npm install @akin-travel/sdk-embed @akin-travel/sdk-core
import { createAkinEmbed } from '@akin-travel/sdk-embed';

const akin = createAkinEmbed({
  partnerId: 'YOUR_PARTNER_ID',
  apiKey: 'YOUR_PARTNER_API_KEY',
  environment: 'production',
  firebaseApiKey: '…',
  firebaseAuthDomain: '…',
  firebaseProjectId: '…',
  firebaseAppId: '…',
  verifyCallbackUrl: 'https://your-site.example/loyalty',
});

Then use the elements in your templates — they're standard custom elements:

  • Vue: works out of the box; tell Vue they're custom elements via compilerOptions.isCustomElement = (tag) => tag.startsWith('akin-').
  • Angular: add CUSTOM_ELEMENTS_SCHEMA to the module/component.
  • Svelte: use the tags directly in markup.

Using only <akin-book-search>? It needs no createAkinEmbed call — define it with registerElements(INIT_FREE_ELEMENT_TAGS) from the same import

import { registerElements, INIT_FREE_ELEMENT_TAGS } from '@akin-travel/sdk-embed';

registerElements(INIT_FREE_ELEMENT_TAGS);
<akin-login />
<akin-loyalty-card />

Builds

| Output | For | |---|---| | dist/akin-embed.global.js (IIFE, window.AkinEmbed) | The CDN <script> drop-in — fully self-contained (inlines firebase/auth). unpkg/jsdelivr point here | | dist/index.js (ESM) + dist/index.d.ts | npm consumers who bundle it themselves; runtime deps stay external |

npm run build ends with a size check: scripts/check-bundle-size.mjs fails the build when dist/akin-embed.global.js outgrows its gzip budget. Every visitor to a partner page downloads that file, so a raised budget is a decision recorded in the commit, never a way to get a red build green

Demo

demo/index.html is a no-framework page wiring all three widgets, theming, and a Shadow-DOM-isolation probe. Build, then open it:

npm run build --workspace=@akin-travel/sdk-embed
# open packages/sdk-embed/demo/index.html

Publishing

Published by the shared SDK release pipeline: the @akin-travel/* scope is auto-discovered, built via build:sdk, and published idempotently. The CDN entry point is the unpkg/jsdelivr field above.