@crvouga/mockingbird-service-posthog 1.7.1 → 1.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 +15 -0
- package/dist/chunk-LKPLI7YI.js +827 -0
- package/dist/chunk-LKPLI7YI.js.map +7 -0
- package/dist/{chunk-ZRB53DZ6.js → chunk-Y3KAEWE2.js} +166 -27
- package/dist/chunk-Y3KAEWE2.js.map +7 -0
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +31 -2
- package/dist/index.js +1 -1
- package/dist/server.d.ts +62 -4
- package/dist/server.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-FWRNARH5.js +0 -360
- package/dist/chunk-FWRNARH5.js.map +0 -7
- package/dist/chunk-ZRB53DZ6.js.map +0 -7
package/dist/cli.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -280,6 +280,15 @@ type FaultCandidate = {
|
|
|
280
280
|
namespace: string;
|
|
281
281
|
};
|
|
282
282
|
type FaultRegistry = {
|
|
283
|
+
isolateNamespaces(): void;
|
|
284
|
+
snapshot(namespace: string): {
|
|
285
|
+
rules: (FaultRule & {
|
|
286
|
+
remaining: number | null;
|
|
287
|
+
hits: number;
|
|
288
|
+
position: number;
|
|
289
|
+
})[];
|
|
290
|
+
rngState: number;
|
|
291
|
+
};
|
|
283
292
|
add(rule: FaultRule): FaultRule;
|
|
284
293
|
list(): (FaultRule & {
|
|
285
294
|
remaining: number | null;
|
|
@@ -287,6 +296,14 @@ type FaultRegistry = {
|
|
|
287
296
|
})[];
|
|
288
297
|
remove(id: string): boolean;
|
|
289
298
|
clear(): void;
|
|
299
|
+
restore(namespace: string, state: {
|
|
300
|
+
rules: (FaultRule & {
|
|
301
|
+
remaining: number | null;
|
|
302
|
+
hits: number;
|
|
303
|
+
position: number;
|
|
304
|
+
})[];
|
|
305
|
+
rngState: number;
|
|
306
|
+
}): void;
|
|
290
307
|
/**
|
|
291
308
|
* Every fault this request should get, in rule order, stopping at the first that answers
|
|
292
309
|
* or drops (effect-only and delay-only rules let later rules match too). Consumes one of
|
|
@@ -464,8 +481,8 @@ type MetricsReport = {
|
|
|
464
481
|
};
|
|
465
482
|
type Metrics = {
|
|
466
483
|
record(entry: RequestLog): void;
|
|
467
|
-
report(): MetricsReport;
|
|
468
|
-
reset(): void;
|
|
484
|
+
report(namespace?: string): MetricsReport;
|
|
485
|
+
reset(namespace?: string): void;
|
|
469
486
|
};
|
|
470
487
|
|
|
471
488
|
/** One journal entry: a request log stamped with when (on the mock clock) it was handled. */
|
|
@@ -631,6 +648,9 @@ type PublishInput = {
|
|
|
631
648
|
id?: string;
|
|
632
649
|
};
|
|
633
650
|
type WebhookHub = {
|
|
651
|
+
pending(namespace: string): number;
|
|
652
|
+
snapshot(namespace: string): unknown;
|
|
653
|
+
restore(namespace: string, snapshot: unknown): void;
|
|
634
654
|
publish(input: PublishInput): WebhookMessage;
|
|
635
655
|
/** Replace a namespace's own endpoints (`PUT /__admin/webhook-endpoints`). */
|
|
636
656
|
setEndpoints(namespace: string, endpoints: WebhookEndpoint[]): WebhookEndpoint[];
|
|
@@ -658,6 +678,8 @@ type ServiceTimelineState = Readonly<{
|
|
|
658
678
|
}>;
|
|
659
679
|
type ServiceCheckpoint = Checkpoint<ServiceTimelineState>;
|
|
660
680
|
type ServiceRuntime<T extends ServiceInstance> = FetchAPI & {
|
|
681
|
+
/** Stop package-owned lifecycle timers when a supervisor closes the runtime. */
|
|
682
|
+
stop?(): void;
|
|
661
683
|
readonly name: string;
|
|
662
684
|
readonly sqlite: SqliteClient$1;
|
|
663
685
|
readonly clock: Clock;
|
|
@@ -668,6 +690,13 @@ type ServiceRuntime<T extends ServiceInstance> = FetchAPI & {
|
|
|
668
690
|
readonly credentials: CredentialRegistry;
|
|
669
691
|
/** The webhook hub, when the service has outbound webhooks. */
|
|
670
692
|
readonly webhooks: WebhookHub | undefined;
|
|
693
|
+
/** Enable independent namespace clocks and PRNGs before serving a fleet. */
|
|
694
|
+
isolateNamespaces(): void;
|
|
695
|
+
namespaceClock(namespace?: string): Clock;
|
|
696
|
+
namespaceOf(request: Request): string;
|
|
697
|
+
/** Opaque in-process checkpoint including records, controls and webhook history. */
|
|
698
|
+
fleetSnapshot(namespace: string): object;
|
|
699
|
+
fleetRestore(snapshot: unknown, namespace: string): void;
|
|
671
700
|
/** Expand a named preset into fault rules (and webhook faults) for `namespace`. */
|
|
672
701
|
applyPreset(name: string, namespace?: string, overrides?: Partial<FaultRule>): FaultRule[];
|
|
673
702
|
/** The instance behind `namespace` (the default one when omitted), created on first use. */
|
package/dist/index.js
CHANGED
package/dist/server.d.ts
CHANGED
|
@@ -155,6 +155,15 @@ type FaultCandidate$1 = {
|
|
|
155
155
|
namespace: string;
|
|
156
156
|
};
|
|
157
157
|
type FaultRegistry$1 = {
|
|
158
|
+
isolateNamespaces(): void;
|
|
159
|
+
snapshot(namespace: string): {
|
|
160
|
+
rules: (FaultRule$1 & {
|
|
161
|
+
remaining: number | null;
|
|
162
|
+
hits: number;
|
|
163
|
+
position: number;
|
|
164
|
+
})[];
|
|
165
|
+
rngState: number;
|
|
166
|
+
};
|
|
158
167
|
add(rule: FaultRule$1): FaultRule$1;
|
|
159
168
|
list(): (FaultRule$1 & {
|
|
160
169
|
remaining: number | null;
|
|
@@ -162,6 +171,14 @@ type FaultRegistry$1 = {
|
|
|
162
171
|
})[];
|
|
163
172
|
remove(id: string): boolean;
|
|
164
173
|
clear(): void;
|
|
174
|
+
restore(namespace: string, state: {
|
|
175
|
+
rules: (FaultRule$1 & {
|
|
176
|
+
remaining: number | null;
|
|
177
|
+
hits: number;
|
|
178
|
+
position: number;
|
|
179
|
+
})[];
|
|
180
|
+
rngState: number;
|
|
181
|
+
}): void;
|
|
165
182
|
/**
|
|
166
183
|
* Every fault this request should get, in rule order, stopping at the first that answers
|
|
167
184
|
* or drops (effect-only and delay-only rules let later rules match too). Consumes one of
|
|
@@ -326,8 +343,8 @@ type MetricsReport$1 = {
|
|
|
326
343
|
};
|
|
327
344
|
type Metrics$1 = {
|
|
328
345
|
record(entry: RequestLog$1): void;
|
|
329
|
-
report(): MetricsReport$1;
|
|
330
|
-
reset(): void;
|
|
346
|
+
report(namespace?: string): MetricsReport$1;
|
|
347
|
+
reset(namespace?: string): void;
|
|
331
348
|
};
|
|
332
349
|
|
|
333
350
|
/** One journal entry: a request log stamped with when (on the mock clock) it was handled. */
|
|
@@ -481,6 +498,9 @@ type PublishInput$1 = {
|
|
|
481
498
|
id?: string;
|
|
482
499
|
};
|
|
483
500
|
type WebhookHub$1 = {
|
|
501
|
+
pending(namespace: string): number;
|
|
502
|
+
snapshot(namespace: string): unknown;
|
|
503
|
+
restore(namespace: string, snapshot: unknown): void;
|
|
484
504
|
publish(input: PublishInput$1): WebhookMessage$1;
|
|
485
505
|
/** Replace a namespace's own endpoints (`PUT /__admin/webhook-endpoints`). */
|
|
486
506
|
setEndpoints(namespace: string, endpoints: WebhookEndpoint$1[]): WebhookEndpoint$1[];
|
|
@@ -508,6 +528,8 @@ type ServiceTimelineState$1 = Readonly<{
|
|
|
508
528
|
}>;
|
|
509
529
|
type ServiceCheckpoint$1 = Checkpoint$1<ServiceTimelineState$1>;
|
|
510
530
|
type ServiceRuntime$1<T extends ServiceInstance$1> = FetchAPI$2 & {
|
|
531
|
+
/** Stop package-owned lifecycle timers when a supervisor closes the runtime. */
|
|
532
|
+
stop?(): void;
|
|
511
533
|
readonly name: string;
|
|
512
534
|
readonly sqlite: SqliteClient$2;
|
|
513
535
|
readonly clock: Clock$1;
|
|
@@ -518,6 +540,13 @@ type ServiceRuntime$1<T extends ServiceInstance$1> = FetchAPI$2 & {
|
|
|
518
540
|
readonly credentials: CredentialRegistry$1;
|
|
519
541
|
/** The webhook hub, when the service has outbound webhooks. */
|
|
520
542
|
readonly webhooks: WebhookHub$1 | undefined;
|
|
543
|
+
/** Enable independent namespace clocks and PRNGs before serving a fleet. */
|
|
544
|
+
isolateNamespaces(): void;
|
|
545
|
+
namespaceClock(namespace?: string): Clock$1;
|
|
546
|
+
namespaceOf(request: Request): string;
|
|
547
|
+
/** Opaque in-process checkpoint including records, controls and webhook history. */
|
|
548
|
+
fleetSnapshot(namespace: string): object;
|
|
549
|
+
fleetRestore(snapshot: unknown, namespace: string): void;
|
|
521
550
|
/** Expand a named preset into fault rules (and webhook faults) for `namespace`. */
|
|
522
551
|
applyPreset(name: string, namespace?: string, overrides?: Partial<FaultRule$1>): FaultRule$1[];
|
|
523
552
|
/** The instance behind `namespace` (the default one when omitted), created on first use. */
|
|
@@ -781,6 +810,15 @@ type FaultCandidate = {
|
|
|
781
810
|
namespace: string;
|
|
782
811
|
};
|
|
783
812
|
type FaultRegistry = {
|
|
813
|
+
isolateNamespaces(): void;
|
|
814
|
+
snapshot(namespace: string): {
|
|
815
|
+
rules: (FaultRule & {
|
|
816
|
+
remaining: number | null;
|
|
817
|
+
hits: number;
|
|
818
|
+
position: number;
|
|
819
|
+
})[];
|
|
820
|
+
rngState: number;
|
|
821
|
+
};
|
|
784
822
|
add(rule: FaultRule): FaultRule;
|
|
785
823
|
list(): (FaultRule & {
|
|
786
824
|
remaining: number | null;
|
|
@@ -788,6 +826,14 @@ type FaultRegistry = {
|
|
|
788
826
|
})[];
|
|
789
827
|
remove(id: string): boolean;
|
|
790
828
|
clear(): void;
|
|
829
|
+
restore(namespace: string, state: {
|
|
830
|
+
rules: (FaultRule & {
|
|
831
|
+
remaining: number | null;
|
|
832
|
+
hits: number;
|
|
833
|
+
position: number;
|
|
834
|
+
})[];
|
|
835
|
+
rngState: number;
|
|
836
|
+
}): void;
|
|
791
837
|
/**
|
|
792
838
|
* Every fault this request should get, in rule order, stopping at the first that answers
|
|
793
839
|
* or drops (effect-only and delay-only rules let later rules match too). Consumes one of
|
|
@@ -965,8 +1011,8 @@ type MetricsReport = {
|
|
|
965
1011
|
};
|
|
966
1012
|
type Metrics = {
|
|
967
1013
|
record(entry: RequestLog): void;
|
|
968
|
-
report(): MetricsReport;
|
|
969
|
-
reset(): void;
|
|
1014
|
+
report(namespace?: string): MetricsReport;
|
|
1015
|
+
reset(namespace?: string): void;
|
|
970
1016
|
};
|
|
971
1017
|
|
|
972
1018
|
/** One journal entry: a request log stamped with when (on the mock clock) it was handled. */
|
|
@@ -1132,6 +1178,9 @@ type PublishInput = {
|
|
|
1132
1178
|
id?: string;
|
|
1133
1179
|
};
|
|
1134
1180
|
type WebhookHub = {
|
|
1181
|
+
pending(namespace: string): number;
|
|
1182
|
+
snapshot(namespace: string): unknown;
|
|
1183
|
+
restore(namespace: string, snapshot: unknown): void;
|
|
1135
1184
|
publish(input: PublishInput): WebhookMessage;
|
|
1136
1185
|
/** Replace a namespace's own endpoints (`PUT /__admin/webhook-endpoints`). */
|
|
1137
1186
|
setEndpoints(namespace: string, endpoints: WebhookEndpoint[]): WebhookEndpoint[];
|
|
@@ -1159,6 +1208,8 @@ type ServiceTimelineState = Readonly<{
|
|
|
1159
1208
|
}>;
|
|
1160
1209
|
type ServiceCheckpoint = Checkpoint<ServiceTimelineState>;
|
|
1161
1210
|
type ServiceRuntime<T extends ServiceInstance> = FetchAPI$1 & {
|
|
1211
|
+
/** Stop package-owned lifecycle timers when a supervisor closes the runtime. */
|
|
1212
|
+
stop?(): void;
|
|
1162
1213
|
readonly name: string;
|
|
1163
1214
|
readonly sqlite: SqliteClient$1;
|
|
1164
1215
|
readonly clock: Clock;
|
|
@@ -1169,6 +1220,13 @@ type ServiceRuntime<T extends ServiceInstance> = FetchAPI$1 & {
|
|
|
1169
1220
|
readonly credentials: CredentialRegistry;
|
|
1170
1221
|
/** The webhook hub, when the service has outbound webhooks. */
|
|
1171
1222
|
readonly webhooks: WebhookHub | undefined;
|
|
1223
|
+
/** Enable independent namespace clocks and PRNGs before serving a fleet. */
|
|
1224
|
+
isolateNamespaces(): void;
|
|
1225
|
+
namespaceClock(namespace?: string): Clock;
|
|
1226
|
+
namespaceOf(request: Request): string;
|
|
1227
|
+
/** Opaque in-process checkpoint including records, controls and webhook history. */
|
|
1228
|
+
fleetSnapshot(namespace: string): object;
|
|
1229
|
+
fleetRestore(snapshot: unknown, namespace: string): void;
|
|
1172
1230
|
/** Expand a named preset into fault rules (and webhook faults) for `namespace`. */
|
|
1173
1231
|
applyPreset(name: string, namespace?: string, overrides?: Partial<FaultRule>): FaultRule[];
|
|
1174
1232
|
/** The instance behind `namespace` (the default one when omitted), created on first use. */
|
package/dist/server.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crvouga/mockingbird-service-posthog",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Stateful mock of PostHog: /flags v2 and legacy /decide evaluation with per-test flag, variant and payload control, remote config, gzip/base64 capture (/batch/, /e/, /i/v0/e/), recordings intake, and the feature-flag management and HogQL API slice.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/dist/chunk-FWRNARH5.js
DELETED
|
@@ -1,360 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ACME_FLAG_STATE,
|
|
3
|
-
createRuntime,
|
|
4
|
-
specsFromState
|
|
5
|
-
} from "./chunk-ZRB53DZ6.js";
|
|
6
|
-
|
|
7
|
-
// ../../adapters/node/dist/cli.js
|
|
8
|
-
import { readFile } from "node:fs/promises";
|
|
9
|
-
import { parseArgs } from "node:util";
|
|
10
|
-
|
|
11
|
-
// ../../adapters/node/dist/serve.js
|
|
12
|
-
import { createServer } from "node:http";
|
|
13
|
-
var serve = async (api, options = {}) => {
|
|
14
|
-
const server = createServer(async (req, res) => {
|
|
15
|
-
const chunks = [];
|
|
16
|
-
for await (const chunk of req) {
|
|
17
|
-
chunks.push(Buffer.from(chunk));
|
|
18
|
-
}
|
|
19
|
-
const body = Buffer.concat(chunks);
|
|
20
|
-
const address = server.address();
|
|
21
|
-
const port = typeof address === "object" && address !== null ? address.port : void 0;
|
|
22
|
-
const base = `http://${req.headers.host ?? `localhost:${port ?? 80}`}`;
|
|
23
|
-
const raw = req.url ?? "/";
|
|
24
|
-
const url = new URL(raw.replace(/^\/+/, "/"), base);
|
|
25
|
-
const method = req.method ?? "GET";
|
|
26
|
-
const init = { method, headers: req.headers };
|
|
27
|
-
if (method !== "GET" && method !== "HEAD" && body.length > 0) {
|
|
28
|
-
init.body = body;
|
|
29
|
-
}
|
|
30
|
-
const aborted = new AbortController();
|
|
31
|
-
res.once("close", () => {
|
|
32
|
-
if (!res.writableFinished)
|
|
33
|
-
aborted.abort();
|
|
34
|
-
});
|
|
35
|
-
init.signal = aborted.signal;
|
|
36
|
-
const request = new Request(url, init);
|
|
37
|
-
let response;
|
|
38
|
-
try {
|
|
39
|
-
response = await api.fetch(request);
|
|
40
|
-
} catch (error) {
|
|
41
|
-
if (error.code === "MOCKINGBIRD_DROP") {
|
|
42
|
-
req.socket.destroy();
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
res.writeHead(500, { "content-type": "application/json" });
|
|
46
|
-
res.end(JSON.stringify({
|
|
47
|
-
error: {
|
|
48
|
-
type: "mockingbird_internal",
|
|
49
|
-
message: error instanceof Error ? error.message : String(error)
|
|
50
|
-
}
|
|
51
|
-
}));
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
const headers = Object.fromEntries(response.headers);
|
|
55
|
-
const cookies = response.headers.getSetCookie();
|
|
56
|
-
if (cookies.length > 0)
|
|
57
|
-
headers["set-cookie"] = cookies;
|
|
58
|
-
if (!response.body) {
|
|
59
|
-
res.writeHead(response.status, headers);
|
|
60
|
-
res.end();
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
res.writeHead(response.status, headers);
|
|
64
|
-
res.flushHeaders();
|
|
65
|
-
const reader = response.body.getReader();
|
|
66
|
-
try {
|
|
67
|
-
for (; ; ) {
|
|
68
|
-
const { done, value } = await reader.read();
|
|
69
|
-
if (done)
|
|
70
|
-
break;
|
|
71
|
-
if (!res.write(value))
|
|
72
|
-
await new Promise((resolve) => res.once("drain", resolve));
|
|
73
|
-
}
|
|
74
|
-
res.end();
|
|
75
|
-
} catch {
|
|
76
|
-
res.destroy();
|
|
77
|
-
} finally {
|
|
78
|
-
reader.releaseLock();
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
await new Promise((resolve, reject) => {
|
|
82
|
-
server.once("error", reject);
|
|
83
|
-
server.listen(options.port ?? 0, options.host, resolve);
|
|
84
|
-
});
|
|
85
|
-
return server;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
// ../../adapters/node/dist/listen.js
|
|
89
|
-
var listen = async (api, options = {}) => {
|
|
90
|
-
const host = options.host ?? "127.0.0.1";
|
|
91
|
-
const server = await serve(api, { port: options.port ?? 0, host });
|
|
92
|
-
const address = server.address();
|
|
93
|
-
const port = typeof address === "object" && address !== null ? address.port : options.port ?? 0;
|
|
94
|
-
const shown = host.includes(":") ? `[${host}]` : host;
|
|
95
|
-
return {
|
|
96
|
-
url: `http://${shown}:${port}`,
|
|
97
|
-
port,
|
|
98
|
-
host,
|
|
99
|
-
server,
|
|
100
|
-
close: () => new Promise((resolve, reject) => {
|
|
101
|
-
server.close((error) => {
|
|
102
|
-
if (error && error.code !== "ERR_SERVER_NOT_RUNNING")
|
|
103
|
-
reject(error);
|
|
104
|
-
else
|
|
105
|
-
resolve();
|
|
106
|
-
});
|
|
107
|
-
server.closeAllConnections?.();
|
|
108
|
-
})
|
|
109
|
-
};
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
// ../../adapters/node/dist/cli.js
|
|
113
|
-
var optionHelp = (options) => Object.entries(options).map(([name, option]) => {
|
|
114
|
-
const flag = `--${name}${option.type === "string" ? ` ${option.value ?? "<value>"}` : ""}`;
|
|
115
|
-
const fallback = option.default !== void 0 ? ` (default: ${String(option.default)})` : "";
|
|
116
|
-
return ` ${flag.padEnd(30)} ${option.description}${fallback}`;
|
|
117
|
-
});
|
|
118
|
-
var help = (spec) => [
|
|
119
|
-
`${spec.bin} \u2014 ${spec.description}`,
|
|
120
|
-
"",
|
|
121
|
-
"Usage:",
|
|
122
|
-
` ${spec.bin} <command> [options]`,
|
|
123
|
-
"",
|
|
124
|
-
"Commands:",
|
|
125
|
-
...Object.entries(spec.commands).map(([name, c]) => ` ${name.padEnd(30)} ${c.summary}`),
|
|
126
|
-
"",
|
|
127
|
-
`Run \`${spec.bin} <command> --help\` for a command's options.`
|
|
128
|
-
].join("\n");
|
|
129
|
-
var commandHelp = (spec, name, command) => [
|
|
130
|
-
`${spec.bin} ${name} \u2014 ${command.summary}`,
|
|
131
|
-
"",
|
|
132
|
-
"Usage:",
|
|
133
|
-
` ${command.usage ?? `${spec.bin} ${name} [options]`}`,
|
|
134
|
-
...command.options ? ["", "Options:", ...optionHelp(command.options)] : []
|
|
135
|
-
].join("\n");
|
|
136
|
-
var runCli = async (spec, argv) => {
|
|
137
|
-
const pair = argv.length >= 2 ? `${argv[0]} ${argv[1]}` : void 0;
|
|
138
|
-
const words = pair !== void 0 && spec.commands[pair] ? 2 : 1;
|
|
139
|
-
const name = words === 2 ? pair : argv[0];
|
|
140
|
-
const rest = argv.slice(words);
|
|
141
|
-
if (name === void 0 || name === "--help" || name === "-h" || name === "help") {
|
|
142
|
-
console.log(help(spec));
|
|
143
|
-
return 0;
|
|
144
|
-
}
|
|
145
|
-
const command = spec.commands[name];
|
|
146
|
-
if (!command) {
|
|
147
|
-
console.error(`${spec.bin}: unknown command ${JSON.stringify(name)}
|
|
148
|
-
|
|
149
|
-
${help(spec)}`);
|
|
150
|
-
return 2;
|
|
151
|
-
}
|
|
152
|
-
if (rest.includes("--help") || rest.includes("-h")) {
|
|
153
|
-
console.log(commandHelp(spec, name, command));
|
|
154
|
-
return 0;
|
|
155
|
-
}
|
|
156
|
-
let parsed;
|
|
157
|
-
try {
|
|
158
|
-
parsed = parseArgs({
|
|
159
|
-
args: rest,
|
|
160
|
-
allowPositionals: true,
|
|
161
|
-
strict: true,
|
|
162
|
-
options: Object.fromEntries(Object.entries(command.options ?? {}).map(([key, option]) => [
|
|
163
|
-
key,
|
|
164
|
-
{
|
|
165
|
-
type: option.type,
|
|
166
|
-
...option.default !== void 0 ? { default: option.default } : {}
|
|
167
|
-
}
|
|
168
|
-
]))
|
|
169
|
-
});
|
|
170
|
-
} catch (error) {
|
|
171
|
-
console.error(`${spec.bin} ${name}: ${error instanceof Error ? error.message : String(error)}
|
|
172
|
-
|
|
173
|
-
${commandHelp(spec, name, command)}`);
|
|
174
|
-
return 2;
|
|
175
|
-
}
|
|
176
|
-
return command.run(parsed.values, parsed.positionals);
|
|
177
|
-
};
|
|
178
|
-
var COMMON_SERVE_OPTIONS = {
|
|
179
|
-
port: { type: "string", value: "<port>", description: "Port to listen on" },
|
|
180
|
-
host: { type: "string", value: "<host>", description: "Interface to bind", default: "127.0.0.1" },
|
|
181
|
-
"admin-key": {
|
|
182
|
-
type: "string",
|
|
183
|
-
value: "<key>",
|
|
184
|
-
description: "Require x-mockingbird-admin-key on /__admin/* (env MOCKINGBIRD_ADMIN_KEY)"
|
|
185
|
-
},
|
|
186
|
-
seed: { type: "string", value: "<seed>", description: "Seed for every random choice" },
|
|
187
|
-
log: {
|
|
188
|
-
type: "string",
|
|
189
|
-
value: "<pretty|json|off>",
|
|
190
|
-
description: "Request log format",
|
|
191
|
-
default: "pretty"
|
|
192
|
-
},
|
|
193
|
-
"log-requests": {
|
|
194
|
-
type: "boolean",
|
|
195
|
-
description: "One JSON line per request: namespace, operationId, status, ids touched (never bodies). Same as --log json"
|
|
196
|
-
},
|
|
197
|
-
config: {
|
|
198
|
-
type: "string",
|
|
199
|
-
value: "<file>",
|
|
200
|
-
description: "Serve every service in a mockingbird.json config instead"
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
var formatLog = (format) => {
|
|
204
|
-
if (format === "off")
|
|
205
|
-
return void 0;
|
|
206
|
-
if (format === "json")
|
|
207
|
-
return (entry) => console.log(JSON.stringify(entry));
|
|
208
|
-
return (entry) => {
|
|
209
|
-
const op = entry.operationId ?? (entry.unmatched ? "UNMATCHED" : "-");
|
|
210
|
-
const ns = entry.namespace === "default" ? "" : ` [${entry.namespace}]`;
|
|
211
|
-
const fault = entry.faultId ? ` fault=${entry.faultId}` : "";
|
|
212
|
-
const adopted = entry.adopted ? " adopted" : "";
|
|
213
|
-
const received = entry.request ? ` received=${entry.request.bodyBytes === null ? "?" : `${entry.request.bodyBytes}B`} ct=${entry.request.contentType ?? "-"}${entry.request.transferEncoding ? ` te=${entry.request.transferEncoding}` : ""}` : "";
|
|
214
|
-
const issues = entry.issues?.length ? ` issues=${entry.issues.slice(0, 3).map((issue) => `${issue.path || "$"}: ${issue.message}`).join("; ")}${entry.issues.length > 3 ? ` (+${entry.issues.length - 3})` : ""}` : "";
|
|
215
|
-
console.log(`${entry.service} ${entry.method} ${entry.path} ${entry.status} ${op} ${entry.durationMs}ms${ns}${fault}${adopted}${received}${issues}`);
|
|
216
|
-
};
|
|
217
|
-
};
|
|
218
|
-
var asString = (value) => typeof value === "string" ? value : void 0;
|
|
219
|
-
var loadTarget = async (name, own) => {
|
|
220
|
-
if (name === own.name)
|
|
221
|
-
return own;
|
|
222
|
-
const specifier = `@crvouga/mockingbird-service-${name}/server`;
|
|
223
|
-
try {
|
|
224
|
-
const mod = await import(specifier);
|
|
225
|
-
if (!mod.serveTarget)
|
|
226
|
-
throw new Error(`${specifier} exports no serveTarget`);
|
|
227
|
-
return mod.serveTarget;
|
|
228
|
-
} catch (error) {
|
|
229
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
230
|
-
throw new Error(`cannot load service "${name}": ${reason}. Install @crvouga/mockingbird-service-${name}.`);
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
var start = async (target, values, config) => {
|
|
234
|
-
const runtime = await target.create(values, {
|
|
235
|
-
adminKey: config.adminKey,
|
|
236
|
-
seed: config.seed,
|
|
237
|
-
onLog: formatLog(config.log)
|
|
238
|
-
});
|
|
239
|
-
const listening = await listen(runtime, { port: config.port, host: config.host });
|
|
240
|
-
target.listening?.(listening);
|
|
241
|
-
console.log(`${target.name} mock listening on ${listening.url}`);
|
|
242
|
-
console.log(`${target.name} health: GET ${listening.url}/health`);
|
|
243
|
-
console.log(`${target.name} admin: ${listening.url}/__admin (${config.adminKey ? "x-mockingbird-admin-key required" : "open \u2014 pass --admin-key to lock"})`);
|
|
244
|
-
console.log(`${target.name} admin ui: ${listening.url}/__admin/ui`);
|
|
245
|
-
for (const line of target.banner?.(runtime) ?? [])
|
|
246
|
-
console.log(`${target.name} ${line}`);
|
|
247
|
-
return listening;
|
|
248
|
-
};
|
|
249
|
-
var untilSignal = async (servers) => new Promise((resolve) => {
|
|
250
|
-
const stop = async () => {
|
|
251
|
-
await Promise.allSettled(servers.map((s) => s.close()));
|
|
252
|
-
resolve(0);
|
|
253
|
-
};
|
|
254
|
-
process.once("SIGINT", stop);
|
|
255
|
-
process.once("SIGTERM", stop);
|
|
256
|
-
});
|
|
257
|
-
var serveCommand = (target) => ({
|
|
258
|
-
summary: `Serve the ${target.name} mock over HTTP`,
|
|
259
|
-
options: { ...COMMON_SERVE_OPTIONS, ...target.options },
|
|
260
|
-
async run(values) {
|
|
261
|
-
const log = values["log-requests"] === true ? "json" : asString(values.log) ?? "pretty";
|
|
262
|
-
if (!["pretty", "json", "off"].includes(log)) {
|
|
263
|
-
console.error(`--log must be pretty, json or off (got ${log})`);
|
|
264
|
-
return 2;
|
|
265
|
-
}
|
|
266
|
-
const configPath = asString(values.config);
|
|
267
|
-
if (configPath !== void 0) {
|
|
268
|
-
const config = JSON.parse(await readFile(configPath, "utf8"));
|
|
269
|
-
const servers = [];
|
|
270
|
-
try {
|
|
271
|
-
for (const [name, entry] of Object.entries(config.services ?? {})) {
|
|
272
|
-
const each = await loadTarget(name, target);
|
|
273
|
-
servers.push(await start(each, entry.options ?? {}, {
|
|
274
|
-
port: entry.port ?? each.defaultPort,
|
|
275
|
-
host: entry.host ?? "127.0.0.1",
|
|
276
|
-
...entry.adminKey !== void 0 ? { adminKey: entry.adminKey } : {},
|
|
277
|
-
...entry.seed !== void 0 ? { seed: entry.seed } : {},
|
|
278
|
-
log: config.log ?? log
|
|
279
|
-
}));
|
|
280
|
-
}
|
|
281
|
-
} catch (error) {
|
|
282
|
-
await Promise.allSettled(servers.map((s) => s.close()));
|
|
283
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
284
|
-
return 1;
|
|
285
|
-
}
|
|
286
|
-
return untilSignal(servers);
|
|
287
|
-
}
|
|
288
|
-
const port = asString(values.port);
|
|
289
|
-
const adminKey = asString(values["admin-key"]) ?? process.env.MOCKINGBIRD_ADMIN_KEY;
|
|
290
|
-
const seed = asString(values.seed);
|
|
291
|
-
let listening;
|
|
292
|
-
try {
|
|
293
|
-
listening = await start(target, values, {
|
|
294
|
-
port: port === void 0 ? target.defaultPort : Number.parseInt(port, 10),
|
|
295
|
-
host: asString(values.host) ?? "127.0.0.1",
|
|
296
|
-
...adminKey !== void 0 ? { adminKey } : {},
|
|
297
|
-
...seed !== void 0 ? { seed } : {},
|
|
298
|
-
log
|
|
299
|
-
});
|
|
300
|
-
} catch (error) {
|
|
301
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
302
|
-
return 1;
|
|
303
|
-
}
|
|
304
|
-
return untilSignal([listening]);
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
// src/server.ts
|
|
309
|
-
var DEFAULT_PORT = 8795;
|
|
310
|
-
var createServer2 = async (options = {}) => {
|
|
311
|
-
const { port, host, ...rest } = options;
|
|
312
|
-
const runtime = createRuntime(rest);
|
|
313
|
-
const listening = await listen(runtime, {
|
|
314
|
-
port: port ?? 0,
|
|
315
|
-
...host !== void 0 ? { host } : {}
|
|
316
|
-
});
|
|
317
|
-
return { ...listening, runtime };
|
|
318
|
-
};
|
|
319
|
-
var text = (value) => typeof value === "string" ? value : void 0;
|
|
320
|
-
var serveTarget = {
|
|
321
|
-
name: "posthog",
|
|
322
|
-
defaultPort: DEFAULT_PORT,
|
|
323
|
-
options: {
|
|
324
|
-
"import-flags": {
|
|
325
|
-
type: "string",
|
|
326
|
-
value: "<dev|prod>",
|
|
327
|
-
description: "Seed every namespace from the bundled consumer-app docs/feature-flags/state.json (member-app project)"
|
|
328
|
-
},
|
|
329
|
-
"session-recording": {
|
|
330
|
-
type: "boolean",
|
|
331
|
-
description: "Advertise session recording ({endpoint: /s/}) in remote config"
|
|
332
|
-
}
|
|
333
|
-
},
|
|
334
|
-
create: (values, common) => {
|
|
335
|
-
const env = text(values["import-flags"]);
|
|
336
|
-
if (env !== void 0 && env !== "dev" && env !== "prod") {
|
|
337
|
-
throw new Error('--import-flags must be "dev" or "prod"');
|
|
338
|
-
}
|
|
339
|
-
return createRuntime({
|
|
340
|
-
...env ? { flags: specsFromState(ACME_FLAG_STATE, { env }) } : {},
|
|
341
|
-
...values["session-recording"] === true ? { settings: { sessionRecording: true } } : {},
|
|
342
|
-
...common.adminKey !== void 0 ? { adminKey: common.adminKey } : {},
|
|
343
|
-
...common.seed !== void 0 ? { seed: common.seed } : {},
|
|
344
|
-
...common.onLog ? { onLog: common.onLog } : {}
|
|
345
|
-
});
|
|
346
|
-
},
|
|
347
|
-
banner: () => [
|
|
348
|
-
"point POSTHOG_HOST / NEXT_PUBLIC_POSTHOG_HOST here; set flags with PUT /__admin/flags/<key>",
|
|
349
|
-
"namespaces: /ns/<name> host prefix, x-mockingbird-namespace, or PUT /__admin/credentials {<phc_token>: <ns>}"
|
|
350
|
-
]
|
|
351
|
-
};
|
|
352
|
-
|
|
353
|
-
export {
|
|
354
|
-
runCli,
|
|
355
|
-
serveCommand,
|
|
356
|
-
DEFAULT_PORT,
|
|
357
|
-
createServer2 as createServer,
|
|
358
|
-
serveTarget
|
|
359
|
-
};
|
|
360
|
-
//# sourceMappingURL=chunk-FWRNARH5.js.map
|