@crvouga/mockingbird-service-formbricks 3.6.0 → 3.7.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 +7 -1
- package/README.md +20 -1
- package/dist/{chunk-VZ55MOOA.js → chunk-M3VADEIL.js} +230 -13
- package/dist/chunk-M3VADEIL.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 +76 -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,12 @@
|
|
|
1
1
|
# Changelog — @crvouga/mockingbird-service-formbricks
|
|
2
2
|
|
|
3
|
-
## 3.
|
|
3
|
+
## 3.7.0 (2026-10-19)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- resolve the agent-reported issue queue in one batch ([25c52b6](https://github.com/crvouga/mockingbird/commit/25c52b668a53ddb80b968419601a20abf33f956b))
|
|
8
|
+
|
|
9
|
+
## 3.6.0 (2026-10-16)
|
|
4
10
|
|
|
5
11
|
### Features
|
|
6
12
|
|
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.7.0" });
|
|
3018
3066
|
} catch (error) {
|
|
3019
3067
|
throw new Error(`service ${name}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
3020
3068
|
}
|
|
@@ -3334,6 +3382,7 @@ var httpChild = async (target, entry, options, admission, generation, privateAcc
|
|
|
3334
3382
|
options.onLog?.(entry2);
|
|
3335
3383
|
},
|
|
3336
3384
|
...entry.adminControls !== void 0 ? { adminControls: entry.adminControls } : {},
|
|
3385
|
+
...entry.settings !== void 0 ? { settings: checkSettings(target, entry.settings, "settings") } : {},
|
|
3337
3386
|
...entry.fixtures !== void 0 ? { fixtures: entry.fixtures } : {},
|
|
3338
3387
|
...(advertisedHttpBase(entry) ?? entry.baseUrl) !== void 0 ? { baseUrl: advertisedHttpBase(entry) ?? entry.baseUrl } : {}
|
|
3339
3388
|
});
|
|
@@ -3352,6 +3401,8 @@ var httpChild = async (target, entry, options, admission, generation, privateAcc
|
|
|
3352
3401
|
].some((method) => typeof created[method] !== "function"))
|
|
3353
3402
|
throw new Error("HTTP target does not implement the fleet runtime protocol; build/install matching supported service versions");
|
|
3354
3403
|
const runtime = created;
|
|
3404
|
+
if (entry.idBase !== void 0)
|
|
3405
|
+
runtime.setIdBase?.(entry.idBase);
|
|
3355
3406
|
const unobserve = runtime.webhooks?.observeDeliveries(observer.delivery);
|
|
3356
3407
|
if (options.outbound && runtime.webhooks)
|
|
3357
3408
|
runtime.webhooks.setTransportPolicy(ownedTransport(options.outbound));
|
|
@@ -3950,6 +4001,7 @@ async function startFleet(config, options) {
|
|
|
3950
4001
|
...entry,
|
|
3951
4002
|
adminPrefix: resolveAdminPrefix(entry.adminPrefix ?? adminPrefix),
|
|
3952
4003
|
...adminKey !== void 0 ? { adminKey } : {},
|
|
4004
|
+
...(entry.idBase ?? config.idBase) !== void 0 ? { idBase: entry.idBase ?? config.idBase } : {},
|
|
3953
4005
|
// Fleet-wide controls, with the service's own keys replacing them.
|
|
3954
4006
|
...config.adminControls !== void 0 || entry.adminControls !== void 0 ? { adminControls: { ...config.adminControls, ...entry.adminControls } } : {}
|
|
3955
4007
|
}, options, (namespace, request) => management?.admit(namespace, request), (namespace) => management?.generation(namespace), (namespace, request) => management?.privateRequest(namespace, request) ?? false, {
|
|
@@ -4634,6 +4686,16 @@ var COMMON_SERVE_OPTIONS = {
|
|
|
4634
4686
|
value: "<seed>",
|
|
4635
4687
|
description: "Seed for every random choice (env MOCKINGBIRD_SEED)"
|
|
4636
4688
|
},
|
|
4689
|
+
"id-base": {
|
|
4690
|
+
type: "string",
|
|
4691
|
+
value: "<n|boot>",
|
|
4692
|
+
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)"
|
|
4693
|
+
},
|
|
4694
|
+
settings: {
|
|
4695
|
+
type: "string",
|
|
4696
|
+
value: "<json|file>",
|
|
4697
|
+
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)"
|
|
4698
|
+
},
|
|
4637
4699
|
"admin-controls": {
|
|
4638
4700
|
type: "string",
|
|
4639
4701
|
value: "<json|file>",
|
|
@@ -4728,19 +4790,40 @@ var readJsonFile = async (path, label) => {
|
|
|
4728
4790
|
}
|
|
4729
4791
|
};
|
|
4730
4792
|
var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4793
|
+
var readJsonOption = async (source, label) => {
|
|
4794
|
+
const trimmed = source.trim();
|
|
4795
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
4796
|
+
try {
|
|
4797
|
+
return JSON.parse(trimmed);
|
|
4798
|
+
} catch {
|
|
4799
|
+
throw new Error(`${label}: not valid JSON`);
|
|
4800
|
+
}
|
|
4801
|
+
}
|
|
4802
|
+
return readJsonFile(source, label);
|
|
4803
|
+
};
|
|
4804
|
+
var resolveServeIdBase = (values, env = process.env) => {
|
|
4805
|
+
const source = asString(values["id-base"]) ?? env.MOCKINGBIRD_ID_BASE;
|
|
4806
|
+
if (source === void 0 || source.trim() === "")
|
|
4807
|
+
return void 0;
|
|
4808
|
+
normalizeIdBase(source.trim(), "--id-base");
|
|
4809
|
+
return source.trim();
|
|
4810
|
+
};
|
|
4811
|
+
var resolveServeSettings = async (target, values, env = process.env) => {
|
|
4812
|
+
const flag = asString(values.settings);
|
|
4813
|
+
const source = flag ?? env.MOCKINGBIRD_SETTINGS;
|
|
4814
|
+
if (source === void 0 || source.trim() === "")
|
|
4815
|
+
return void 0;
|
|
4816
|
+
const label = flag !== void 0 ? "--settings" : "MOCKINGBIRD_SETTINGS";
|
|
4817
|
+
const parsed = await readJsonOption(source, label);
|
|
4818
|
+
if (!isObject2(parsed))
|
|
4819
|
+
throw new Error(`${label}: expected a JSON object`);
|
|
4820
|
+
return checkSettings(target, parsed, label);
|
|
4821
|
+
};
|
|
4731
4822
|
var resolveServeControls = async (values, env = process.env, base) => {
|
|
4732
4823
|
const source = asString(values["admin-controls"]) ?? env.MOCKINGBIRD_ADMIN_CONTROLS;
|
|
4733
4824
|
let document = isObject2(base) ? { ...base } : {};
|
|
4734
4825
|
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");
|
|
4826
|
+
const parsed = source.trim().startsWith("{") ? await readJsonOption(source, "--admin-controls") : await readJsonFile(source, "--admin-controls");
|
|
4744
4827
|
if (!isObject2(parsed))
|
|
4745
4828
|
throw new Error("--admin-controls: expected a JSON object");
|
|
4746
4829
|
document = { ...document, ...parsed };
|
|
@@ -4790,8 +4873,11 @@ var start = async (target, values, config) => {
|
|
|
4790
4873
|
adminKey: config.adminKey,
|
|
4791
4874
|
seed: config.seed,
|
|
4792
4875
|
onLog: formatLog(config.log),
|
|
4876
|
+
...config.settings !== void 0 ? { settings: config.settings } : {},
|
|
4793
4877
|
...config.adminControls !== void 0 ? { adminControls: config.adminControls } : {}
|
|
4794
4878
|
});
|
|
4879
|
+
if (config.idBase !== void 0)
|
|
4880
|
+
runtime.setIdBase?.(config.idBase);
|
|
4795
4881
|
const listening = await listen(runtime, { port: config.port, host: config.host });
|
|
4796
4882
|
target.listening?.(listening);
|
|
4797
4883
|
console.log(`${target.name} emulator listening on ${listening.url}`);
|
|
@@ -4837,6 +4923,11 @@ var serveCommand = (target) => ({
|
|
|
4837
4923
|
const adminControls = await resolveServeControls(values, process.env, config.adminControls);
|
|
4838
4924
|
if (adminControls !== void 0)
|
|
4839
4925
|
config.adminControls = adminControls;
|
|
4926
|
+
if (values.settings !== void 0)
|
|
4927
|
+
throw new Error("--settings serves a single service; give each service its own `settings` in the config");
|
|
4928
|
+
const idBase = config.idBase ?? resolveServeIdBase(values);
|
|
4929
|
+
if (idBase !== void 0)
|
|
4930
|
+
config.idBase = idBase;
|
|
4840
4931
|
const fleet = await startFleet(config, {
|
|
4841
4932
|
load: (name) => loadTarget(name, target),
|
|
4842
4933
|
onLog: formatLog(config.log ?? log),
|
|
@@ -4866,8 +4957,12 @@ var serveCommand = (target) => ({
|
|
|
4866
4957
|
let listening;
|
|
4867
4958
|
try {
|
|
4868
4959
|
const adminControls = await resolveServeControls(values);
|
|
4960
|
+
const idBase = resolveServeIdBase(values);
|
|
4961
|
+
const settings = await resolveServeSettings(target, values);
|
|
4869
4962
|
listening = await start(target, values, {
|
|
4870
4963
|
...adminControls !== void 0 ? { adminControls } : {},
|
|
4964
|
+
...idBase !== void 0 ? { idBase } : {},
|
|
4965
|
+
...settings !== void 0 ? { settings } : {},
|
|
4871
4966
|
adminPrefix: resolveAdminPrefix(asString(values["admin-prefix"]) ?? process.env.MOCKINGBIRD_ADMIN_PREFIX),
|
|
4872
4967
|
port: port === void 0 ? target.defaultPort : Number.parseInt(port, 10),
|
|
4873
4968
|
host: asString(values.host) ?? "127.0.0.1",
|
|
@@ -4883,6 +4978,84 @@ var serveCommand = (target) => ({
|
|
|
4883
4978
|
}
|
|
4884
4979
|
});
|
|
4885
4980
|
|
|
4981
|
+
// src/boot.ts
|
|
4982
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4983
|
+
var strings = (value, where) => {
|
|
4984
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string"))
|
|
4985
|
+
throw new Error(`${where} must be an array of strings`);
|
|
4986
|
+
return value;
|
|
4987
|
+
};
|
|
4988
|
+
var KEYS = [
|
|
4989
|
+
"workspaces",
|
|
4990
|
+
"legacyEnvironmentIds",
|
|
4991
|
+
"apiKeys",
|
|
4992
|
+
"webhookId",
|
|
4993
|
+
"contactsEnabled",
|
|
4994
|
+
"validation"
|
|
4995
|
+
];
|
|
4996
|
+
var parseBootSettings = (input) => {
|
|
4997
|
+
for (const key of Object.keys(input))
|
|
4998
|
+
if (!KEYS.includes(key)) throw new Error(`unknown setting ${JSON.stringify(key)}`);
|
|
4999
|
+
const out = {};
|
|
5000
|
+
if (input.workspaces !== void 0) out.workspaces = strings(input.workspaces, "workspaces");
|
|
5001
|
+
if (input.legacyEnvironmentIds !== void 0) {
|
|
5002
|
+
const aliases = input.legacyEnvironmentIds;
|
|
5003
|
+
if (!isRecord(aliases) || Object.values(aliases).some((id) => typeof id !== "string"))
|
|
5004
|
+
throw new Error("legacyEnvironmentIds must be an object of workspace ids by environment id");
|
|
5005
|
+
out.legacyEnvironmentIds = aliases;
|
|
5006
|
+
}
|
|
5007
|
+
if (input.apiKeys !== void 0) out.apiKeys = strings(input.apiKeys, "apiKeys");
|
|
5008
|
+
if (input.webhookId !== void 0) {
|
|
5009
|
+
if (typeof input.webhookId !== "string") throw new Error("webhookId must be a string");
|
|
5010
|
+
out.webhookId = input.webhookId;
|
|
5011
|
+
}
|
|
5012
|
+
if (input.contactsEnabled !== void 0) {
|
|
5013
|
+
if (typeof input.contactsEnabled !== "boolean")
|
|
5014
|
+
throw new Error("contactsEnabled must be a boolean");
|
|
5015
|
+
out.contactsEnabled = input.contactsEnabled;
|
|
5016
|
+
}
|
|
5017
|
+
if (input.validation !== void 0) {
|
|
5018
|
+
if (input.validation !== "present-only" && input.validation !== "finished-validates-all")
|
|
5019
|
+
throw new Error('validation must be "present-only" or "finished-validates-all"');
|
|
5020
|
+
out.validation = input.validation;
|
|
5021
|
+
}
|
|
5022
|
+
return out;
|
|
5023
|
+
};
|
|
5024
|
+
var parseSurveysDocument = (input, legacyEnvironmentIds = {}) => {
|
|
5025
|
+
const list = Array.isArray(input) ? input : isRecord(input) ? input.surveys : void 0;
|
|
5026
|
+
if (!Array.isArray(list)) throw new Error("expected [survey] or {surveys: [survey]}");
|
|
5027
|
+
return list.map((entry, index) => {
|
|
5028
|
+
const where = `surveys[${index}]`;
|
|
5029
|
+
if (!isRecord(entry)) throw new Error(`${where} must be an object`);
|
|
5030
|
+
if (typeof entry.id !== "string" || entry.id === "")
|
|
5031
|
+
throw new Error(`${where}.id must be a non-empty string`);
|
|
5032
|
+
if (typeof entry.name !== "string") throw new Error(`${where}.name must be a string`);
|
|
5033
|
+
const { environmentId, ...rest } = entry;
|
|
5034
|
+
for (const key of ["workspaceId", "environmentId", "type", "status"])
|
|
5035
|
+
if (entry[key] !== void 0 && entry[key] !== null && typeof entry[key] !== "string")
|
|
5036
|
+
throw new Error(`${where}.${key} must be a string`);
|
|
5037
|
+
const owner = entry.workspaceId ?? environmentId;
|
|
5038
|
+
return {
|
|
5039
|
+
...rest,
|
|
5040
|
+
id: entry.id,
|
|
5041
|
+
name: entry.name,
|
|
5042
|
+
type: entry.type ?? "app",
|
|
5043
|
+
status: entry.status ?? "inProgress",
|
|
5044
|
+
workspaceId: owner ? legacyEnvironmentIds[owner] ?? owner : null
|
|
5045
|
+
};
|
|
5046
|
+
});
|
|
5047
|
+
};
|
|
5048
|
+
var appendSurveys = (base, extra) => {
|
|
5049
|
+
const seen = new Set(base.map((survey) => survey.id));
|
|
5050
|
+
const out = [...base];
|
|
5051
|
+
for (const survey of extra) {
|
|
5052
|
+
if (seen.has(survey.id)) continue;
|
|
5053
|
+
seen.add(survey.id);
|
|
5054
|
+
out.push(survey);
|
|
5055
|
+
}
|
|
5056
|
+
return out;
|
|
5057
|
+
};
|
|
5058
|
+
|
|
4886
5059
|
// src/server.ts
|
|
4887
5060
|
var DEFAULT_PORT = 8813;
|
|
4888
5061
|
var createServer2 = async (options = {}) => {
|
|
@@ -4895,9 +5068,24 @@ var createServer2 = async (options = {}) => {
|
|
|
4895
5068
|
return { ...listening, runtime };
|
|
4896
5069
|
};
|
|
4897
5070
|
var text = (value) => typeof value === "string" ? value : void 0;
|
|
5071
|
+
var loadSurveysFile = (path, mode = "append", legacyEnvironmentIds = DEFAULT_SETTINGS.legacyEnvironmentIds) => {
|
|
5072
|
+
let raw;
|
|
5073
|
+
try {
|
|
5074
|
+
raw = readFileSync(path, "utf8");
|
|
5075
|
+
} catch {
|
|
5076
|
+
throw new Error(`formbricks surveys not found: ${path}`);
|
|
5077
|
+
}
|
|
5078
|
+
try {
|
|
5079
|
+
const surveys = parseSurveysDocument(JSON.parse(raw), legacyEnvironmentIds);
|
|
5080
|
+
return appendSurveys(mode === "replace" ? [] : CORPUS_SURVEYS, surveys);
|
|
5081
|
+
} catch (error) {
|
|
5082
|
+
throw new Error(`formbricks surveys ${path}: ${error.message}`);
|
|
5083
|
+
}
|
|
5084
|
+
};
|
|
4898
5085
|
var serveTarget = {
|
|
4899
5086
|
name: "formbricks",
|
|
4900
5087
|
defaultPort: DEFAULT_PORT,
|
|
5088
|
+
parseSettings: (settings) => parseBootSettings(settings),
|
|
4901
5089
|
options: {
|
|
4902
5090
|
"webhook-url": {
|
|
4903
5091
|
type: "string",
|
|
@@ -4918,6 +5106,16 @@ var serveTarget = {
|
|
|
4918
5106
|
type: "string",
|
|
4919
5107
|
value: "<id>",
|
|
4920
5108
|
description: "An extra workspace id that serves the survey corpus"
|
|
5109
|
+
},
|
|
5110
|
+
surveys: {
|
|
5111
|
+
type: "string",
|
|
5112
|
+
value: "<file.json>",
|
|
5113
|
+
description: "Surveys every namespace starts with, as a list or {surveys: [...]}, added after the corpus surveys (a repeated id is skipped)"
|
|
5114
|
+
},
|
|
5115
|
+
"surveys-mode": {
|
|
5116
|
+
type: "string",
|
|
5117
|
+
value: "<append|replace>",
|
|
5118
|
+
description: "With --surveys: add to the corpus (default) or use the file as the whole corpus"
|
|
4921
5119
|
}
|
|
4922
5120
|
},
|
|
4923
5121
|
create: (values, common) => {
|
|
@@ -4925,10 +5123,28 @@ var serveTarget = {
|
|
|
4925
5123
|
const token = text(values["webhook-token"]);
|
|
4926
5124
|
const secret = text(values["webhook-secret"]);
|
|
4927
5125
|
const workspace = text(values["workspace-id"]);
|
|
5126
|
+
const surveysPath = text(values.surveys);
|
|
5127
|
+
const mode = text(values["surveys-mode"]);
|
|
5128
|
+
if (mode !== void 0 && mode !== "append" && mode !== "replace")
|
|
5129
|
+
throw new Error('--surveys-mode must be "append" or "replace"');
|
|
5130
|
+
if (mode !== void 0 && surveysPath === void 0)
|
|
5131
|
+
throw new Error("--surveys-mode needs --surveys");
|
|
5132
|
+
const boot = common.settings;
|
|
5133
|
+
const settings = {
|
|
5134
|
+
...boot,
|
|
5135
|
+
...workspace ? { workspaces: [workspace, ...boot?.workspaces ?? DEFAULT_SETTINGS.workspaces] } : {}
|
|
5136
|
+
};
|
|
4928
5137
|
const target = url && token ? `${url}${url.includes("?") ? "&" : "?"}token=${encodeURIComponent(token)}` : url;
|
|
4929
5138
|
return createRuntime({
|
|
4930
5139
|
...target ? { webhooks: { url: target, ...secret ? { secret } : {} } } : {},
|
|
4931
|
-
...
|
|
5140
|
+
...Object.keys(settings).length > 0 ? { settings } : {},
|
|
5141
|
+
...surveysPath ? {
|
|
5142
|
+
surveys: loadSurveysFile(
|
|
5143
|
+
surveysPath,
|
|
5144
|
+
mode,
|
|
5145
|
+
settings.legacyEnvironmentIds ?? DEFAULT_SETTINGS.legacyEnvironmentIds
|
|
5146
|
+
)
|
|
5147
|
+
} : {},
|
|
4932
5148
|
...common.adminPrefix !== void 0 ? { adminPrefix: common.adminPrefix } : {},
|
|
4933
5149
|
...common.adminKey !== void 0 ? { adminKey: common.adminKey } : {},
|
|
4934
5150
|
...common.adminControls !== void 0 ? { adminControls: common.adminControls } : {},
|
|
@@ -4947,6 +5163,7 @@ export {
|
|
|
4947
5163
|
serveCommand,
|
|
4948
5164
|
DEFAULT_PORT,
|
|
4949
5165
|
createServer2 as createServer,
|
|
5166
|
+
loadSurveysFile,
|
|
4950
5167
|
serveTarget
|
|
4951
5168
|
};
|
|
4952
|
-
//# sourceMappingURL=chunk-
|
|
5169
|
+
//# sourceMappingURL=chunk-M3VADEIL.js.map
|