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

@w6w/embed

v0.2.1

Published

Embed w6w's managed editor and providers in a host React app.

Readme

@w6w/embed

Embed w6w in a host React app: one provider that wires the w6w API client, @w6w/ui's components and an identity-scoped query cache to your own sign-in, and a managed workflow editor (<WorkflowEditor>) you mount as a route.

License: FSL-1.1-ALv2 (Functional Source License). This package is source-available, not open source: the licence's Competing-Use clause applies, and each version converts to Apache-2.0 after two years. @w6w/sdk, @w6w/react and @w6w/ui, which it depends on, stay MIT.

Install

npm i @w6w/embed react react-dom

This is the only w6w package most hosts install. @w6w/sdk, @w6w/react, @w6w/ui and @tanstack/react-query come with it as dependencies; install @w6w/ui yourself too (^0.2.0, so there is one copy) only if you write depth 2 below, since a strict resolver such as pnpm does not let host code import a transitive dependency; react and react-dom (18 or later) are yours. Import the one stylesheet once, at your app's root:

import "@w6w/embed/styles.css"; // @w6w/ui's styles, React Flow base included

Entries

Every import path the published package exposes. Each carries "use client", so a Next.js server page can import it.

| Import | What it holds | | --- | --- | | @w6w/embed | W6WEmbedProvider, useEmbedStatus, useEmbedProject, EmbedAuthError. | | @w6w/embed/editor | WorkflowEditor alone, loaded lazily. | | @w6w/embed/editors | The callable editors: WorkflowEditor, FunctionEditor, EndpointEditor, DocumentEditor. | | @w6w/embed/lists | The connected lists and detail views: FunctionList, EndpointList, WorkflowList, ReliabilityBoard, ActivitySummary, RunHistory, DocumentList, ServiceDetail, ConnectionList, ConnectionDetail. | | @w6w/embed/forms | The configuration forms: CreateFunctionModal, CreateEndpointModal, VarsManager, SecretsManager. | | @w6w/embed/rail | CallableWorkspace and CallableRail, to place the editor's rail yourself. | | @w6w/embed/repos | The data hooks the components read through (useFunctionsQuery, useAppActionsQuery, ...), on the provider's one cache. | | @w6w/embed/draft | The headless draft lifecycle (autosave, flush on hide, the stale-write token). No UI. | | @w6w/embed/lib | Pure helpers the editor shares with Studio. | | @w6w/embed/styles.css | The one stylesheet: @w6w/ui's styles, React Flow base included. |

Three depths

Use as much of the package as you need.

1. Drop in the component. It fetches, renders and writes on its own:

import { FunctionList } from "@w6w/embed/lists";

<FunctionList onOpen={(id) => router.push(`/functions/${id}`)} />;

2. Keep @w6w/ui's presentational component and feed it from an embed hook. You own the rows:

import { CallableList } from "@w6w/ui";
import { useFunctionsQuery } from "@w6w/embed/repos";

function MyFunctions() {
  const { data = [], isLoading } = useFunctionsQuery();
  return (
    <CallableList
      title="Functions"
      loading={isLoading}
      rows={data.map((f) => ({
        id: f.id,
        title: f.displayName || f.key,
        archived: f.archivedAt != null,
      }))}
      onOpen={(id) => router.push(`/functions/${id}`)}
    />
  );
}

3. Use the hook alone and draw everything yourself:

import { useFunctionsQuery } from "@w6w/embed/repos";

const { data } = useFunctionsQuery();
return <ul>{data?.map((f) => <li key={f.id}>{f.displayName || f.key}</li>)}</ul>;

Depth 2 imports @w6w/ui directly, so your host must list it (see Install); depth 3 does not. All three read the provider's one cache (see One cache).

The provider

import { W6WEmbedProvider } from "@w6w/embed";

function Host({ user }: { user: { id: string; accountId: string } }) {
  return (
    <W6WEmbedProvider
      apiOrigin="https://api.w6w.io"
      identityKey={`${user.id}:${user.accountId}`}
      getSession={() => fetch("/api/w6w-session", { method: "POST" }).then((r) => r.json())} // -> {token, expiresAt}
      project="proj_123" // optional default project, see "The project rule"
      theme="dark" // optional: follow your host's theme
    >
      {/* @w6w/ui components, @w6w/react hooks and <WorkflowEditor> work here */}
    </W6WEmbedProvider>
  );
}

