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

@stainless-code/svelte-layers

v0.1.0

Published

Svelte adapter for @stainless-code/layers — call a layer like an async function and await its response.

Readme

@stainless-code/svelte-layers

Open any layer from anywhere with @stainless-code/svelte-layers and manage modal, dialog, drawer, popover, and toast UI as ordered, named stacks. Await a typed result when the caller needs one, or fire-and-forget with void client.open(...); both are first-class. Named stacks, singleton upsert with live update, serial queues, nested child stacks via useLayerGroup, async actions via useMutationFlow, transitions, blockers, and validation provide standalone value.

Full fit matrix: README — When to use it.

Install

bun add @stainless-code/svelte-layers

@stainless-code/layers core is pulled in automatically and re-exported from both entry points. svelte is a required peer dependency (you already have it; do not add it to the install line).

Two entry points

| Import | Svelte version | Reactivity | | ------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | @stainless-code/svelte-layers | 5.7+ (runes) | useStack / useLayerSvelteStack (stack.current, stack.callFor); useMutationFlow{ pending, run } (flow.pending in markup); useLayerGroup → child SvelteStack via group.stack | | @stainless-code/svelte-layers/store | 3+ (stores) | useStack / useLayerReadable<T> ($stack); useMutationFlow.pendingReadable<boolean> ($pending); useLayerGroup.stackReadable<LayerState[]>; standalone callFor(client, stackId, state) |

Pick one entry per app — do not mix runes and store bindings for the same stack surface.

Getting started — runes (@stainless-code/svelte-layers)

1. Declare a layer

// layers/confirm.ts
import { layerOptions } from "@stainless-code/svelte-layers";
import ConfirmDialog from "./ConfirmDialog.svelte";

export type ConfirmPayload = {
  title: string;
};

export type ConfirmResponse = boolean;

export const confirm = layerOptions<ConfirmPayload, ConfirmResponse>({
  stack: "confirm",
  key: ["confirm", "remove"],
  component: ConfirmDialog,
  exitingDelay: 200,
});
<!-- ConfirmDialog.svelte -->
<script lang="ts">
  import type { LayerComponentProps } from "@stainless-code/svelte-layers";
  import type { ConfirmPayload, ConfirmResponse } from "./confirm";

  let { call, payload }: LayerComponentProps<ConfirmPayload, ConfirmResponse> =
    $props();
</script>

<div role="dialog">
  <h2>{payload.title}</h2>
  <button type="button" onclick={() => void call.end(true)}>Yes</button>
  <button type="button" onclick={() => void call.end(false)}>No</button>
</div>

2. Mount the stack outlet

