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

@emulates/formbricks

v2.3.1

Published

Stateful emulator of open-source Formbricks: the client environment state (seeded with a synthetic survey corpus), response creation with upstream's validation and {code, message, details} errors, the v1 management API, the widget script, and signed respo

Readme

@emulates/formbricks

Part of Emulates: high-fidelity, in-process emulators for APIs and databases.

Stateful emulator of open-source Formbricks (6.x) for test suites: the client environment state the JS SDK loads surveys from, response creation with upstream's validation and {code, message, details} errors, the v1 management API (responses and surveys, x-api-key), the widget script the web SDK loads, and the response pipeline's responseCreated / responseFinished webhooks. Every namespace is seeded with a small synthetic survey corpus (src/corpus/surveys.json: NPS, Onboarding, Product Feedback, a link survey and a paused survey; invented content).

  • Operation coverage: SUPPORT.md
  • The contract (openapi.yaml) is hand-authored from upstream Formbricks 6.0.0's route handlers and the docs.

Install

npm install -D @emulates/formbricks

ESM only. Node >= 22 or Bun >= 1.2. No native dependencies. Serve it with npx emulates-formbricks serve, createServer from ./server (Node), or createRuntime with any Fetch server.

Usage

Point the SDK's appUrl (and your management API base URL) at the emulator, use the workspace id cworkspace000000000000001 (or its legacy environment id cenvironment0000000000001, as older SDKs send), and any management API key.

npx emulates-formbricks serve --port 8813 \
  --webhook-url http://127.0.0.1:3000/webhooks/formbricks \
  --webhook-secret "whsec_…"
import { createRuntime, WORKSPACE_ID } from "@emulates/formbricks"

const formbricks = createRuntime({
  webhooks: { url: "http://127.0.0.1:3000/webhooks/formbricks", secret: "whsec_…" },
})
// …the app POSTs /api/v2/client/<workspace>/responses {surveyId, finished: true, data}…
// the mock validates it, stores it, and posts responseFinished to the webhook.
const response = await formbricks.fetch(new Request("http://formbricks.test/__admin/responses"))

Routes

Client routes take a workspace id or a legacy (pre-Formbricks-5) environment id in the path; both resolve to the same workspace.

