@crvouga/mockingbird-service-formbricks 3.6.0 → 3.8.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 +13 -1
- package/README.md +20 -1
- package/dist/{chunk-VZ55MOOA.js → chunk-5ZAU7WOP.js} +242 -16
- package/dist/chunk-5ZAU7WOP.js.map +7 -0
- package/dist/{chunk-THESW6F4.js → chunk-XJB56QGS.js} +107 -23
- package/dist/chunk-XJB56QGS.js.map +7 -0
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +28 -0
- package/dist/index.js +1 -1
- package/dist/server.d.ts +78 -1
- package/dist/server.js +4 -2
- package/package.json +1 -1
- package/dist/chunk-THESW6F4.js.map +0 -7
- package/dist/chunk-VZ55MOOA.js.map +0 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
# Changelog — @crvouga/mockingbird-service-formbricks
|
|
2
2
|
|
|
3
|
-
## 3.
|
|
3
|
+
## 3.8.0 (2026-10-21)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- raise the serve and fleet body limit to the S3 maximum ([86b9e2b](https://github.com/crvouga/mockingbird/commit/86b9e2b80bcaaaef57f52edbac50b27da0b42f33))
|
|
8
|
+
|
|
9
|
+
## 3.7.0 (2026-10-18)
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
- resolve the agent-reported issue queue in one batch ([25c52b6](https://github.com/crvouga/mockingbird/commit/25c52b668a53ddb80b968419601a20abf33f956b))
|
|
14
|
+
|
|
15
|
+
## 3.6.0 (2026-10-16)
|
|
4
16
|
|
|
5
17
|
### Features
|
|
6
18
|
|
package/README.md
CHANGED
|
@@ -36,6 +36,25 @@ npx mockingbird-formbricks serve --port 8813 \
|
|
|
36
36
|
--webhook-secret "whsec_…"
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
Boot it with your own settings and surveys, without a wrapper process or a `PUT` after boot:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx mockingbird-formbricks serve \
|
|
43
|
+
--settings '{"workspaces": ["cworkspace000000000000002"], "legacyEnvironmentIds": {"cenvironment0000000000002": "cworkspace000000000000002"}}' \
|
|
44
|
+
--surveys ./surveys.json
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`--settings <json|file>` (or `MOCKINGBIRD_SETTINGS`, or a fleet entry's `settings`) takes the
|
|
48
|
+
fields of `GET|PUT /__admin/settings`, checked with their types (a misspelled key is an error). They
|
|
49
|
+
are every namespace's defaults, a reset returns to them, and a field you give replaces the
|
|
50
|
+
built-in value whole (give `legacyEnvironmentIds` and the default environment alias is gone).
|
|
51
|
+
`--workspace-id` adds to the booted `workspaces`. `--surveys <file>` (a list, or `{surveys: [...]}`)
|
|
52
|
+
adds your surveys after the corpus ones for every namespace, and they survive a reset. A survey
|
|
53
|
+
whose id the corpus (or an earlier row) already has is skipped, a survey with no `workspaceId` is served by
|
|
54
|
+
every configured workspace, and `type`, `status` and a legacy `environmentId` are handled as
|
|
55
|
+
`PUT /__admin/surveys` handles them. `--surveys-mode replace` uses the file as the whole corpus.
|
|
56
|
+
In a fleet, set `options: {"surveys": "surveys.json"}`.
|
|
57
|
+
|
|
39
58
|
```ts
|
|
40
59
|
import { WORKSPACE_ID } from "@crvouga/mockingbird-service-formbricks"
|
|
41
60
|
import { createServer } from "@crvouga/mockingbird-service-formbricks/server"
|
|
@@ -135,7 +154,7 @@ string), `management_unauthorized`, `connection_drop`, `duplicate` (the next web
|
|
|
135
154
|
| Export | Kind | Description |
|
|
136
155
|
| --- | --- | --- |
|
|
137
156
|
| `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`. |
|
|
138
|
-
| `createRuntime` | function | The emulator with the full service contract (health, admin, namespaces, credentials, presets, webhooks). Options: `webhooks: {url, secret?, events?, retryDelaysMs?, fetch?}`, `surveys
|
|
157
|
+
| `createRuntime` | function | The emulator with the full service contract (health, admin, namespaces, credentials, presets, webhooks). Options: `webhooks: {url, secret?, events?, retryDelaysMs?, fetch?}`, `surveys` (replaces the corpus; `serve --surveys` appends), `settings`, `clock`, `seed`, `adminKey`, `onLog`, `sqlite`. |
|
|
139
158
|
| `FORMBRICKS_PRESETS` | object | Every named fault preset. |
|
|
140
159
|
| `FORMBRICKS_NAMESPACE` | string | The service name, `"formbricks"`. |
|
|
141
160
|
| `WEBHOOK_PATH` | string | A conventional receiver path for examples, `/webhooks/formbricks`. |
|
|
@@ -1,20 +1,38 @@
|
|
|
1
1
|
import {
|
|
2
|
+
CORPUS_SURVEYS,
|
|
2
3
|
DEFAULT_SETTINGS,
|
|
3
4
|
createClock,
|
|
4
5
|
createRuntime,
|
|
5
6
|
normalizeBootControls,
|
|
6
7
|
normalizeControls,
|
|
8
|
+
normalizeIdBase,
|
|
7
9
|
ownedTransport,
|
|
8
10
|
parseDuration,
|
|
9
11
|
parseFaultHeader,
|
|
10
12
|
resolveAdminPrefix,
|
|
11
13
|
restoreClock
|
|
12
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-XJB56QGS.js";
|
|
15
|
+
|
|
16
|
+
// src/server.ts
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
13
18
|
|
|
14
19
|
// ../../adapters/node/dist/cli.js
|
|
15
20
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
16
21
|
import { parseArgs } from "node:util";
|
|
17
22
|
|
|
23
|
+
// ../../adapters/node/dist/boot-settings.js
|
|
24
|
+
var checkSettings = (target, settings, label) => {
|
|
25
|
+
if (!target.parseSettings)
|
|
26
|
+
throw new Error(`${label}: ${target.name} takes no boot settings`);
|
|
27
|
+
try {
|
|
28
|
+
return target.parseSettings(settings);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
throw new Error(`${label}: ${error instanceof Error ? error.message : String(error)}`, {
|
|
31
|
+
cause: error
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
18
36
|
// ../../adapters/node/dist/fleet.js
|
|
19
37
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
20
38
|
import { createHash as createHash4, randomUUID as randomUUID4 } from "node:crypto";
|
|
@@ -2664,6 +2682,9 @@ var fleet_config_schema_default = {
|
|
|
2664
2682
|
adminControls: {
|
|
2665
2683
|
$ref: "#/$defs/controls"
|
|
2666
2684
|
},
|
|
2685
|
+
idBase: {
|
|
2686
|
+
$ref: "#/$defs/idBase"
|
|
2687
|
+
},
|
|
2667
2688
|
namespace: {
|
|
2668
2689
|
type: "string",
|
|
2669
2690
|
pattern: "^[A-Za-z0-9_.-]{1,64}$"
|
|
@@ -2753,6 +2774,12 @@ var fleet_config_schema_default = {
|
|
|
2753
2774
|
adminControls: {
|
|
2754
2775
|
$ref: "#/$defs/controls"
|
|
2755
2776
|
},
|
|
2777
|
+
idBase: {
|
|
2778
|
+
$ref: "#/$defs/idBase"
|
|
2779
|
+
},
|
|
2780
|
+
settings: {
|
|
2781
|
+
type: "object"
|
|
2782
|
+
},
|
|
2756
2783
|
fixtures: {
|
|
2757
2784
|
type: "object"
|
|
2758
2785
|
},
|
|
@@ -2799,6 +2826,18 @@ var fleet_config_schema_default = {
|
|
|
2799
2826
|
}
|
|
2800
2827
|
}
|
|
2801
2828
|
},
|
|
2829
|
+
idBase: {
|
|
2830
|
+
anyOf: [
|
|
2831
|
+
{
|
|
2832
|
+
type: "integer",
|
|
2833
|
+
minimum: 0
|
|
2834
|
+
},
|
|
2835
|
+
{
|
|
2836
|
+
type: "string",
|
|
2837
|
+
pattern: "^(boot|[0-9]+)$"
|
|
2838
|
+
}
|
|
2839
|
+
]
|
|
2840
|
+
},
|
|
2802
2841
|
controls: {
|
|
2803
2842
|
type: "object",
|
|
2804
2843
|
additionalProperties: false,
|
|
@@ -2948,6 +2987,8 @@ async function preflightFleet(config, options) {
|
|
|
2948
2987
|
throw new Error("protected archive policy requires a protected repository; configure its protector before startup");
|
|
2949
2988
|
resolveAdminPrefix(config.adminPrefix);
|
|
2950
2989
|
normalizeBootControls(config.adminControls);
|
|
2990
|
+
if (config.idBase !== void 0)
|
|
2991
|
+
normalizeIdBase(config.idBase, "idBase");
|
|
2951
2992
|
const targets = /* @__PURE__ */ new Map();
|
|
2952
2993
|
const ports = /* @__PURE__ */ new Set();
|
|
2953
2994
|
for (const [name, entry] of Object.entries(config.services)) {
|
|
@@ -2979,6 +3020,13 @@ async function preflightFleet(config, options) {
|
|
|
2979
3020
|
throw new Error("invalid protocol namespace selector");
|
|
2980
3021
|
}
|
|
2981
3022
|
}
|
|
3023
|
+
if (entry.idBase !== void 0)
|
|
3024
|
+
normalizeIdBase(entry.idBase, "idBase");
|
|
3025
|
+
if (entry.settings !== void 0) {
|
|
3026
|
+
if (protocol !== "http" || !("create" in target))
|
|
3027
|
+
throw new Error("settings: only HTTP services take boot settings");
|
|
3028
|
+
checkSettings(target, entry.settings, "settings");
|
|
3029
|
+
}
|
|
2982
3030
|
const port = entry.port ?? target.defaultPort;
|
|
2983
3031
|
if (port !== 0) {
|
|
2984
3032
|
const key = `${entry.host ?? "127.0.0.1"}:${port}`;
|
|
@@ -3014,7 +3062,7 @@ async function preflightFleet(config, options) {
|
|
|
3014
3062
|
break;
|
|
3015
3063
|
}
|
|
3016
3064
|
}
|
|
3017
|
-
targets.set(name, { ...target, packageVersion: packageVersion ?? "3.
|
|
3065
|
+
targets.set(name, { ...target, packageVersion: packageVersion ?? "3.8.0" });
|
|
3018
3066
|
} catch (error) {
|
|
3019
3067
|
throw new Error(`service ${name}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
3020
3068
|
}
|
|
@@ -3240,7 +3288,11 @@ var serve = async (api, options = {}) => {
|
|
|
3240
3288
|
// ../../adapters/node/dist/listen.js
|
|
3241
3289
|
var listen = async (api, options = {}) => {
|
|
3242
3290
|
const host = options.host ?? "127.0.0.1";
|
|
3243
|
-
const server = await serve(api, {
|
|
3291
|
+
const server = await serve(api, {
|
|
3292
|
+
port: options.port ?? 0,
|
|
3293
|
+
host,
|
|
3294
|
+
...options.maxBodyBytes !== void 0 ? { maxBodyBytes: options.maxBodyBytes } : {}
|
|
3295
|
+
});
|
|
3244
3296
|
const address = server.address();
|
|
3245
3297
|
const port = typeof address === "object" && address !== null ? address.port : options.port ?? 0;
|
|
3246
3298
|
const shown = host.includes(":") ? `[${host}]` : host;
|
|
@@ -3334,6 +3386,7 @@ var httpChild = async (target, entry, options, admission, generation, privateAcc
|
|
|
3334
3386
|
options.onLog?.(entry2);
|
|
3335
3387
|
},
|
|
3336
3388
|
...entry.adminControls !== void 0 ? { adminControls: entry.adminControls } : {},
|
|
3389
|
+
...entry.settings !== void 0 ? { settings: checkSettings(target, entry.settings, "settings") } : {},
|
|
3337
3390
|
...entry.fixtures !== void 0 ? { fixtures: entry.fixtures } : {},
|
|
3338
3391
|
...(advertisedHttpBase(entry) ?? entry.baseUrl) !== void 0 ? { baseUrl: advertisedHttpBase(entry) ?? entry.baseUrl } : {}
|
|
3339
3392
|
});
|
|
@@ -3352,6 +3405,8 @@ var httpChild = async (target, entry, options, admission, generation, privateAcc
|
|
|
3352
3405
|
].some((method) => typeof created[method] !== "function"))
|
|
3353
3406
|
throw new Error("HTTP target does not implement the fleet runtime protocol; build/install matching supported service versions");
|
|
3354
3407
|
const runtime = created;
|
|
3408
|
+
if (entry.idBase !== void 0)
|
|
3409
|
+
runtime.setIdBase?.(entry.idBase);
|
|
3355
3410
|
const unobserve = runtime.webhooks?.observeDeliveries(observer.delivery);
|
|
3356
3411
|
if (options.outbound && runtime.webhooks)
|
|
3357
3412
|
runtime.webhooks.setTransportPolicy(ownedTransport(options.outbound));
|
|
@@ -3436,7 +3491,8 @@ var httpChild = async (target, entry, options, admission, generation, privateAcc
|
|
|
3436
3491
|
try {
|
|
3437
3492
|
server = await listen(api, {
|
|
3438
3493
|
port: entry.port ?? target.defaultPort,
|
|
3439
|
-
host: entry.host ?? "127.0.0.1"
|
|
3494
|
+
host: entry.host ?? "127.0.0.1",
|
|
3495
|
+
...target.maxBodyBytes !== void 0 ? { maxBodyBytes: target.maxBodyBytes } : {}
|
|
3440
3496
|
});
|
|
3441
3497
|
target.listening?.(server);
|
|
3442
3498
|
} catch (error) {
|
|
@@ -3950,6 +4006,7 @@ async function startFleet(config, options) {
|
|
|
3950
4006
|
...entry,
|
|
3951
4007
|
adminPrefix: resolveAdminPrefix(entry.adminPrefix ?? adminPrefix),
|
|
3952
4008
|
...adminKey !== void 0 ? { adminKey } : {},
|
|
4009
|
+
...(entry.idBase ?? config.idBase) !== void 0 ? { idBase: entry.idBase ?? config.idBase } : {},
|
|
3953
4010
|
// Fleet-wide controls, with the service's own keys replacing them.
|
|
3954
4011
|
...config.adminControls !== void 0 || entry.adminControls !== void 0 ? { adminControls: { ...config.adminControls, ...entry.adminControls } } : {}
|
|
3955
4012
|
}, options, (namespace, request) => management?.admit(namespace, request), (namespace) => management?.generation(namespace), (namespace, request) => management?.privateRequest(namespace, request) ?? false, {
|
|
@@ -4634,6 +4691,16 @@ var COMMON_SERVE_OPTIONS = {
|
|
|
4634
4691
|
value: "<seed>",
|
|
4635
4692
|
description: "Seed for every random choice (env MOCKINGBIRD_SEED)"
|
|
4636
4693
|
},
|
|
4694
|
+
"id-base": {
|
|
4695
|
+
type: "string",
|
|
4696
|
+
value: "<n|boot>",
|
|
4697
|
+
description: "Start generated ids at this base so a restart does not reissue the last boot's ids; boot derives one from the clock (env MOCKINGBIRD_ID_BASE; default 0, the same ids every boot)"
|
|
4698
|
+
},
|
|
4699
|
+
settings: {
|
|
4700
|
+
type: "string",
|
|
4701
|
+
value: "<json|file>",
|
|
4702
|
+
description: "Service settings to boot with, as JSON or a JSON file; every namespace starts from them (env MOCKINGBIRD_SETTINGS; only for services that declare settings)"
|
|
4703
|
+
},
|
|
4637
4704
|
"admin-controls": {
|
|
4638
4705
|
type: "string",
|
|
4639
4706
|
value: "<json|file>",
|
|
@@ -4728,19 +4795,40 @@ var readJsonFile = async (path, label) => {
|
|
|
4728
4795
|
}
|
|
4729
4796
|
};
|
|
4730
4797
|
var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4798
|
+
var readJsonOption = async (source, label) => {
|
|
4799
|
+
const trimmed = source.trim();
|
|
4800
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
4801
|
+
try {
|
|
4802
|
+
return JSON.parse(trimmed);
|
|
4803
|
+
} catch {
|
|
4804
|
+
throw new Error(`${label}: not valid JSON`);
|
|
4805
|
+
}
|
|
4806
|
+
}
|
|
4807
|
+
return readJsonFile(source, label);
|
|
4808
|
+
};
|
|
4809
|
+
var resolveServeIdBase = (values, env = process.env) => {
|
|
4810
|
+
const source = asString(values["id-base"]) ?? env.MOCKINGBIRD_ID_BASE;
|
|
4811
|
+
if (source === void 0 || source.trim() === "")
|
|
4812
|
+
return void 0;
|
|
4813
|
+
normalizeIdBase(source.trim(), "--id-base");
|
|
4814
|
+
return source.trim();
|
|
4815
|
+
};
|
|
4816
|
+
var resolveServeSettings = async (target, values, env = process.env) => {
|
|
4817
|
+
const flag = asString(values.settings);
|
|
4818
|
+
const source = flag ?? env.MOCKINGBIRD_SETTINGS;
|
|
4819
|
+
if (source === void 0 || source.trim() === "")
|
|
4820
|
+
return void 0;
|
|
4821
|
+
const label = flag !== void 0 ? "--settings" : "MOCKINGBIRD_SETTINGS";
|
|
4822
|
+
const parsed = await readJsonOption(source, label);
|
|
4823
|
+
if (!isObject2(parsed))
|
|
4824
|
+
throw new Error(`${label}: expected a JSON object`);
|
|
4825
|
+
return checkSettings(target, parsed, label);
|
|
4826
|
+
};
|
|
4731
4827
|
var resolveServeControls = async (values, env = process.env, base) => {
|
|
4732
4828
|
const source = asString(values["admin-controls"]) ?? env.MOCKINGBIRD_ADMIN_CONTROLS;
|
|
4733
4829
|
let document = isObject2(base) ? { ...base } : {};
|
|
4734
4830
|
if (source !== void 0 && source.trim() !== "") {
|
|
4735
|
-
|
|
4736
|
-
if (source.trim().startsWith("{")) {
|
|
4737
|
-
try {
|
|
4738
|
-
parsed = JSON.parse(source);
|
|
4739
|
-
} catch {
|
|
4740
|
-
throw new Error("--admin-controls: not valid JSON");
|
|
4741
|
-
}
|
|
4742
|
-
} else
|
|
4743
|
-
parsed = await readJsonFile(source, "--admin-controls");
|
|
4831
|
+
const parsed = source.trim().startsWith("{") ? await readJsonOption(source, "--admin-controls") : await readJsonFile(source, "--admin-controls");
|
|
4744
4832
|
if (!isObject2(parsed))
|
|
4745
4833
|
throw new Error("--admin-controls: expected a JSON object");
|
|
4746
4834
|
document = { ...document, ...parsed };
|
|
@@ -4790,9 +4878,16 @@ var start = async (target, values, config) => {
|
|
|
4790
4878
|
adminKey: config.adminKey,
|
|
4791
4879
|
seed: config.seed,
|
|
4792
4880
|
onLog: formatLog(config.log),
|
|
4881
|
+
...config.settings !== void 0 ? { settings: config.settings } : {},
|
|
4793
4882
|
...config.adminControls !== void 0 ? { adminControls: config.adminControls } : {}
|
|
4794
4883
|
});
|
|
4795
|
-
|
|
4884
|
+
if (config.idBase !== void 0)
|
|
4885
|
+
runtime.setIdBase?.(config.idBase);
|
|
4886
|
+
const listening = await listen(runtime, {
|
|
4887
|
+
port: config.port,
|
|
4888
|
+
host: config.host,
|
|
4889
|
+
...target.maxBodyBytes !== void 0 ? { maxBodyBytes: target.maxBodyBytes } : {}
|
|
4890
|
+
});
|
|
4796
4891
|
target.listening?.(listening);
|
|
4797
4892
|
console.log(`${target.name} emulator listening on ${listening.url}`);
|
|
4798
4893
|
console.log(`${target.name} health: GET ${listening.url}${adminPrefix}/health`);
|
|
@@ -4837,6 +4932,11 @@ var serveCommand = (target) => ({
|
|
|
4837
4932
|
const adminControls = await resolveServeControls(values, process.env, config.adminControls);
|
|
4838
4933
|
if (adminControls !== void 0)
|
|
4839
4934
|
config.adminControls = adminControls;
|
|
4935
|
+
if (values.settings !== void 0)
|
|
4936
|
+
throw new Error("--settings serves a single service; give each service its own `settings` in the config");
|
|
4937
|
+
const idBase = config.idBase ?? resolveServeIdBase(values);
|
|
4938
|
+
if (idBase !== void 0)
|
|
4939
|
+
config.idBase = idBase;
|
|
4840
4940
|
const fleet = await startFleet(config, {
|
|
4841
4941
|
load: (name) => loadTarget(name, target),
|
|
4842
4942
|
onLog: formatLog(config.log ?? log),
|
|
@@ -4866,8 +4966,12 @@ var serveCommand = (target) => ({
|
|
|
4866
4966
|
let listening;
|
|
4867
4967
|
try {
|
|
4868
4968
|
const adminControls = await resolveServeControls(values);
|
|
4969
|
+
const idBase = resolveServeIdBase(values);
|
|
4970
|
+
const settings = await resolveServeSettings(target, values);
|
|
4869
4971
|
listening = await start(target, values, {
|
|
4870
4972
|
...adminControls !== void 0 ? { adminControls } : {},
|
|
4973
|
+
...idBase !== void 0 ? { idBase } : {},
|
|
4974
|
+
...settings !== void 0 ? { settings } : {},
|
|
4871
4975
|
adminPrefix: resolveAdminPrefix(asString(values["admin-prefix"]) ?? process.env.MOCKINGBIRD_ADMIN_PREFIX),
|
|
4872
4976
|
port: port === void 0 ? target.defaultPort : Number.parseInt(port, 10),
|
|
4873
4977
|
host: asString(values.host) ?? "127.0.0.1",
|
|
@@ -4883,6 +4987,84 @@ var serveCommand = (target) => ({
|
|
|
4883
4987
|
}
|
|
4884
4988
|
});
|
|
4885
4989
|
|
|
4990
|
+
// src/boot.ts
|
|
4991
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4992
|
+
var strings = (value, where) => {
|
|
4993
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string"))
|
|
4994
|
+
throw new Error(`${where} must be an array of strings`);
|
|
4995
|
+
return value;
|
|
4996
|
+
};
|
|
4997
|
+
var KEYS = [
|
|
4998
|
+
"workspaces",
|
|
4999
|
+
"legacyEnvironmentIds",
|
|
5000
|
+
"apiKeys",
|
|
5001
|
+
"webhookId",
|
|
5002
|
+
"contactsEnabled",
|
|
5003
|
+
"validation"
|
|
5004
|
+
];
|
|
5005
|
+
var parseBootSettings = (input) => {
|
|
5006
|
+
for (const key of Object.keys(input))
|
|
5007
|
+
if (!KEYS.includes(key)) throw new Error(`unknown setting ${JSON.stringify(key)}`);
|
|
5008
|
+
const out = {};
|
|
5009
|
+
if (input.workspaces !== void 0) out.workspaces = strings(input.workspaces, "workspaces");
|
|
5010
|
+
if (input.legacyEnvironmentIds !== void 0) {
|
|
5011
|
+
const aliases = input.legacyEnvironmentIds;
|
|
5012
|
+
if (!isRecord(aliases) || Object.values(aliases).some((id) => typeof id !== "string"))
|
|
5013
|
+
throw new Error("legacyEnvironmentIds must be an object of workspace ids by environment id");
|
|
5014
|
+
out.legacyEnvironmentIds = aliases;
|
|
5015
|
+
}
|
|
5016
|
+
if (input.apiKeys !== void 0) out.apiKeys = strings(input.apiKeys, "apiKeys");
|
|
5017
|
+
if (input.webhookId !== void 0) {
|
|
5018
|
+
if (typeof input.webhookId !== "string") throw new Error("webhookId must be a string");
|
|
5019
|
+
out.webhookId = input.webhookId;
|
|
5020
|
+
}
|
|
5021
|
+
if (input.contactsEnabled !== void 0) {
|
|
5022
|
+
if (typeof input.contactsEnabled !== "boolean")
|
|
5023
|
+
throw new Error("contactsEnabled must be a boolean");
|
|
5024
|
+
out.contactsEnabled = input.contactsEnabled;
|
|
5025
|
+
}
|
|
5026
|
+
if (input.validation !== void 0) {
|
|
5027
|
+
if (input.validation !== "present-only" && input.validation !== "finished-validates-all")
|
|
5028
|
+
throw new Error('validation must be "present-only" or "finished-validates-all"');
|
|
5029
|
+
out.validation = input.validation;
|
|
5030
|
+
}
|
|
5031
|
+
return out;
|
|
5032
|
+
};
|
|
5033
|
+
var parseSurveysDocument = (input, legacyEnvironmentIds = {}) => {
|
|
5034
|
+
const list = Array.isArray(input) ? input : isRecord(input) ? input.surveys : void 0;
|
|
5035
|
+
if (!Array.isArray(list)) throw new Error("expected [survey] or {surveys: [survey]}");
|
|
5036
|
+
return list.map((entry, index) => {
|
|
5037
|
+
const where = `surveys[${index}]`;
|
|
5038
|
+
if (!isRecord(entry)) throw new Error(`${where} must be an object`);
|
|
5039
|
+
if (typeof entry.id !== "string" || entry.id === "")
|
|
5040
|
+
throw new Error(`${where}.id must be a non-empty string`);
|
|
5041
|
+
if (typeof entry.name !== "string") throw new Error(`${where}.name must be a string`);
|
|
5042
|
+
const { environmentId, ...rest } = entry;
|
|
5043
|
+
for (const key of ["workspaceId", "environmentId", "type", "status"])
|
|
5044
|
+
if (entry[key] !== void 0 && entry[key] !== null && typeof entry[key] !== "string")
|
|
5045
|
+
throw new Error(`${where}.${key} must be a string`);
|
|
5046
|
+
const owner = entry.workspaceId ?? environmentId;
|
|
5047
|
+
return {
|
|
5048
|
+
...rest,
|
|
5049
|
+
id: entry.id,
|
|
5050
|
+
name: entry.name,
|
|
5051
|
+
type: entry.type ?? "app",
|
|
5052
|
+
status: entry.status ?? "inProgress",
|
|
5053
|
+
workspaceId: owner ? legacyEnvironmentIds[owner] ?? owner : null
|
|
5054
|
+
};
|
|
5055
|
+
});
|
|
5056
|
+
};
|
|
5057
|
+
var appendSurveys = (base, extra) => {
|
|
5058
|
+
const seen = new Set(base.map((survey) => survey.id));
|
|
5059
|
+
const out = [...base];
|
|
5060
|
+
for (const survey of extra) {
|
|
5061
|
+
if (seen.has(survey.id)) continue;
|
|
5062
|
+
seen.add(survey.id);
|
|
5063
|
+
out.push(survey);
|
|
5064
|
+
}
|
|
5065
|
+
return out;
|
|
5066
|
+
};
|
|
5067
|
+
|
|
4886
5068
|
// src/server.ts
|
|
4887
5069
|
var DEFAULT_PORT = 8813;
|
|
4888
5070
|
var createServer2 = async (options = {}) => {
|
|
@@ -4895,9 +5077,24 @@ var createServer2 = async (options = {}) => {
|
|
|
4895
5077
|
return { ...listening, runtime };
|
|
4896
5078
|
};
|
|
4897
5079
|
var text = (value) => typeof value === "string" ? value : void 0;
|
|
5080
|
+
var loadSurveysFile = (path, mode = "append", legacyEnvironmentIds = DEFAULT_SETTINGS.legacyEnvironmentIds) => {
|
|
5081
|
+
let raw;
|
|
5082
|
+
try {
|
|
5083
|
+
raw = readFileSync(path, "utf8");
|
|
5084
|
+
} catch {
|
|
5085
|
+
throw new Error(`formbricks surveys not found: ${path}`);
|
|
5086
|
+
}
|
|
5087
|
+
try {
|
|
5088
|
+
const surveys = parseSurveysDocument(JSON.parse(raw), legacyEnvironmentIds);
|
|
5089
|
+
return appendSurveys(mode === "replace" ? [] : CORPUS_SURVEYS, surveys);
|
|
5090
|
+
} catch (error) {
|
|
5091
|
+
throw new Error(`formbricks surveys ${path}: ${error.message}`);
|
|
5092
|
+
}
|
|
5093
|
+
};
|
|
4898
5094
|
var serveTarget = {
|
|
4899
5095
|
name: "formbricks",
|
|
4900
5096
|
defaultPort: DEFAULT_PORT,
|
|
5097
|
+
parseSettings: (settings) => parseBootSettings(settings),
|
|
4901
5098
|
options: {
|
|
4902
5099
|
"webhook-url": {
|
|
4903
5100
|
type: "string",
|
|
@@ -4918,6 +5115,16 @@ var serveTarget = {
|
|
|
4918
5115
|
type: "string",
|
|
4919
5116
|
value: "<id>",
|
|
4920
5117
|
description: "An extra workspace id that serves the survey corpus"
|
|
5118
|
+
},
|
|
5119
|
+
surveys: {
|
|
5120
|
+
type: "string",
|
|
5121
|
+
value: "<file.json>",
|
|
5122
|
+
description: "Surveys every namespace starts with, as a list or {surveys: [...]}, added after the corpus surveys (a repeated id is skipped)"
|
|
5123
|
+
},
|
|
5124
|
+
"surveys-mode": {
|
|
5125
|
+
type: "string",
|
|
5126
|
+
value: "<append|replace>",
|
|
5127
|
+
description: "With --surveys: add to the corpus (default) or use the file as the whole corpus"
|
|
4921
5128
|
}
|
|
4922
5129
|
},
|
|
4923
5130
|
create: (values, common) => {
|
|
@@ -4925,10 +5132,28 @@ var serveTarget = {
|
|
|
4925
5132
|
const token = text(values["webhook-token"]);
|
|
4926
5133
|
const secret = text(values["webhook-secret"]);
|
|
4927
5134
|
const workspace = text(values["workspace-id"]);
|
|
5135
|
+
const surveysPath = text(values.surveys);
|
|
5136
|
+
const mode = text(values["surveys-mode"]);
|
|
5137
|
+
if (mode !== void 0 && mode !== "append" && mode !== "replace")
|
|
5138
|
+
throw new Error('--surveys-mode must be "append" or "replace"');
|
|
5139
|
+
if (mode !== void 0 && surveysPath === void 0)
|
|
5140
|
+
throw new Error("--surveys-mode needs --surveys");
|
|
5141
|
+
const boot = common.settings;
|
|
5142
|
+
const settings = {
|
|
5143
|
+
...boot,
|
|
5144
|
+
...workspace ? { workspaces: [workspace, ...boot?.workspaces ?? DEFAULT_SETTINGS.workspaces] } : {}
|
|
5145
|
+
};
|
|
4928
5146
|
const target = url && token ? `${url}${url.includes("?") ? "&" : "?"}token=${encodeURIComponent(token)}` : url;
|
|
4929
5147
|
return createRuntime({
|
|
4930
5148
|
...target ? { webhooks: { url: target, ...secret ? { secret } : {} } } : {},
|
|
4931
|
-
...
|
|
5149
|
+
...Object.keys(settings).length > 0 ? { settings } : {},
|
|
5150
|
+
...surveysPath ? {
|
|
5151
|
+
surveys: loadSurveysFile(
|
|
5152
|
+
surveysPath,
|
|
5153
|
+
mode,
|
|
5154
|
+
settings.legacyEnvironmentIds ?? DEFAULT_SETTINGS.legacyEnvironmentIds
|
|
5155
|
+
)
|
|
5156
|
+
} : {},
|
|
4932
5157
|
...common.adminPrefix !== void 0 ? { adminPrefix: common.adminPrefix } : {},
|
|
4933
5158
|
...common.adminKey !== void 0 ? { adminKey: common.adminKey } : {},
|
|
4934
5159
|
...common.adminControls !== void 0 ? { adminControls: common.adminControls } : {},
|
|
@@ -4947,6 +5172,7 @@ export {
|
|
|
4947
5172
|
serveCommand,
|
|
4948
5173
|
DEFAULT_PORT,
|
|
4949
5174
|
createServer2 as createServer,
|
|
5175
|
+
loadSurveysFile,
|
|
4950
5176
|
serveTarget
|
|
4951
5177
|
};
|
|
4952
|
-
//# sourceMappingURL=chunk-
|
|
5178
|
+
//# sourceMappingURL=chunk-5ZAU7WOP.js.map
|