getSession calls an endpoint on your backend that mints an account-scoped session for the signed-in user with createEmbedSessionClient from @w6w/sdk/server; the tenant client secret never reaches the browser. The recipe is in the guide at /guides/embed/.

  • identityKey names who is signed in. When it changes, the API client and the query cache are replaced and the subtree re-mounts, so nothing cached for the previous identity is shown to the next one.
  • The provider owns its QueryClient; it does not use, and does not need, one from your app.
  • ready={false} holds every read until you have a user; project sets the default project.
  • project={{ use: "default" }} uses the account's default project instead of an id you know. It is looked up once per identity (one GET /projects, the row with isDefault), never the first row of the list. useEmbedProject() returns { project, status, error? } with status one of "resolving" | "ready" | "error": while resolving, project is null and editors stay in their "project required" state; if the lookup fails or no row is the default, status is "error" and nothing falls back to another project. A plain project="proj_123" is "ready" at once; omitted, it is { project: null, status: "ready" }.
  • identityKey={null} means nobody is signed in: the provider renders fallback (default nothing) and mounts none of the embed, so no session is requested and no API call is made. Going null to an identity starts at once; going from an identity to null first writes any pending draft into the account that made it, then shows fallback.
  • useEmbedStatus() returns { status, error } with status "connecting" | "ready" | "error". The session is acquired by the first request a mounted surface makes, so without eager the status stays "connecting" until then. eager acquires the session when the provider mounts. A token your backend minted but the API refused (401) is "error" with an EmbedAuthError (cause is the SDK ApiError) until the next acquisition starts or the identity changes; a failing getSession is "error" with its own error. With project={{ use: "default" }} the lookup itself acquires a session on mount (so getSession is called even without eager), and the client is rebuilt once the project resolves, which acquires a session again: the status goes connecting, ready, connecting.

Next.js server components

A server component cannot pass a plain function to a client component, so a getSession written inline there does not work. Define it as an inline server action instead:

// app/page.tsx (a server component)
import { createEmbedSessionClient } from "@w6w/sdk/server";
import { W6WEmbedProvider } from "@w6w/embed";
import { currentUser } from "./your-auth"; // YOUR server-side session lookup

const sessions = createEmbedSessionClient({
  baseUrl: process.env.W6W_BASE_URL!,
  clientId: process.env.W6W_TENANT_CLIENT_ID!,
  clientSecret: process.env.W6W_TENANT_CLIENT_SECRET!, // stays on the server
});

export default async function Page() {
  const user = await currentUser(); // from your own session, on the server
  if (!user) return null;

  async function getSession() {
    "use server";
    const u = await currentUser(); // no arguments: who is signed in comes from the server
    if (!u) throw new Error("unauthenticated");
    const s = await sessions.create({ subject: u.id, accountRef: u.companyId });
    return { token: s.token, expiresAt: s.expiresAt }; // nothing else leaves the server
  }

  return (
    <W6WEmbedProvider
      apiOrigin={process.env.W6W_BASE_URL!}
      identityKey={`${user.id}:${user.companyId}`} // the same server-side user
      getSession={getSession}
    >
      {/* a client component renders <WorkflowEditor> here */}
    </W6WEmbedProvider>
  );
}

A server action is a public POST endpoint: anyone can call it with any arguments. So getSession must never accept the subject or the account from its caller, and takes no arguments at all. It reads the signed-in user from your own server-side session, mints for that user only, and returns only { token, expiresAt }. The tenant client secret stays on the server, and identityKey is computed from the same server-side user. In a client component, the fetch form above stays the default.

The editor

Mount the managed editor as a route in your app. It is loaded lazily, so it adds nothing to your bundle until the route renders:

import { WorkflowEditor } from "@w6w/embed/editor";

function WorkflowRoute({ workflowId }: { workflowId: string }) {
  return (
    <WorkflowEditor
      workflowId={workflowId}
      project="proj_123" // optional when the provider has one
      onNavigate={(to) => {/* route inside your app */}}
    />
  );
}

Props: workflowId (required), project, initialRunId, onNavigate and slots. onNavigate is optional (default: a no-op) and hands navigation (back to the list, or to another workflow) to your router; slots lets you place your own elements in the editor chrome; initialRunId opens the editor on a run. The editor must render inside <W6WEmbedProvider>.

Placing the rail yourself

