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

@surrealdb/fabric

v0.2.0

Published

The shared SurrealDB **Fabric API** integration — a typed OpenAPI client, TanStack Query hooks, and the support ticketing surfaces built on top of them. Published to npm so every SurrealDB frontend can talk to `api.surrealdb.com` the same way.

Downloads

263

Readme

@surrealdb/fabric

The shared SurrealDB Fabric API integration — a typed OpenAPI client, TanStack Query hooks, and the support ticketing surfaces built on top of them. Published to npm so every SurrealDB frontend can talk to api.surrealdb.com the same way.

This is the sibling of @surrealdb/ui: same tooling, same conventions, but no Storybook — it is an integration package, not purely a UI library.

What's inside

| Area | Contents | |---|---| | API client | configureFabricClient, getFabricClient, fabricFetch, FabricApiError — an openapi-fetch client typed against the generated paths | | Generated types | src/api/openapi.yml, src/api/schema.ts and every type in src/api/types.ts — all derived from the OpenAPI spec, regenerated with bun run schema | | Hooks | 37 generated TanStack Query hooks — one per API operation, across Accounts, Sidekick, Support, University and Website | | Components | Contact modal, help search palette, conversation table, conversation detail, article and collection views — one folder each under src/components, styled with Mantine props rather than custom CSS |

Installation

bun add @surrealdb/fabric

Peer dependencies: react 19, @mantine/core/@mantine/hooks/@mantine/dates 9, @surrealdb/ui, @tanstack/react-query 5, and date-fns 4. No auth SDK — this package never imports one.

Import the stylesheet once, alongside the @surrealdb/ui one:

import "@surrealdb/fabric/styles.css";

Setup

One provider, one import path. apiBase is the only required field.

import { FabricProvider } from "@surrealdb/fabric";

<QueryClientProvider client={queryClient}>
    <FabricProvider config={{ apiBase: import.meta.env.VITE_FABRIC_API }}>
        <App />
    </FabricProvider>
</QueryClientProvider>;

That is the whole setup for a project with no login — the website, the docs. Blogs, events, press, banners and the help centre all work.

Adding a session

Login is optional. Hand the provider a way to fetch a token and the account-bound hooks switch on:

const { getAccessTokenSilently } = useAuth0();

<FabricProvider config={{ apiBase, getAccessToken: getAccessTokenSilently }}>

authenticated is inferred from getAccessToken, so that one field is enough. Pass authenticated: isAuthenticated as well if you also want requests suppressed while the user is signed out. Any auth stack works — nothing here imports Auth0, or knows it exists.

What happens without a session

Hooks that need a login stay disabled and say so, once, rather than failing silently or firing a doomed request:

[fabric] useConversationsQuery is disabled because it needs a signed-in session.
Pass `getAccessToken` to <FabricProvider> — e.g. `getAccessToken: getAccessTokenSilently`
from useAuth0(). Public data (blogs, events, press, banners, help centre) needs no session.

The query simply never fetches, so rendering an account-bound surface in a project without login is harmless.

| Field | Purpose | |---|---| | apiBase | Required. e.g. https://api.surrealdb.com | | getAccessToken | Enables account-bound hooks. Omit for public data only | | authenticated | Defaults to whether getAccessToken was given. Set it to also suppress requests while signed out | | user | Labels replies the current user is composing | | supportTickets | Mirrors the host's support_tickets flag (default true) | | teamAvatar | Avatar for staff and bot authors without one of their own | | openUrl | External links. Defaults to window.open(url, "_blank") |

Navigation

This package never navigates on its own, and never assumes a route exists. Where a component goes is a prop on that component, not global configuration — so the same card can open a drawer in one place and route in another.

<SupportCenterView
    onSelectConversation={(c) => router.push(`/support/conversations/${c.id}`)}
    onSelectCollection={(c) => router.push(`/support/collections/${c.id}`)}
    onViewRequests={() => router.push("/support/requests")}
/>

<ConversationView
    id={id}
    onBack={() => router.push("/support/requests")}
    onOpenSupport={() => router.push("/support")}
/>

Every handler is optional, and omitting one is a real choice rather than a broken link:

