@crvouga/mockingbird-service-formbricks 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog — @crvouga/mockingbird-service-formbricks
2
+
3
+ ## 0.1.0 (2026-09-22)
4
+
5
+ Initial release.
package/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # @crvouga/mockingbird-service-formbricks
2
+
3
+ Stateful mock of **Formbricks** (as our fork, geviti-formbrick, serves it) for test suites: the
4
+ client environment state our member app and backend load surveys from, response creation with
5
+ the fork's validation and `{code, message, details}` errors, the v1 management API (responses and
6
+ surveys, `x-api-key`), the widget script the web SDK loads, and the **`responseFinished`
7
+ webhook** that nothing sends in non-prod today. Surveys are seeded from our committed production
8
+ clone (`packages/forms-fixtures/formbricks/prod-clone.json`).
9
+
10
+ - Operation coverage: [SUPPORT.md](https://github.com/crvouga/mockingbird/blob/main/packages/service/formbricks/SUPPORT.md)
11
+ - The contract (`openapi.yaml`) is hand-authored from the fork's route handlers and our
12
+ consumers.
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ npm install -D @crvouga/mockingbird-service-formbricks
18
+ ```
19
+
20
+ ESM only. Node >= 22 or Bun >= 1.2. No native dependencies. Serve it with
21
+ `npx mockingbird-formbricks serve`, `createServer` from `./server` (Node), or `createRuntime`
22
+ with any Fetch server.
23
+
24
+ ## Usage
25
+
26
+ Point `FORMBRICKS_APP_URL` at the mock (instead of the backend's compat shim), keep
27
+ `FORMBRICKS_ENVIRONMENT_ID` (`cmlhiza9j0009lj01my5c1g0q` production or `cmlhiza9c0004lj01jbuhp0nz`
28
+ development; both serve the clone), set any `FORMBRICKS_API_KEY`, and pass the backend's
29
+ `FORMBRICKS_WEBHOOK_SECRET` as `--webhook-token`.
30
+
31
+ ```bash
32
+ npx mockingbird-formbricks serve --port 8813 \
33
+ --webhook-url http://127.0.0.1:3000/onboarding-tasks/formbricks-webhook \
34
+ --webhook-token "$FORMBRICKS_WEBHOOK_SECRET"
35
+ ```
36
+
37
+ ```ts
38
+ import { createRuntime } from "@crvouga/mockingbird-service-formbricks"
39
+
40
+ const formbricks = createRuntime({
41
+ webhooks: { url: "http://127.0.0.1:3000/onboarding-tasks/formbricks-webhook?token=s3cret" },
42
+ })
43
+ // …the member app POSTs /api/v2/client/<env>/responses {surveyId, finished: true, data, userId}…
44
+ // the mock validates it, stores it, and posts responseFinished to the backend.
45
+ const response = await formbricks.fetch(new Request("http://formbricks.test/__admin/responses"))
46
+ ```
47
+
48
+ ### Routes
49
+
50
+ | Route | Behaviour |
51
+ | --- | --- |
52
+ | `GET /api/v1/client/{env}/environment` | `{data: {data: {surveys, actionClasses, project}, expiresAt}}` (our consumers read `data.surveys ?? data.data.surveys`). Surveys are served as stored (blocks, elements, logic, `questions`), `inProgress` only. A malformed env id is 400 `Invalid environment ID format`; an unknown one 404 `not_found`. |
53
+ | `POST /api/v2/client/{env}/responses` | `{surveyId, finished, data, userId?, meta?, …}` → `{data: {id, quotaFull: false}}`. Input errors are 400 `Fields are missing or incorrectly formatted` with zod-style `details` (`{surveyId: "Invalid cuid2", finished: "Required"}`); an unknown survey 404; a survey of another environment 400. Element validation (every element when `finished`, only present ones otherwise): required → `Please fill out this field`; openText email / url / phone → `Please enter a valid …`; 400 `Validation failed` with `details: {"response.data.<elementId>": "<messages joined by '; '>"}`. `userId` (the member's email) finds-or-creates a contact (`contact: {id, userId}`); unknown top-level and `meta` keys are stripped. |
54
+ | `GET /api/v1/management/responses?surveyId=&limit=&skip=` | `{data: [response]}`, newest first. |
55
+ | `GET /api/v1/management/responses/{id}` | `{data: response}` or 404. |
56
+ | `GET /api/v1/management/surveys?environmentId=` | `{data: [survey]}`. |
57
+ | `POST /api/v1/management/surveys` | `{environmentId, name, type?, status?, questions?, blocks?}` → `{data: survey}` (it belongs to that environment only). |
58
+ | `GET /api/v1/management/surveys/{id}` | `{data: survey}` (our intake sync reads `questions[].metadata.intakeField`) or 404. |
59
+ | `GET /js/formbricks.umd.cjs` | A no-op script defining `window.formbricks` (every SDK method resolves). |
60
+
61
+ Management routes need `x-api-key` (any non-empty key, unless `apiKeys` is set), else 401
62
+ `{code: "not_authenticated", message: "Not authenticated", details: {"x-Api-Key": "…"}}`.
63
+
64
+ ### Webhooks
65
+
66
+ `POST <webhook-url>` (carrying our `?token=`) with `{webhookId, event, data: {...response,
67
+ survey: {title, type, status, createdAt, updatedAt}}}`, headers `webhook-id`,
68
+ `webhook-timestamp` and, when the endpoint has a `whsec_…` secret, `webhook-signature: v1,<base64
69
+ HMAC-SHA256>` (Standard Webhooks; our receiver does not verify it). Only events an endpoint
70
+ subscribes to are sent: `responseFinished` by default (`events: ["responseCreated",
71
+ "responseFinished"]` for both). Retries, `GET /__admin/webhooks`, `…/events`, `…/replay`,
72
+ `…/flush` and `PUT /__admin/webhook-endpoints` work as usual.
73
+
74
+ ### Admin (beyond the standard contract)
75
+
76
+ | Route | Effect |
77
+ | --- | --- |
78
+ | `GET /__admin/responses?surveyId=` | Stored responses, oldest first. |
79
+ | `GET\|PUT /__admin/surveys` | Read, or add / replace surveys (`[{id, name, type?, status?, environmentId?, blocks?, questions?, …}]`; no `environmentId` = served by every configured environment). |
80
+ | `GET\|PUT /__admin/settings` | `{environments?, apiKeys?, webhookId?, contactsEnabled?}`. |
81
+
82
+ Fault presets (`POST /__admin/faults {"preset": "<name>", "count"?: n}`; `GET /__admin/faults/presets`):
83
+ `rate_limited` (429 `too_many_requests`; the member app retries ×3), `server_error` (500),
84
+ `missing_response_id` (stored, but no id in the answer), `environment_unavailable`,
85
+ `data_as_string` (management `data` as a JSON string), `management_unauthorized`,
86
+ `connection_drop`, `duplicate` (the next webhook twice: our receiver does not dedupe),
87
+ `webhook_drop`, `webhook_reorder`.
88
+
89
+ ### Namespaces
90
+
91
+ `x-mockingbird-namespace`, a `/ns/<name>` prefix on `FORMBRICKS_APP_URL`, or by credential: the
92
+ environment id in the client paths (the member app's fetch cannot add headers) or the
93
+ management `x-api-key`, through `PUT /__admin/credentials {"credentials": {"<env id or key>":
94
+ "<namespace>"}}`.
95
+
96
+ ### Deliberately not modelled
97
+
98
+ - Formbricks' **Postgres**: backend eRx and the form sources also read it directly
99
+ (`FORMBRICKS_DATABASE_URL`); an HTTP mock cannot cover that path.
100
+ - The rest of the JS SDK protocol (displays, contacts/identify, `app/sync`, storage): the widget
101
+ script is a no-op, so the SDK never calls them.
102
+ - Quotas (always `quotaFull: false`), single-use links, reCAPTCHA, the "other" option length
103
+ check, custom `validation.rules` on elements (only `required` and the implicit openText
104
+ email / url / phone rules are applied), response updates (`PUT …/responses/{id}`).
105
+ - Survey editing beyond create; the Formbricks UI.
106
+
107
+ ## API
108
+
109
+ | Export | Kind | Description |
110
+ | --- | --- | --- |
111
+ | `FormbricksAPI` | class | The in-process mock: `fetch(request)`, `reset()`, `responses()`, `render(record)`, `pipeline(event, record, survey)`, `state`. Options: `sqlite`, `now`, `namespace`, `surveys`, `settings`, `onWebhook`. |
112
+ | `createRuntime` | function | The mock 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`. |
113
+ | `FORMBRICKS_PRESETS` | object | Every named fault preset. |
114
+ | `FORMBRICKS_NAMESPACE` | string | The service name, `"formbricks"`. |
115
+ | `WEBHOOK_PATH` | string | Our receiver's path, `/onboarding-tasks/formbricks-webhook`. |
116
+ | `formbricksCredential` | function | The environment id or API key a request carries. |
117
+ | `formbricksError` | function | Build the fork's `{code, message, details}` error response. |
118
+ | `validateResponseData` | function | The fork's element validation: `(survey, data, finished)` → `{elementId: messages}` or `null`. |
119
+ | `PROD_CLONE_SURVEYS`, `PROD_CLONE_EXPORTED_AT` | values | The seeded production survey clone and when it was exported. |
120
+ | `PRODUCTION_ENVIRONMENT_ID`, `DEVELOPMENT_ENVIRONMENT_ID`, `DEFAULT_SETTINGS` | values | Our environment ids and the default settings. |
121
+ | `document`, `operationIds`, `supportedOperationIds` | values | The vendored OpenAPI contract and its operation ids. |
122
+ | `createServer`, `serveTarget`, `DEFAULT_PORT` (`./server`) | Node | Serve over `node:http`; the `serve` CLI target; port 8813. |
123
+
124
+ Part of [mockingbird](https://github.com/crvouga/mockingbird).
@@ -0,0 +1,370 @@
1
+ import {
2
+ createRuntime
3
+ } from "./chunk-FZI2QHRQ.js";
4
+
5
+ // ../../adapters/node/dist/cli.js
6
+ import { readFile } from "node:fs/promises";
7
+ import { parseArgs } from "node:util";
8
+
9
+ // ../../adapters/node/dist/serve.js
10
+ import { createServer } from "node:http";
11
+ var serve = async (api, options = {}) => {
12
+ const server = createServer(async (req, res) => {
13
+ const chunks = [];
14
+ for await (const chunk of req) {
15
+ chunks.push(Buffer.from(chunk));
16
+ }
17
+ const body = Buffer.concat(chunks);
18
+ const address = server.address();
19
+ const port = typeof address === "object" && address !== null ? address.port : void 0;
20
+ const base = `http://${req.headers.host ?? `localhost:${port ?? 80}`}`;
21
+ const raw = req.url ?? "/";
22
+ const url = new URL(raw.replace(/^\/+/, "/"), base);
23
+ const method = req.method ?? "GET";
24
+ const init = { method, headers: req.headers };
25
+ if (method !== "GET" && method !== "HEAD" && body.length > 0) {
26
+ init.body = body;
27
+ }
28
+ const aborted = new AbortController();
29
+ res.once("close", () => {
30
+ if (!res.writableFinished)
31
+ aborted.abort();
32
+ });
33
+ init.signal = aborted.signal;
34
+ const request = new Request(url, init);
35
+ let response;
36
+ try {
37
+ response = await api.fetch(request);
38
+ } catch (error) {
39
+ if (error.code === "MOCKINGBIRD_DROP") {
40
+ req.socket.destroy();
41
+ return;
42
+ }
43
+ res.writeHead(500, { "content-type": "application/json" });
44
+ res.end(JSON.stringify({
45
+ error: {
46
+ type: "mockingbird_internal",
47
+ message: error instanceof Error ? error.message : String(error)
48
+ }
49
+ }));
50
+ return;
51
+ }
52
+ const headers = Object.fromEntries(response.headers);
53
+ const cookies = response.headers.getSetCookie();
54
+ if (cookies.length > 0)
55
+ headers["set-cookie"] = cookies;
56
+ if (!response.body) {
57
+ res.writeHead(response.status, headers);
58
+ res.end();
59
+ return;
60
+ }
61
+ res.writeHead(response.status, headers);
62
+ res.flushHeaders();
63
+ const reader = response.body.getReader();
64
+ try {
65
+ for (; ; ) {
66
+ const { done, value } = await reader.read();
67
+ if (done)
68
+ break;
69
+ if (!res.write(value))
70
+ await new Promise((resolve) => res.once("drain", resolve));
71
+ }
72
+ res.end();
73
+ } catch {
74
+ res.destroy();
75
+ } finally {
76
+ reader.releaseLock();
77
+ }
78
+ });
79
+ await new Promise((resolve, reject) => {
80
+ server.once("error", reject);
81
+ server.listen(options.port ?? 0, options.host, resolve);
82
+ });
83
+ return server;
84
+ };
85
+
86
+ // ../../adapters/node/dist/listen.js
87
+ var listen = async (api, options = {}) => {
88
+ const host = options.host ?? "127.0.0.1";
89
+ const server = await serve(api, { port: options.port ?? 0, host });
90
+ const address = server.address();
91
+ const port = typeof address === "object" && address !== null ? address.port : options.port ?? 0;
92
+ const shown = host.includes(":") ? `[${host}]` : host;
93
+ return {
94
+ url: `http://${shown}:${port}`,
95
+ port,
96
+ host,
97
+ server,
98
+ close: () => new Promise((resolve, reject) => {
99
+ server.close((error) => {
100
+ if (error && error.code !== "ERR_SERVER_NOT_RUNNING")
101
+ reject(error);
102
+ else
103
+ resolve();
104
+ });
105
+ server.closeAllConnections?.();
106
+ })
107
+ };
108
+ };
109
+
110
+ // ../../adapters/node/dist/cli.js
111
+ var optionHelp = (options) => Object.entries(options).map(([name, option]) => {
112
+ const flag = `--${name}${option.type === "string" ? ` ${option.value ?? "<value>"}` : ""}`;
113
+ const fallback = option.default !== void 0 ? ` (default: ${String(option.default)})` : "";
114
+ return ` ${flag.padEnd(30)} ${option.description}${fallback}`;
115
+ });
116
+ var help = (spec) => [
117
+ `${spec.bin} \u2014 ${spec.description}`,
118
+ "",
119
+ "Usage:",
120
+ ` ${spec.bin} <command> [options]`,
121
+ "",
122
+ "Commands:",
123
+ ...Object.entries(spec.commands).map(([name, c]) => ` ${name.padEnd(30)} ${c.summary}`),
124
+ "",
125
+ `Run \`${spec.bin} <command> --help\` for a command's options.`
126
+ ].join("\n");
127
+ var commandHelp = (spec, name, command) => [
128
+ `${spec.bin} ${name} \u2014 ${command.summary}`,
129
+ "",
130
+ "Usage:",
131
+ ` ${command.usage ?? `${spec.bin} ${name} [options]`}`,
132
+ ...command.options ? ["", "Options:", ...optionHelp(command.options)] : []
133
+ ].join("\n");
134
+ var runCli = async (spec, argv) => {
135
+ const pair = argv.length >= 2 ? `${argv[0]} ${argv[1]}` : void 0;
136
+ const words = pair !== void 0 && spec.commands[pair] ? 2 : 1;
137
+ const name = words === 2 ? pair : argv[0];
138
+ const rest = argv.slice(words);
139
+ if (name === void 0 || name === "--help" || name === "-h" || name === "help") {
140
+ console.log(help(spec));
141
+ return 0;
142
+ }
143
+ const command = spec.commands[name];
144
+ if (!command) {
145
+ console.error(`${spec.bin}: unknown command ${JSON.stringify(name)}
146
+
147
+ ${help(spec)}`);
148
+ return 2;
149
+ }
150
+ if (rest.includes("--help") || rest.includes("-h")) {
151
+ console.log(commandHelp(spec, name, command));
152
+ return 0;
153
+ }
154
+ let parsed;
155
+ try {
156
+ parsed = parseArgs({
157
+ args: rest,
158
+ allowPositionals: true,
159
+ strict: true,
160
+ options: Object.fromEntries(Object.entries(command.options ?? {}).map(([key, option]) => [
161
+ key,
162
+ {
163
+ type: option.type,
164
+ ...option.default !== void 0 ? { default: option.default } : {}
165
+ }
166
+ ]))
167
+ });
168
+ } catch (error) {
169
+ console.error(`${spec.bin} ${name}: ${error instanceof Error ? error.message : String(error)}
170
+
171
+ ${commandHelp(spec, name, command)}`);
172
+ return 2;
173
+ }
174
+ return command.run(parsed.values, parsed.positionals);
175
+ };
176
+ var COMMON_SERVE_OPTIONS = {
177
+ port: { type: "string", value: "<port>", description: "Port to listen on" },
178
+ host: { type: "string", value: "<host>", description: "Interface to bind", default: "127.0.0.1" },
179
+ "admin-key": {
180
+ type: "string",
181
+ value: "<key>",
182
+ description: "Require x-mockingbird-admin-key on /__admin/* (env MOCKINGBIRD_ADMIN_KEY)"
183
+ },
184
+ seed: { type: "string", value: "<seed>", description: "Seed for every random choice" },
185
+ log: {
186
+ type: "string",
187
+ value: "<pretty|json|off>",
188
+ description: "Request log format",
189
+ default: "pretty"
190
+ },
191
+ "log-requests": {
192
+ type: "boolean",
193
+ description: "One JSON line per request: namespace, operationId, status, ids touched (never bodies). Same as --log json"
194
+ },
195
+ config: {
196
+ type: "string",
197
+ value: "<file>",
198
+ description: "Serve every service in a mockingbird.json config instead"
199
+ }
200
+ };
201
+ var formatLog = (format) => {
202
+ if (format === "off")
203
+ return void 0;
204
+ if (format === "json")
205
+ return (entry) => console.log(JSON.stringify(entry));
206
+ return (entry) => {
207
+ const op = entry.operationId ?? (entry.unmatched ? "UNMATCHED" : "-");
208
+ const ns = entry.namespace === "default" ? "" : ` [${entry.namespace}]`;
209
+ const fault = entry.faultId ? ` fault=${entry.faultId}` : "";
210
+ const adopted = entry.adopted ? " adopted" : "";
211
+ console.log(`${entry.service} ${entry.method} ${entry.path} ${entry.status} ${op} ${entry.durationMs}ms${ns}${fault}${adopted}`);
212
+ };
213
+ };
214
+ var asString = (value) => typeof value === "string" ? value : void 0;
215
+ var loadTarget = async (name, own) => {
216
+ if (name === own.name)
217
+ return own;
218
+ const specifier = `@crvouga/mockingbird-service-${name}/server`;
219
+ try {
220
+ const mod = await import(specifier);
221
+ if (!mod.serveTarget)
222
+ throw new Error(`${specifier} exports no serveTarget`);
223
+ return mod.serveTarget;
224
+ } catch (error) {
225
+ const reason = error instanceof Error ? error.message : String(error);
226
+ throw new Error(`cannot load service "${name}": ${reason}. Install @crvouga/mockingbird-service-${name}.`);
227
+ }
228
+ };
229
+ var start = async (target, values, config) => {
230
+ const runtime = await target.create(values, {
231
+ adminKey: config.adminKey,
232
+ seed: config.seed,
233
+ onLog: formatLog(config.log)
234
+ });
235
+ const listening = await listen(runtime, { port: config.port, host: config.host });
236
+ console.log(`${target.name} mock listening on ${listening.url}`);
237
+ console.log(`${target.name} health: GET ${listening.url}/health`);
238
+ console.log(`${target.name} admin: ${listening.url}/__admin (${config.adminKey ? "x-mockingbird-admin-key required" : "open \u2014 pass --admin-key to lock"})`);
239
+ for (const line of target.banner?.(runtime) ?? [])
240
+ console.log(`${target.name} ${line}`);
241
+ return listening;
242
+ };
243
+ var untilSignal = async (servers) => new Promise((resolve) => {
244
+ const stop = async () => {
245
+ await Promise.allSettled(servers.map((s) => s.close()));
246
+ resolve(0);
247
+ };
248
+ process.once("SIGINT", stop);
249
+ process.once("SIGTERM", stop);
250
+ });
251
+ var serveCommand = (target) => ({
252
+ summary: `Serve the ${target.name} mock over HTTP`,
253
+ options: { ...COMMON_SERVE_OPTIONS, ...target.options },
254
+ async run(values) {
255
+ const log = values["log-requests"] === true ? "json" : asString(values.log) ?? "pretty";
256
+ if (!["pretty", "json", "off"].includes(log)) {
257
+ console.error(`--log must be pretty, json or off (got ${log})`);
258
+ return 2;
259
+ }
260
+ const configPath = asString(values.config);
261
+ if (configPath !== void 0) {
262
+ const config = JSON.parse(await readFile(configPath, "utf8"));
263
+ const servers = [];
264
+ try {
265
+ for (const [name, entry] of Object.entries(config.services ?? {})) {
266
+ const each = await loadTarget(name, target);
267
+ servers.push(await start(each, entry.options ?? {}, {
268
+ port: entry.port ?? each.defaultPort,
269
+ host: entry.host ?? "127.0.0.1",
270
+ ...entry.adminKey !== void 0 ? { adminKey: entry.adminKey } : {},
271
+ ...entry.seed !== void 0 ? { seed: entry.seed } : {},
272
+ log: config.log ?? log
273
+ }));
274
+ }
275
+ } catch (error) {
276
+ await Promise.allSettled(servers.map((s) => s.close()));
277
+ console.error(error instanceof Error ? error.message : String(error));
278
+ return 1;
279
+ }
280
+ return untilSignal(servers);
281
+ }
282
+ const port = asString(values.port);
283
+ const adminKey = asString(values["admin-key"]) ?? process.env.MOCKINGBIRD_ADMIN_KEY;
284
+ const seed = asString(values.seed);
285
+ let listening;
286
+ try {
287
+ listening = await start(target, values, {
288
+ port: port === void 0 ? target.defaultPort : Number.parseInt(port, 10),
289
+ host: asString(values.host) ?? "127.0.0.1",
290
+ ...adminKey !== void 0 ? { adminKey } : {},
291
+ ...seed !== void 0 ? { seed } : {},
292
+ log
293
+ });
294
+ } catch (error) {
295
+ console.error(error instanceof Error ? error.message : String(error));
296
+ return 1;
297
+ }
298
+ return untilSignal([listening]);
299
+ }
300
+ });
301
+
302
+ // src/server.ts
303
+ var DEFAULT_PORT = 8813;
304
+ var createServer2 = async (options = {}) => {
305
+ const { port, host, ...rest } = options;
306
+ const runtime = createRuntime(rest);
307
+ const listening = await listen(runtime, {
308
+ port: port ?? 0,
309
+ ...host !== void 0 ? { host } : {}
310
+ });
311
+ return { ...listening, runtime };
312
+ };
313
+ var text = (value) => typeof value === "string" ? value : void 0;
314
+ var serveTarget = {
315
+ name: "formbricks",
316
+ defaultPort: DEFAULT_PORT,
317
+ options: {
318
+ "webhook-url": {
319
+ type: "string",
320
+ value: "<url>",
321
+ description: "Deliver webhooks here (e.g. http://127.0.0.1:3000/onboarding-tasks/formbricks-webhook)"
322
+ },
323
+ "webhook-token": {
324
+ type: "string",
325
+ value: "<token>",
326
+ description: "Appended as ?token= (the app's FORMBRICKS_WEBHOOK_SECRET)"
327
+ },
328
+ "webhook-secret": {
329
+ type: "string",
330
+ value: "<whsec_\u2026>",
331
+ description: "Standard Webhooks key that signs webhook-signature (optional)"
332
+ },
333
+ "environment-id": {
334
+ type: "string",
335
+ value: "<id>",
336
+ description: "An extra environment id that serves the survey fixture"
337
+ }
338
+ },
339
+ create: (values, common) => {
340
+ const url = text(values["webhook-url"]);
341
+ const token = text(values["webhook-token"]);
342
+ const secret = text(values["webhook-secret"]);
343
+ const environment = text(values["environment-id"]);
344
+ const target = url && token ? `${url}${url.includes("?") ? "&" : "?"}token=${encodeURIComponent(token)}` : url;
345
+ return createRuntime({
346
+ ...target ? { webhooks: { url: target, ...secret ? { secret } : {} } } : {},
347
+ ...environment ? {
348
+ settings: {
349
+ environments: [environment, "cmlhiza9j0009lj01my5c1g0q", "cmlhiza9c0004lj01jbuhp0nz"]
350
+ }
351
+ } : {},
352
+ ...common.adminKey !== void 0 ? { adminKey: common.adminKey } : {},
353
+ ...common.seed !== void 0 ? { seed: common.seed } : {},
354
+ ...common.onLog ? { onLog: common.onLog } : {}
355
+ });
356
+ },
357
+ banner: () => [
358
+ "point FORMBRICKS_APP_URL at this url; FORMBRICKS_ENVIRONMENT_ID cmlhiza9j0009lj01my5c1g0q (prod clone)",
359
+ "management API: x-api-key <any>; namespaces: x-mockingbird-namespace, /ns/<name>/\u2026, or PUT /__admin/credentials {<env id | api key>: <ns>}"
360
+ ]
361
+ };
362
+
363
+ export {
364
+ runCli,
365
+ serveCommand,
366
+ DEFAULT_PORT,
367
+ createServer2 as createServer,
368
+ serveTarget
369
+ };
370
+ //# sourceMappingURL=chunk-2FV4RSDZ.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../adapters/node/src/cli.ts", "../../../adapters/node/src/serve.ts", "../../../adapters/node/src/listen.ts", "../src/server.ts"],
4
+ "sourcesContent": ["import { readFile } from \"node:fs/promises\"\nimport { type ParseArgsConfig, parseArgs } from \"node:util\"\nimport type { RequestLog, ServiceInstance, ServiceRuntime } from \"@crvouga/mockingbird-service\"\nimport { type Listening, listen } from \"./listen.js\"\n\nexport type CliOption = {\n type: \"string\" | \"boolean\"\n description: string\n /** Shown in help; the value placeholder, e.g. `<port>`. */\n value?: string\n default?: string | boolean\n}\n\nexport type CliValues = Record<string, string | boolean | undefined>\n\nexport type CliCommand = {\n summary: string\n usage?: string\n options?: Record<string, CliOption>\n /** Resolves to an exit code; a server command resolves only when it stops. */\n run(values: CliValues, positionals: string[]): Promise<number>\n}\n\nexport type CliSpec = {\n bin: string\n description: string\n commands: Record<string, CliCommand>\n}\n\nconst optionHelp = (options: Record<string, CliOption>): string[] =>\n Object.entries(options).map(([name, option]) => {\n const flag = `--${name}${option.type === \"string\" ? ` ${option.value ?? \"<value>\"}` : \"\"}`\n const fallback = option.default !== undefined ? ` (default: ${String(option.default)})` : \"\"\n return ` ${flag.padEnd(30)} ${option.description}${fallback}`\n })\n\nconst help = (spec: CliSpec): string =>\n [\n `${spec.bin} \u2014 ${spec.description}`,\n \"\",\n \"Usage:\",\n ` ${spec.bin} <command> [options]`,\n \"\",\n \"Commands:\",\n ...Object.entries(spec.commands).map(([name, c]) => ` ${name.padEnd(30)} ${c.summary}`),\n \"\",\n `Run \\`${spec.bin} <command> --help\\` for a command's options.`,\n ].join(\"\\n\")\n\nconst commandHelp = (spec: CliSpec, name: string, command: CliCommand): string =>\n [\n `${spec.bin} ${name} \u2014 ${command.summary}`,\n \"\",\n \"Usage:\",\n ` ${command.usage ?? `${spec.bin} ${name} [options]`}`,\n ...(command.options ? [\"\", \"Options:\", ...optionHelp(command.options)] : []),\n ].join(\"\\n\")\n\n/** Parse `argv` against `spec` and run the chosen command. Resolves to an exit code. */\nexport const runCli = async (spec: CliSpec, argv: string[]): Promise<number> => {\n // Two-word commands (`corpus pull`) win over one-word ones.\n const pair = argv.length >= 2 ? `${argv[0]} ${argv[1]}` : undefined\n const words = pair !== undefined && spec.commands[pair] ? 2 : 1\n const name = words === 2 ? pair : argv[0]\n const rest = argv.slice(words)\n if (name === undefined || name === \"--help\" || name === \"-h\" || name === \"help\") {\n console.log(help(spec))\n return 0\n }\n const command = spec.commands[name]\n if (!command) {\n console.error(`${spec.bin}: unknown command ${JSON.stringify(name)}\\n\\n${help(spec)}`)\n return 2\n }\n if (rest.includes(\"--help\") || rest.includes(\"-h\")) {\n console.log(commandHelp(spec, name, command))\n return 0\n }\n let parsed: { values: CliValues; positionals: string[] }\n try {\n parsed = parseArgs({\n args: rest,\n allowPositionals: true,\n strict: true,\n options: Object.fromEntries(\n Object.entries(command.options ?? {}).map(([key, option]) => [\n key,\n {\n type: option.type,\n ...(option.default !== undefined ? { default: option.default } : {}),\n },\n ]),\n ) as ParseArgsConfig[\"options\"],\n }) as { values: CliValues; positionals: string[] }\n } catch (error) {\n console.error(\n `${spec.bin} ${name}: ${error instanceof Error ? error.message : String(error)}\\n\\n${commandHelp(spec, name, command)}`,\n )\n return 2\n }\n return command.run(parsed.values, parsed.positionals)\n}\n\n// \u2500\u2500 serve \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type LogFormat = \"pretty\" | \"json\" | \"off\"\n\nexport type CommonServeOptions = {\n adminKey: string | undefined\n seed: string | undefined\n onLog: ((entry: RequestLog) => void) | undefined\n}\n\n/**\n * What a service contributes to `serve`: how to build its runtime from CLI flags,\n * and what to say at startup. Every service's `./server` entry exports one as\n * `serveTarget`, which is also how `serve --config` finds services by name.\n */\nexport type ServeTarget = {\n name: string\n defaultPort: number\n /** Serve flags beyond the common ones. */\n options?: Record<string, CliOption>\n create(\n values: CliValues,\n common: CommonServeOptions,\n ): Promise<ServiceRuntime<ServiceInstance>> | ServiceRuntime<ServiceInstance>\n /** Startup lines after the listen address, e.g. the loaded corpus version. */\n banner?(runtime: ServiceRuntime<ServiceInstance>): string[]\n}\n\nconst COMMON_SERVE_OPTIONS: Record<string, CliOption> = {\n port: { type: \"string\", value: \"<port>\", description: \"Port to listen on\" },\n host: { type: \"string\", value: \"<host>\", description: \"Interface to bind\", default: \"127.0.0.1\" },\n \"admin-key\": {\n type: \"string\",\n value: \"<key>\",\n description: \"Require x-mockingbird-admin-key on /__admin/* (env MOCKINGBIRD_ADMIN_KEY)\",\n },\n seed: { type: \"string\", value: \"<seed>\", description: \"Seed for every random choice\" },\n log: {\n type: \"string\",\n value: \"<pretty|json|off>\",\n description: \"Request log format\",\n default: \"pretty\",\n },\n \"log-requests\": {\n type: \"boolean\",\n description:\n \"One JSON line per request: namespace, operationId, status, ids touched (never bodies). Same as --log json\",\n },\n config: {\n type: \"string\",\n value: \"<file>\",\n description: \"Serve every service in a mockingbird.json config instead\",\n },\n}\n\nconst formatLog = (format: LogFormat) => {\n if (format === \"off\") return undefined\n if (format === \"json\") return (entry: RequestLog) => console.log(JSON.stringify(entry))\n return (entry: RequestLog) => {\n const op = entry.operationId ?? (entry.unmatched ? \"UNMATCHED\" : \"-\")\n const ns = entry.namespace === \"default\" ? \"\" : ` [${entry.namespace}]`\n const fault = entry.faultId ? ` fault=${entry.faultId}` : \"\"\n const adopted = entry.adopted ? \" adopted\" : \"\"\n console.log(\n `${entry.service} ${entry.method} ${entry.path} ${entry.status} ${op} ${entry.durationMs}ms${ns}${fault}${adopted}`,\n )\n }\n}\n\nconst asString = (value: string | boolean | undefined): string | undefined =>\n typeof value === \"string\" ? value : undefined\n\n/** A service entry in `mockingbird.json`. */\nexport type ConfigService = {\n port?: number\n host?: string\n adminKey?: string\n seed?: string\n /** Service-specific serve flags, by long name: `{ \"webhook-url\": \"\u2026\" }`. */\n options?: Record<string, string | boolean>\n}\n\nexport type MockingbirdConfig = {\n /** Keyed by service name: `junction` loads `@crvouga/mockingbird-service-junction`. */\n services: Record<string, ConfigService>\n log?: LogFormat\n}\n\nconst loadTarget = async (name: string, own: ServeTarget): Promise<ServeTarget> => {\n if (name === own.name) return own\n const specifier = `@crvouga/mockingbird-service-${name}/server`\n try {\n const mod = (await import(specifier)) as { serveTarget?: ServeTarget }\n if (!mod.serveTarget) throw new Error(`${specifier} exports no serveTarget`)\n return mod.serveTarget\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n throw new Error(\n `cannot load service \"${name}\": ${reason}. Install @crvouga/mockingbird-service-${name}.`,\n )\n }\n}\n\nconst start = async (\n target: ServeTarget,\n values: CliValues,\n config: { port: number; host: string; adminKey?: string; seed?: string; log: LogFormat },\n): Promise<Listening> => {\n const runtime = await target.create(values, {\n adminKey: config.adminKey,\n seed: config.seed,\n onLog: formatLog(config.log),\n })\n const listening = await listen(runtime, { port: config.port, host: config.host })\n console.log(`${target.name} mock listening on ${listening.url}`)\n console.log(`${target.name} health: GET ${listening.url}/health`)\n console.log(\n `${target.name} admin: ${listening.url}/__admin (${config.adminKey ? \"x-mockingbird-admin-key required\" : \"open \u2014 pass --admin-key to lock\"})`,\n )\n for (const line of target.banner?.(runtime) ?? []) console.log(`${target.name} ${line}`)\n return listening\n}\n\nconst untilSignal = async (servers: Listening[]): Promise<number> =>\n new Promise((resolve) => {\n const stop = async () => {\n await Promise.allSettled(servers.map((s) => s.close()))\n resolve(0)\n }\n process.once(\"SIGINT\", stop)\n process.once(\"SIGTERM\", stop)\n })\n\n/** The standard `serve` command for a service, including multi-service `--config`. */\nexport const serveCommand = (target: ServeTarget): CliCommand => ({\n summary: `Serve the ${target.name} mock over HTTP`,\n options: { ...COMMON_SERVE_OPTIONS, ...target.options },\n async run(values) {\n const log = (\n values[\"log-requests\"] === true ? \"json\" : (asString(values.log) ?? \"pretty\")\n ) as LogFormat\n if (![\"pretty\", \"json\", \"off\"].includes(log)) {\n console.error(`--log must be pretty, json or off (got ${log})`)\n return 2\n }\n const configPath = asString(values.config)\n if (configPath !== undefined) {\n const config = JSON.parse(await readFile(configPath, \"utf8\")) as MockingbirdConfig\n const servers: Listening[] = []\n try {\n for (const [name, entry] of Object.entries(config.services ?? {})) {\n const each = await loadTarget(name, target)\n servers.push(\n await start(each, entry.options ?? {}, {\n port: entry.port ?? each.defaultPort,\n host: entry.host ?? \"127.0.0.1\",\n ...(entry.adminKey !== undefined ? { adminKey: entry.adminKey } : {}),\n ...(entry.seed !== undefined ? { seed: entry.seed } : {}),\n log: config.log ?? log,\n }),\n )\n }\n } catch (error) {\n await Promise.allSettled(servers.map((s) => s.close()))\n console.error(error instanceof Error ? error.message : String(error))\n return 1\n }\n return untilSignal(servers)\n }\n const port = asString(values.port)\n const adminKey = asString(values[\"admin-key\"]) ?? process.env.MOCKINGBIRD_ADMIN_KEY\n const seed = asString(values.seed)\n let listening: Listening\n try {\n listening = await start(target, values, {\n port: port === undefined ? target.defaultPort : Number.parseInt(port, 10),\n host: asString(values.host) ?? \"127.0.0.1\",\n ...(adminKey !== undefined ? { adminKey } : {}),\n ...(seed !== undefined ? { seed } : {}),\n log,\n })\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error))\n return 1\n }\n return untilSignal([listening])\n },\n})\n", "import { createServer } from \"node:http\"\nimport type { FetchAPI } from \"@crvouga/mockingbird-core\"\n\n/** Options for {@link serve}. */\nexport type NodeServeOptions = {\n port?: number\n host?: string\n}\n\n/**\n * Serve any Mockingbird {@link FetchAPI} over `node:http`.\n * Port defaults to `0`, so the OS assigns an ephemeral port (read from `server.address()`).\n */\nexport const serve = async (api: FetchAPI, options: NodeServeOptions = {}) => {\n const server = createServer(async (req, res) => {\n const chunks: Buffer[] = []\n for await (const chunk of req) {\n chunks.push(Buffer.from(chunk))\n }\n const body = Buffer.concat(chunks)\n const address = server.address()\n const port = typeof address === \"object\" && address !== null ? address.port : undefined\n const base = `http://${req.headers.host ?? `localhost:${port ?? 80}`}`\n const raw = req.url ?? \"/\"\n const url = new URL(raw.replace(/^\\/+/, \"/\"), base)\n const method = req.method ?? \"GET\"\n const init: RequestInit = { method, headers: req.headers as Record<string, string> }\n if (method !== \"GET\" && method !== \"HEAD\" && body.length > 0) {\n init.body = body\n }\n const aborted = new AbortController()\n res.once(\"close\", () => {\n if (!res.writableFinished) aborted.abort()\n })\n init.signal = aborted.signal\n const request = new Request(url, init)\n let response: Response\n try {\n response = await api.fetch(request)\n } catch (error) {\n // A `drop` fault: destroy the socket so the client sees the connection die.\n if ((error as { code?: string }).code === \"MOCKINGBIRD_DROP\") {\n req.socket.destroy()\n return\n }\n res.writeHead(500, { \"content-type\": \"application/json\" })\n res.end(\n JSON.stringify({\n error: {\n type: \"mockingbird_internal\",\n message: error instanceof Error ? error.message : String(error),\n },\n }),\n )\n return\n }\n // Headers#entries() joins repeated headers; Set-Cookie must stay one header per cookie.\n const headers: Record<string, string | string[]> = Object.fromEntries(response.headers)\n const cookies = response.headers.getSetCookie()\n if (cookies.length > 0) headers[\"set-cookie\"] = cookies\n if (!response.body) {\n res.writeHead(response.status, headers)\n res.end()\n return\n }\n // Stream the body chunk by chunk: event streams and long-polls must not be buffered.\n res.writeHead(response.status, headers)\n res.flushHeaders()\n const reader = response.body.getReader()\n try {\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n if (!res.write(value)) await new Promise<void>((resolve) => res.once(\"drain\", resolve))\n }\n res.end()\n } catch {\n res.destroy()\n } finally {\n reader.releaseLock()\n }\n })\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject)\n server.listen(options.port ?? 0, options.host, resolve)\n })\n return server\n}\n", "import type { Server } from \"node:http\"\nimport type { FetchAPI } from \"@crvouga/mockingbird-core\"\nimport { serve } from \"./serve.js\"\n\nexport type ListenOptions = {\n /** Default `0`: the OS picks a free port. */\n port?: number\n /** Default `127.0.0.1`: a mock should not be reachable off the machine by accident. */\n host?: string\n}\n\n/** A running server, with the address it actually bound. */\nexport type Listening = {\n url: string\n port: number\n host: string\n server: Server\n close(): Promise<void>\n}\n\nexport const listen = async (api: FetchAPI, options: ListenOptions = {}): Promise<Listening> => {\n const host = options.host ?? \"127.0.0.1\"\n const server = await serve(api, { port: options.port ?? 0, host })\n const address = server.address()\n const port = typeof address === \"object\" && address !== null ? address.port : (options.port ?? 0)\n const shown = host.includes(\":\") ? `[${host}]` : host\n return {\n url: `http://${shown}:${port}`,\n port,\n host,\n server,\n close: () =>\n new Promise<void>((resolve, reject) => {\n // Stop accepting first, then drop keep-alive sockets so close() can finish.\n server.close((error) => {\n if (error && (error as { code?: string }).code !== \"ERR_SERVER_NOT_RUNNING\") reject(error)\n else resolve()\n })\n server.closeAllConnections?.()\n }),\n }\n}\n", "/// <reference types=\"node\" />\nimport { type Listening, listen, type ServeTarget } from \"@crvouga/mockingbird-adapter-node\"\nimport { createRuntime, type FormbricksRuntime, type FormbricksRuntimeOptions } from \"./runtime.js\"\n\n/** Port `mockingbird-formbricks serve` listens on when none is given. */\nexport const DEFAULT_PORT = 8813\n\nexport type FormbricksServerOptions = FormbricksRuntimeOptions & {\n /** Default `0`: the OS picks a free port. */\n port?: number\n /** Default `127.0.0.1`. */\n host?: string\n}\n\nexport type FormbricksServer = Listening & { runtime: FormbricksRuntime }\n\n/** Serve the Formbricks mock over `node:http`. */\nexport const createServer = async (\n options: FormbricksServerOptions = {},\n): Promise<FormbricksServer> => {\n const { port, host, ...rest } = options\n const runtime = createRuntime(rest)\n const listening = await listen(runtime, {\n port: port ?? 0,\n ...(host !== undefined ? { host } : {}),\n })\n return { ...listening, runtime }\n}\n\nconst text = (value: string | boolean | undefined) =>\n typeof value === \"string\" ? value : undefined\n\n/** How `serve` (and `serve --config`) builds the Formbricks mock from flags. */\nexport const serveTarget: ServeTarget = {\n name: \"formbricks\",\n defaultPort: DEFAULT_PORT,\n options: {\n \"webhook-url\": {\n type: \"string\",\n value: \"<url>\",\n description:\n \"Deliver webhooks here (e.g. http://127.0.0.1:3000/onboarding-tasks/formbricks-webhook)\",\n },\n \"webhook-token\": {\n type: \"string\",\n value: \"<token>\",\n description: \"Appended as ?token= (the app's FORMBRICKS_WEBHOOK_SECRET)\",\n },\n \"webhook-secret\": {\n type: \"string\",\n value: \"<whsec_\u2026>\",\n description: \"Standard Webhooks key that signs webhook-signature (optional)\",\n },\n \"environment-id\": {\n type: \"string\",\n value: \"<id>\",\n description: \"An extra environment id that serves the survey fixture\",\n },\n },\n create: (values, common) => {\n const url = text(values[\"webhook-url\"])\n const token = text(values[\"webhook-token\"])\n const secret = text(values[\"webhook-secret\"])\n const environment = text(values[\"environment-id\"])\n const target =\n url && token\n ? `${url}${url.includes(\"?\") ? \"&\" : \"?\"}token=${encodeURIComponent(token)}`\n : url\n return createRuntime({\n ...(target ? { webhooks: { url: target, ...(secret ? { secret } : {}) } } : {}),\n ...(environment\n ? {\n settings: {\n environments: [environment, \"cmlhiza9j0009lj01my5c1g0q\", \"cmlhiza9c0004lj01jbuhp0nz\"],\n },\n }\n : {}),\n ...(common.adminKey !== undefined ? { adminKey: common.adminKey } : {}),\n ...(common.seed !== undefined ? { seed: common.seed } : {}),\n ...(common.onLog ? { onLog: common.onLog } : {}),\n })\n },\n banner: () => [\n \"point FORMBRICKS_APP_URL at this url; FORMBRICKS_ENVIRONMENT_ID cmlhiza9j0009lj01my5c1g0q (prod clone)\",\n \"management API: x-api-key <any>; namespaces: x-mockingbird-namespace, /ns/<name>/\u2026, or PUT /__admin/credentials {<env id | api key>: <ns>}\",\n ],\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["createServer"]
7
+ }