By default the editor renders its own right rail (rail omitted or true). Pass rail={false} to render no rail and place it elsewhere: wrap the editor in <CallableWorkspace kind="workflow" id={workflowId}> and put <CallableRail kind="workflow" id={workflowId} /> anywhere inside it, such as a drawer. Both come from @w6w/embed/rail; the rail then edits the same draft the editor saves. Without an enclosing workspace, rail={false} simply has no rail. See the components reference.

The project rule

The editor takes its project from its project prop, else from the provider's project. Saves never fall back to the account's default project, because a save without a project would silently move the workflow. With neither set (or with project={null} or "", which refuses even when the provider has one), the editor opens with saving and publishing disabled and a "project required" message.

Autosave and switching identity

Edits autosave. When identityKey changes, a pending save is first flushed into the account it belongs to, and only then does the provider swap to the new identity. There is no discard path: a save that fails or times out does not block the switch, and is never retried into the new account.

Theming

The editor's chrome is styled by the same @w6w/embed/styles.css. Every rule is scoped under the editor's root element, class w6w-embed-root, so it does not leak into your page. The palette comes from the nearest [data-theme] ancestor: the provider's theme prop puts one around the tree. Omit theme and the editor follows data-theme on <html> if you set it, else the OS preference. The editor fills its parent (width and height 100%), so give its container a height.

The layout rules every embedded surface follows (it fills its parent, navigates by callback, and keeps every class under that root) are in docs/layout.md, a repo doc not shipped in the package.

Function editor

| Component | Import | What it is | | --- | --- | --- | | <FunctionEditor functionId project? onNavigate? slots? rail? tools?> | @w6w/embed/editors | One Function: Interface (name, key, description), Inputs, Implementation, Test invoke, with History, a JSON toggle and Save in the header. |

Saving, the JSON toggle's Save and the rail's Error handling all write the same draft, and every write carries the stored updatedAt. If the Function changed on the server since it was opened, the save is refused with an inline "Reload from server" control and is never retried. rail defaults to true; rail={false} publishes into an enclosing <CallableWorkspace kind="function" id> for the same id (the rail's saves then use that workspace's project). project only scopes the expression rail, since a Function belongs to the account. There is no Aliases section; add one through tools. See the components reference.

Lists

@w6w/embed/lists holds the connected lists (FunctionList, EndpointList, WorkflowList, ReliabilityBoard, ActivitySummary, RunHistory, DocumentList, ServiceDetail, ConnectionList, ConnectionDetail). Each reads through the same provider as the editor and writes through it, so a host mounts nothing else.

| Component | What it does | | --- | --- | | <FunctionList project? onOpen? getHref? onCreated? slots?>, <EndpointList>, <WorkflowList> | The account's Functions, Endpoints and the project's Workflows, each with a Show-archived toggle, New and Import, and rows that are buttons, or real links with getHref. onOpen(id, event) and onCreated(id) hand navigation to your router. Per-prop details, including EndpointList and WorkflowList, are in the components reference. | | <ConnectionList onOpen? getHref? addLabel? slots?> | Your connections with Add, Edit and Delete. Delete asks first. Edit appears only when the API behind the provider can save a connection, which <W6WEmbedProvider> always can. getHref makes each row a real link. slots.actions replaces the Add button (pass null to remove it), slots.header and slots.empty replace the heading and the empty state. Built-in "no credential" rows have no Edit, Delete or link. | | <ConnectionDetail connectionId onOpenAction onBack? slots?> | One connection: Test connection, Edit, the app's actions with their saved tests, and recent runs. onOpenAction(actionKey) fires for an action and onOpenAction(actionKey, savedTestId) for a saved test; onBack shows a back button when given. A missing connection shows a "not found" state. |

import { ConnectionDetail, ConnectionList } from "@w6w/embed/lists";

<ConnectionList onOpen={(id) => router.push(`/connections/${id}`)} />;
<ConnectionDetail connectionId={id} onOpenAction={(key, testId) => {/* your router */}} />;

@w6w/embed/lists holds the connected list components. <ReliabilityBoard project? variant? limit? days? showChart? getHref? onOpen? showAllHref? onShowAll? slots?> shows one project's service reliability: it loads the project's services itself (membership first, then status in serial batches of at most ten) and draws them with @w6w/ui's ReliabilityList. Icons come from the rows, so it makes no app-catalog request. days defaults to 30; limit only trims what is shown. Omit project to use the provider's; null or "" makes no request and asks for a project.