| Prop | Omitted | |---|---| | ConversationTable onSelect | Rows are not clickable | | ArticleCard / CollectionCard onSelect | Opens the record's public help-centre URL | | HelpSearchModal onSelect | Same — opens the article externally | | ContactModal onCreated | Nothing happens after creation | | *View onOpenSupport / onBack | Those buttons are hidden |

Help-centre articles and collections carry a public url, so those degrade to an external link rather than vanishing — a docs site can render the help centre with no routing at all. Handlers receive the whole record, so you can route on any field.

Composite views forward to what they render: CollectionView passes onSelectArticle down to its ArticleCards, SupportCenterView passes onSelectConversation to its ConversationTable.

resolveOrganizationName is likewise a ConversationTable prop — an app with no org concept simply omits it and the label is left off.

The provider carries only what is genuinely host-wide: the API base, the session, openUrl (a shell capability — Electron versus browser), and the branding used to label authors.

Conversations and tickets

Conversations are not organization-bound — opening one needs no configuration beyond apiBase:

import { openContactModal } from "@surrealdb/fabric";

openContactModal({
    type: "conversation",
    conversationType: "general",
    subject: "Account / billing enquiry",
});

Organization tickets are opt-in. Pass the eligible organizations and the contact modal gains ticket mode; resolving which organizations hold a support plan is the host's job, since that is Cloud API data rather than Fabric's:

<FabricSupportModals organizations={organizationsWithSupportPlan} />;

openContactModal({ type: "ticket", organization: org.id });

Mount <FabricSupportModals /> once, near the root and inside FabricProvider.

Hooks

Every operation in the Fabric API gets a hook, and they are generated. src/generated/ is emitted from the OpenAPI document — 37 hooks across Accounts, Sidekick, Support, University and Website — so the package covers the whole API rather than a hand-picked subset.

const { data: conversations } = useConversationsQuery();
const { data: article } = useSupportArticleQuery({ id });
const { data: tokens } = usePersonalAccessTokensQuery();
const { data: courses } = useUniversityCoursesQuery();

const reply = useConversationReplyMutation();
await reply.mutateAsync({ id, body: "Thanks!" });

queryClient.invalidateQueries({ queryKey: fabricKeys.supportCloudConversations() });

Queries take (params?, options?) — path and query params in one object, TanStack options second. Mutations take no arguments; path params travel with the variables, so one hook instance serves any id.

Everything structural comes from the spec: request/response types, params, query keys. Names and behaviour that the spec cannot express come from tools/hook-policy.ts — session gating, refetch intervals, cache invalidation, and friendlier names than the derived use<Path>Query.

Adding an endpoint to the API needs no code here: re-run bun run schema and it appears with a derived name and full types.

Two operations are deliberately skipped, since neither returns a cacheable JSON payload: the PDF course certificate and the SSE chat stream. The generator reports them, and a test pins the list.

Keys are rooted at ["fabric", …] and exported as fabricKeys, one factory per path.

Regenerating the API schema

Every payload type in this package is generated from the Fabric OpenAPI document — there are no hand-written response interfaces. If a payload comes back as unknown, the fix belongs in api.surrealdb.com (declare the response schema on the route), not here.

The OpenAPI document and its generated types are committed so the package builds offline.

bun run schema
bun run schema:prod

To generate against a locally running api.surrealdb.com — the fastest loop when you're changing a response schema — start it there with bun run dev, then:

bun run schema:dev

That targets http://localhost:3000/openapi/public.yaml; set FABRIC_DEV_PORT if the API listens elsewhere. All three write src/api/openapi.yml and src/api/schema.ts. Pass --url=<spec-url> (or set FABRIC_OPENAPI_URL) for any other deployment. Commit the result — but commit a staging or production regeneration, not a schema:dev one, so the checked-in types match a deployed API.

Commands

| Command | Description | |---|---| | bun install | Install dependencies | | bun run build | Production build to dist/ | | bun qc | Biome lint/format check | | bun qa | Biome autofix | | bun qts | TypeScript check | | bun run test | Run the Vitest suite | | bun run hooks | Regenerate src/generated/ from the committed spec | | bun run schema | Re-download the staging spec, then regenerate types and hooks | | bun run schema:dev | Same, from a locally running API (FABRIC_DEV_PORT, default 3000) | | bun run schema:prod | Same, from production |