There is no built-in outlet — render active layers yourself with {#each}.

<!-- App.svelte -->
<script lang="ts">
  import { setLayerClient, useStack } from "@stainless-code/svelte-layers";
  import { confirm } from "./layers/confirm";

  setLayerClient();
  const stack = useStack("confirm");
</script>

{#each stack.current as s (s.id)}
  {@const call = stack.callFor(s)}
  {#if call}
    {@const Comp = confirm.component}
    <Comp {call} payload={s.payload} phase={s.phase} actionStatus={s.actionStatus} />
  {/if}
{/each}

3. Call and await

ConfirmResponse is inferred from layerOptions<ConfirmPayload, ConfirmResponse> — no explicit generics on open.

<script lang="ts">
  import { useLayerClient } from "@stainless-code/svelte-layers";
  import { confirm } from "./layers/confirm";

  const client = useLayerClient();

  async function handleRemove() {
    const ok = await client.open({ ...confirm, payload: { title: "Remove?" } });
    if (!ok) return;
    deleteItem();
  }
</script>

<button type="button" onclick={() => void handleRemove()}>Remove</button>

Getting started — stores (@stainless-code/svelte-layers/store)

Layer options and the call flow are the same, but store-based apps should import core APIs from @stainless-code/svelte-layers/store. In particular, change the declaration above to import layerOptions from the /store entry; importing the runes entry requires Svelte 5.7+. The outlet wiring differs, and Svelte 3/4 components use legacy syntax as shown below.

2. Mount the store outlet

<!-- App.svelte -->
<script lang="ts">
  import { setLayerClient, useStack, callFor } from "@stainless-code/svelte-layers/store";
  import { confirm } from "./layers/confirm";

  const client = setLayerClient();
  const stack = useStack("confirm");
</script>

{#each $stack as s (s.id)}
  {@const call = callFor(client, "confirm", s)}
  {#if call}
    <svelte:component
      this={confirm.component}
      {call}
      payload={s.payload}
      phase={s.phase}
      actionStatus={s.actionStatus}
    />
  {/if}
{/each}

With stores, callFor is a standalone function — it is not a method on the stack handle. The outlet keeps <svelte:component> because this entry targets Svelte 3+; the runes entry uses the Svelte 5 dynamic-component idiom (<Comp/>).

The layer declaration shape is shared, but the component above uses Svelte 5 syntax. In Svelte 3/4, declare props and events with legacy syntax:

<script lang="ts">
  import type { LayerComponentProps } from "@stainless-code/svelte-layers/store";
  import type { ConfirmPayload, ConfirmResponse } from "./confirm";

  export let call: LayerComponentProps<
    ConfirmPayload,
    ConfirmResponse
  >["call"];
  export let payload: LayerComponentProps<
    ConfirmPayload,
    ConfirmResponse
  >["payload"];
</script>

<button type="button" on:click={() => void call.end(true)}>Yes</button>
<button type="button" on:click={() => void call.end(false)}>No</button>

When the payload admits undefined, omit payload from open; when the response is omitted, it defaults to void. This store-entry notice is both no-payload and fire-and-forget. Mount its "notice" stack with the store outlet pattern above.

// layers/notice.ts
import { layerOptions } from "@stainless-code/svelte-layers/store";
import Notice from "./Notice.svelte";

export const notice = layerOptions<void>({
  stack: "notice",
  key: ["notice"],
  component: Notice,
});
<!-- Notice.svelte -->
<script lang="ts">
  import type { LayerComponentProps } from "@stainless-code/svelte-layers/store";

  export let call: LayerComponentProps<void>["call"];
</script>

<p>Changes saved.</p>
<button type="button" on:click={() => void call.dismiss()}>Dismiss</button>
<!-- NoticeButton.svelte -->
<script lang="ts">
  import { useLayerClient } from "@stainless-code/svelte-layers/store";
  import { notice } from "./layers/notice";

  const client = useLayerClient();
</script>

<button type="button" on:click={() => void client.open(notice)}>
  Show notice
</button>

Mutation flow & nested stacks

The adapter ships no .svelte outlet components — render active layers with useStack().current and callFor as above. useMutationFlow and useLayerGroup use the same compiler-free render path on both entries.

useMutationFlow — runes

useMutationFlow(call) returns { pending, run }. pending is a reactive readonly boolean — read flow.pending in markup or $derived. run(fn).orEnd(response) sets actionStatus: "running" while fn runs, ends the layer with response on success, or leaves it open and rethrows on failure.

<!-- SaveDialog.svelte -->
<script lang="ts">
  import { type LayerComponentProps, useMutationFlow } from "@stainless-code/svelte-layers";

  let { call, payload }: LayerComponentProps<{ title: string }, boolean> = $props();
  const flow = useMutationFlow(call);
</script>

<div role="dialog" aria-label={payload.title}>
  <button
    type="button"
    disabled={flow.pending}
    onclick={() => void flow.run(() => save()).orEnd(true)}
  >
    Save
  </button>
</div>

useMutationFlow — stores

Same run(fn).orEnd(response) contract; pending is a Readable<boolean> — bind it and auto-subscribe with $pending.

<!-- SaveDialog.svelte -->
<script lang="ts">
  import { type LayerComponentProps, useMutationFlow } from "@stainless-code/svelte-layers/store";

  let { call, payload }: LayerComponentProps<{ title: string }, boolean> = $props();
  const { pending, run } = useMutationFlow(call);
</script>

<div role="dialog" aria-label={payload.title}>
  <button type="button" disabled={$pending} onclick={() => void run(() => save()).orEnd(true)}>
    Save
  </button>
</div>

useLayerGroup — runes

useLayerGroup(call, options?) returns { open, dismissAll, stack, stackId }. stack is a SvelteStack — render {#each group.stack.current as s} and pair each state with group.stack.callFor(s). The child stack is disposed and dismissed on onDestroy.

<!-- ParentDrawer.svelte -->
<script lang="ts">
  import { type LayerComponentProps, useLayerGroup } from "@stainless-code/svelte-layers";
  import { childLayer } from "./layers/child";

  let { call, payload }: LayerComponentProps<{ title: string }, boolean> = $props();
  const group = useLayerGroup(call);
</script>

<div role="dialog" aria-label={payload.title}>
  <h2>{payload.title}</h2>
  <button
    type="button"
    onclick={() =>
      void group.open({ ...childLayer, payload: { label: "Child" } })}
  >
    Open child
  </button>
  {#each group.stack.current as s (s.id)}
    {@const childCall = group.stack.callFor(s)}
    {#if childCall}
      {@const Comp = childLayer.component}
      <Comp
        call={childCall}
        payload={s.payload}
        phase={s.phase}
        actionStatus={s.actionStatus}
      />
    {/if}
  {/each}
</div>

useLayerGroup — stores

stack is a Readable<LayerState[]>. Render {#each $stack} and pair each state with callFor(client, group.stackId, s).

<!-- ParentDrawer.svelte -->
<script lang="ts">
  import {
    callFor,
    type LayerComponentProps,
    useLayerClient,
    useLayerGroup,
  } from "@stainless-code/svelte-layers/store";
  import { childLayer } from "./layers/child";

  let { call, payload }: LayerComponentProps<{ title: string }, boolean> = $props();
  const client = useLayerClient();
  const group = useLayerGroup(call);
  const stack = group.stack;
</script>

<div role="dialog" aria-label={payload.title}>
  <h2>{payload.title}</h2>
  <button
    type="button"
    onclick={() =>
      void group.open({ ...childLayer, payload: { label: "Child" } })}
  >
    Open child
  </button>
  {#each $stack as s (s.id)}
    {@const childCall = callFor(client, group.stackId, s)}
    {#if childCall}
      <svelte:component
        this={childLayer.component}
        call={childCall}
        payload={s.payload}
        phase={s.phase}
        actionStatus={s.actionStatus}
      />
    {/if}
  {/each}
</div>

API

Runes entry (@stainless-code/svelte-layers) — complete adapter exports

| Export | Signature | Role | | ------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | setLayerClient | (client?) => LayerClient | Provide a LayerClient via Svelte context (creates one if omitted). | | useLayerClient | () => LayerClient | Read the nearest client from context. | | useStack | (client, stackId?, selector?, compare?) or (stackId?, selector?, compare?) => SvelteStack | Reactive stack handle. Default selector returns LayerState[]. | | useLayer | (client, key, stackId?, compare?) or (key, stackId?, compare?) => SvelteStack<…, LayerState \| null> | Subscribe to one layer by key; current is null when inactive. | | useMutationFlow | (call) => MutationFlow<R> | Coordinate async work with actionStatus: "running"; run(fn).orEnd(response) ends on success. | | useLayerGroup | (call, options?) => LayerGroup | Child stack scoped to the calling layer; stack is a SvelteStack; cleaned up on onDestroy. | | SvelteStack | { readonly current, callFor(state, rootProps?) } | Return type of useStack / useLayer / useLayerGroup.stack on the runes entry. | | MutationFlow<R> | { readonly pending: boolean, run } | pending mirrors actionStatus: "running"; read flow.pending in markup. | | MutationRun<R> | { orEnd(response) => Promise<void> } | Ends the layer with response on success; on failure leaves it open and rethrows. | | LayerGroup | { open, dismissAll, stack, stackId } | open is stack-pre-bound; dismissAll(response?) clears the child stack. | | LayerComponentProps | type | Props contract for layer components. |

const count = useStack("confirm", (states) => states.length);
const top = useStack("confirm", (states) => states.at(-1) ?? null);

Read stack.current (or layer.current) inside reactive contexts. stack.callFor(state, rootProps?) builds each layer's call context.

Stores entry (@stainless-code/svelte-layers/store) — complete adapter exports

| Export | Signature | Role | | ------------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | setLayerClient | (client?) => LayerClient | Same context provider as the runes entry. | | useLayerClient | () => LayerClient | Same context reader. | | useStack | (client, stackId?, selector?, compare?) or (stackId?, selector?, compare?) => Readable<T> | Svelte store (default T = LayerState[]). Auto-subscribe with $stack. | | useLayer | (client, key, stackId?, compare?) or (key, stackId?, compare?) => Readable<LayerState \| null> | Store for one layer by key; value is null when inactive. | | useMutationFlow | (call) => MutationFlow<R> | Same run(fn).orEnd(response) contract; pending is a Readable<boolean> (subscribe with $pending). | | useLayerGroup | (call, options?) => LayerGroup | Child stack store via stack: Readable<LayerState[]>; cleaned up on onDestroy. | | callFor | (client, stackId, state, rootProps?) => LayerCallContext \| null | Build a layer's call context; null if the layer is gone. | | MutationFlow<R> | { pending: Readable<boolean>, run } | pending mirrors actionStatus: "running". | | MutationRun<R> | { orEnd(response) => Promise<void> } | Ends on success; on failure leaves the layer open and rethrows. | | LayerGroup | { open, dismissAll, stack, stackId } | Render $stack with callFor(client, stackId, s). | | LayerComponentProps | type | Props contract for layer components. |

Core re-exports (both entries)

Both entries export * from @stainless-code/layers — import core APIs from the same path:

LayerClient, LayerStack, Layer, layerOptions, layerKey, LayerState, LayerCallContext, createCallContext, createLayerGroup, childStackId, keySignature, hashKey, DataTag, ResponseOf, ErrorOf, PayloadValidationError, isPayloadValidationError, and related types.

Engine concepts (transitions, blockers, validation, serial scope, gcTime) live in core — see the layers skill and architecture doc.

Full guide: repo README.