<ActivitySummary getHref? onOpen? slots?> shows the account's run statistics and recent activity: "Today" and "Last 7 days" headline cards, "Activity over time" and "Recent activity". It is account-wide (it takes no project), and it resolves each row's function or workflow name against the account's own lists (the workflow list follows the client's default project when the provider names one, so a workflow outside it stays plain text). The name is a link only when it resolves: getHref makes it an <a href>, onOpen alone makes it a button, and with neither it stays text. A click calls onOpen(target, event) and is never cancelled by the component. It makes two /dashboard/stats requests on purpose: "Today" is cut at the viewer's local midnight (from, bucket=day), and the server only groups by day or week, cutting at midnight in the database's timezone, so one request cannot give a local day. <RunHistory scope? showKind? filters? onFiltersChange? onOpenInEditor?> shows the execution history for the provider's project (or the scope you pass); omit filters and it keeps them itself, pass them and you own them. See the components reference.

<ServiceDetail appId project? days? backHref? onBack? slots?> shows one service's reliability drill-down: health, error mix, the API's own status, daily history and recent calls. It makes one single-service status request (apps=<appId>, never the whole board) plus one call-log request. days defaults to 30; omit project to use the provider's, and with none it makes no request. When the service has no row for the project it shows a not-found card whose back link is an <a href> with backHref, a <button> with only onBack, and absent with neither; a click calls onBack and is never cancelled.

Configuration forms

@w6w/embed/forms holds the connected forms (VarsManager, SecretsManager, and the CreateFunctionModal / CreateEndpointModal dialogs the lists open). Each is one self-contained element (root class w6w-embed-root) that reads and writes through the provider's client.

| Component | Scope | Props | | --- | --- | --- | | <VarsManager> | one project | project?: string \| null, slots? | | <SecretsManager> | the whole account (no project) | slots? |

  • VarsManager lists, adds, edits and deletes typed variables (string, number, boolean, json). Omit project to use the <W6WEmbedProvider project>. null or "" means no project: nothing is requested and "Add" is disabled. Every request names the project it acts on.
  • SecretsManager is write-only. A secret's value is typed into a form and sent; it is never shown again, never held in the query cache, and never passed to a prop or callback. Rotating with a blank value changes only the description. Secrets belong to the account, so the component takes no project.
  • slots: { header?, actions?, empty? }. Leave a slot out for the default (the heading, the "+ Add ..." button, "No ... yet."), pass null for nothing, or pass a node. actions also accepts a function, ({ openCreate, canCreate }) => ReactNode, so a host that supplies its own button can still open the create form.

Documents

| Component | Import | What it is | | --- | --- | --- | | <DocumentList> | @w6w/embed/lists | A project's documents: add, edit in place, delete. | | <DocumentEditor documentId> | @w6w/embed/editors | One document: its key and content, with Edit and Delete. |