| Route | Behaviour | | --- | --- | | GET /api/v1/client/{workspace}/environment | {data: {data: {surveys, actionClasses, workspace, project}, expiresAt}} (project is upstream's legacy alias of workspace). Only app surveys inProgress, with the SDK's survey fields and projectOverwrites mirroring workspaceOverwrites. A malformed id is 400 Invalid ID format; an unknown one 404 Workspace not found. | | POST /api/v2/client/{workspace}/responses | {surveyId, finished, data, contactId?, meta?, …} → {data: {id, quotaFull: false}}. An unknown workspace is 404; input errors are 400 Fields are missing or incorrectly formatted with zod 4 details ({surveyId: "Invalid cuid2", finished: "Invalid input: expected boolean, received undefined"}; userId is not part of the v2 input and is ignored); a contactId while contacts are off is 403 (Enterprise); an unknown survey 404; a survey of another workspace 400 Survey is part of another workspace; a survey not inProgress 403 Survey is not accepting submissions. data keeps only element ids and declared hidden fields (hiddenFields.fieldIds); other keys are dropped. Before element validation, a multipleChoiceSingle or multipleChoiceMulti value (a string, or any string in an array) that equals no choice label in the response language — the body's language, or the label's default when it is omitted; getLocalizedValue does not fall back — and is longer than 250 characters is 400 Response exceeds character limit with details: {questionId}, and nothing is stored. An exact label match is not length-checked; an unmatched entry of 250 characters or fewer passes this check. Element validation defaults to the elements present in data (finished or not). validation: "finished-validates-all" (pre-#7292 Formbricks) checks every element when finished is true and only the present ones when it is false; a failure is 400 and stores nothing. Either way: required → Please fill out this field; a choice outside a choice element's options (when it has no "other") → Please enter a valid format; openText email / url / phone → Please enter a valid …; 400 Validation failed with details: {"response.data.<elementId>": "<messages joined by '; '>"}. meta keeps source, url, action and the auto-captured page / UTM / screen keys, plus userAgent and country (from CF-IPCountry). language is canonicalized (en → en-US); a finished response's ttc gains _total. | | GET /api/v1/management/responses?surveyId=&limit=&skip= | {data: [response]}, newest first; an unknown surveyId is 404. | | GET /api/v1/management/responses/{id} | {data: response} or 404. | | GET /api/v1/management/surveys?limit=&offset= | {data: [survey]}, most recently updated first. | | POST /api/v1/management/surveys | {workspaceId (or legacy environmentId), name, type?, status?, blocks \| questions} → {data: survey}. Legacy questions become one block each; both or neither is 400; a missing workspace id is 400 workspaceId must be provided, an unknown one 404. | | GET /api/v1/management/surveys/{id} | {data: survey} or 404. | | GET /js/formbricks.umd.cjs | A no-op script defining window.formbricks (every SDK method resolves). |

Management surveys carry questions derived from blocks (block button labels on the block's last element), workspaceId, the legacy environmentId, and projectOverwrites. Management routes need x-api-key (any non-empty key, unless apiKeys is set), else 401 {code: "not_authenticated", message: "Not authenticated", details: {"x-Api-Key": "…"}}.

Webhooks

POST <webhook-url> with {webhookId, event, data: {...response, survey: {title, type, status, createdAt, updatedAt}}}, headers webhook-id, webhook-timestamp and, when the endpoint has a whsec_… secret, webhook-signature: v1,<base64 HMAC-SHA256> (Standard Webhooks). Only events an endpoint subscribes to are sent: responseFinished by default (events: ["responseCreated", "responseFinished"] for both). --webhook-token appends a ?token= for receivers that check one. Retries, GET /__admin/webhooks, …/events, …/replay, …/flush and PUT /__admin/webhook-endpoints work as usual.

Admin (beyond the standard contract)

| Route | Effect | | --- | --- | | GET /__admin/responses?surveyId= | Stored responses, oldest first. | | GET\|PUT /__admin/surveys | Read, or add / replace surveys ([{id, name, type?, status?, workspaceId?, blocks?, questions?, …}]; no workspaceId = served by every configured workspace). | | GET\|PUT /__admin/contacts | Read, or add contacts ([{id, userId?, attributes?, workspaceId?}]) for contactId responses. | | GET\|PUT /__admin/settings | {workspaces?, legacyEnvironmentIds?, apiKeys?, webhookId?, contactsEnabled?, validation?}. validation is present-only (default, Formbricks after #7292) or finished-validates-all (before #7292: a finished response validates every element). createRuntime({settings}) accepts the same fields. |

Fault presets (POST /__admin/faults {"preset": "<name>", "count"?: n}; GET /__admin/faults/presets): rate_limited (429 too_many_requests), server_error (500), missing_response_id (stored, but no id in the answer), environment_unavailable, data_as_string (management data as a JSON string), management_unauthorized, connection_drop, duplicate (the next webhook twice), webhook_drop, webhook_reorder.

Namespaces

x-emulates-namespace, a /__admin/ns/<name> prefix on the app URL, or by credential: the workspace (or legacy environment) id in the client paths (the SDK cannot add headers) or the management x-api-key, through PUT /__admin/credentials {"credentials": {"<workspace id or key>": "<namespace>"}}.

Deliberately not modelled

  • Displays, contacts / identify, user and storage routes of the client API (the widget script is a no-op, so the SDK never calls them); response updates (PUT …/responses/{id}).
  • Quotas (always quotaFull: false), single-use links, PIN protection, reCAPTCHA, email verification, file-upload checks, custom validation.rules, survey logic, and the full survey schema on create (only the shapes above are checked).
  • The Embedded Data ingest contract beyond its allow-list (locked fields, coercion, size limits), userAgent parsing (always {device: "desktop"}), and language canonicalization beyond common bare codes.
  • Organizations, API-key permissions (unauthorized), and the rest of the management API; the Formbricks UI.

API

| Export | Kind | Description | | --- | --- | --- | | FormbricksAPI | class | The in-process emulator: fetch(request), reset(), responses(), render(record), wireSurvey(survey), pipeline(event, record, survey), state. Options: sqlite, now, namespace, surveys, settings, onWebhook. | | createRuntime | function | The emulator with the full service contract (health, admin, namespaces, credentials, presets, webhooks). Options: webhooks: {url, secret?, events?, retryDelaysMs?, fetch?}, surveys, settings, clock, seed, adminKey, onLog, sqlite. | | FORMBRICKS_PRESETS | object | Every named fault preset. | | FORMBRICKS_NAMESPACE | string | The service name, "formbricks". | | WEBHOOK_PATH | string | A conventional receiver path for examples, /webhooks/formbricks. | | formbricksCredential | function | The workspace id or API key a request carries. | | formbricksError | function | Build Formbricks' {code, message, details} error response. | | validateResponseData | function | Upstream's element validation: (survey, data, language?, {validation?, finished?}) → {elementId: messages} or null. | | CORPUS_SURVEYS | value | The synthetic survey corpus every namespace is seeded with. | | WORKSPACE_ID, ENVIRONMENT_ID, DEFAULT_SETTINGS | values | The default workspace, its legacy environment id, and the default settings. | | document, operationIds, supportedOperationIds | values | The vendored OpenAPI contract and its operation ids. | | createServer, serveTarget, DEFAULT_PORT (./server) | Node | Serve over node:http; the serve CLI target; port 8813. |

Part of Emulates.