Both take project (omitted: the provider's project; null or "": nothing is read and writes refuse) and slots (omitted: the default, null: nothing, a node: replaces). Documents the server shares in from a repository show no Edit or Delete. The embed has no repository sync and no "synced from" markers; a host that wants them adds them through the list's row slots (lead, sharedActions, footer). Details are in the components reference.

Endpoint editor

| Component | Import | What it is | | --- | --- | --- | | <EndpointEditor endpointId project? onNavigate? slots? rail? tools?> | @w6w/embed/editors | One Endpoint: Identity, the callable URL, Input, Target, Security, Test invoke, History and a JSON toggle, with a Save at the top and the bottom. |

Every write is one save carrying the stored updatedAt; if someone else saved first, the editor says so inline with a Reload and never retries. Secret fields are write-only: a stored secret shows "stored — type to replace" and is kept unless you type a new one. There is no Aliases section. rail={false} inside a <CallableWorkspace> for the same id publishes into that workspace. Details are in the components reference.

Console exclusions

Your host code makes no client.console call. The package uses a public @w6w/sdk operation wherever one exists, and every remaining client.console or raw request() site is listed here with its reason.

| File | Call | Reason | | --- | --- | --- | | src/repos/workflows.ts | console.workflows.listRuns, console.workflows.getRun + pollRunToTerminal | no public run-list or run-get operation; the run poll is what the Run feature needs | | src/repos/workflows.ts | console.workflows.upsert | creating a workflow in a named project has no public operation | | src/lib/history-query.ts | console.workflows.getRun; raw GET /history, GET /history/stats, GET /invocations/:id | no public history or invocation operation | | src/repos/step-tests.ts | console.stepTests.list, .save, .recordRun | no public operation | | src/repos/vault.ts | console.vault.list, .seal, .create, .update, .delete | vault is outside the public contract; used by SecretsManager (@w6w/embed/forms) to list, add, rotate and delete a secret, and to seal one from the expression editor | | src/repos/documents.ts | raw POST /documents/:id/sync | no public operation | | src/repos/spec-import.ts | raw POST /spec/import | no public operation (Settings import) | | src/repos/vars.ts | raw GET /vars, POST /vars, PATCH /vars/:id, DELETE /vars/:id | the SDK's vars methods take no options argument, so they cannot send ?project=; VarsManager (@w6w/embed/forms) needs it on every call | | src/repos/versions.ts | raw GET /:c/:id/versions, GET /:c/:id/versions/:v, POST /:c/:id/publish, POST /:c/:id/:x, POST /:c/:id/versions/:v/revert, POST /:c/:id/versions/:v/:x | the endpoint arm has no public version operation; the function arm of invoke-version keeps the raw invocation frame, which functions.runVersion unwraps | | src/repos/callables.ts | console.functions.list, .get, .upsert; console.endpoints.list, .get, .upsert; console.workflows.get, .upsert, .archive, .delete; the function and endpoint archive (POST /:c/:id/archive) and delete (DELETE /:c/:id) go through the client's plain request() with a computed path | endpoints have no public list, read or write operation (the public SDK exposes only endpoints.run), and no public archive or delete; the function arm is not a missing-operation case: public functions.list/get/create/update/archive exist, but these calls moved verbatim from Studio, whose editor hydrates from the console get (which splices valid), and migrating them to the public operations is deferred; the workflow arm keeps the typed console calls its rail already used | | src/repos/functions.ts | console.functions.list, .get, .upsert | moved verbatim from Studio and still on the console calls (the stored-definition read splices the server's valid; the guarded save sends ifUnmodifiedSince); public functions.list/get/update exist and migrating to them is deferred | | src/repos/endpoints.ts | console.endpoints.get, .upsert, .invoke | endpoints have no public operation; the save keeps its own mutationFn because the 201 carries the rendered invokeUrls[] | | src/draft/use-callable-draft.ts | console.functions.upsert, console.endpoints.upsert | the callable draft's guarded save (ifUnmodifiedSince) for the Function and Endpoint editors; endpoints have no public write operation, and the Function save is moved verbatim from Studio | | src/repos/spec-export.ts | raw GET /:c/:id/spec | no public export operation (Settings export) | | src/repos/apps.ts | console.apps.getActions, console.apps.get | the per-app Actions list and the app detail (version, source, actions) have no public operation; same GET /apps/:id payload the canvas adapter serves | | src/repos/connections.ts | console.connections.listForApp, .create, .update, .test | no public per-app connection list, create, update or test; the list itself is the public connections.list | | src/repos/connections.ts | console.connections.get, .delete | public connections.get/delete exist; these moved verbatim from Studio and still use the console calls, and migrating them is deferred | | src/repos/saved-tests.ts | console.savedTests.list, .listTestRuns, .create, .update, .delete, .recordTestRun | saved tests and tester run history have no public operation | | src/repos/reliability.ts | console.reliability.listMembers, console.reliability.list | no public reliability operation; the board's membership discovery and serial status batches | | src/repos/api-calls.ts | console.apiCalls.list | no public operation; ServiceDetail's recent-calls log | | @w6w/react createW6WUiAdapter (canvas) | console.apps.*, saved and step tests, subscriptions | the adapter's own call sites, used by the canvas from @w6w/ui/flow |

One cache

Inside <W6WEmbedProvider>, read data through the @w6w/embed/repos hooks (useFunctionsQuery, useAppActionsQuery, useConnectionsForAppQuery, ...), not through @w6w/react's read hooks. The repos hooks share the provider's identity-scoped TanStack QueryClient under fixed query keys, so a save in one surface refreshes every other surface that shows the same record. @w6w/react's read hooks keep their own useAsync state per hook call, which is a second cache: the two disagree after a write and neither invalidates the other.