@crvouga/mockingbird-service-postgres 3.3.0 → 3.4.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.
@@ -10,7 +10,7 @@ type ClockState = {
10
10
  now: number;
11
11
  /** True while time does not advance on its own. */
12
12
  frozen: boolean;
13
- /** Milliseconds this clock adds to its underlying source. */
13
+ /** Milliseconds a running clock adds to its underlying source. `0` while frozen. */
14
14
  offsetMs: number;
15
15
  };
16
16
  type Clock = {
@@ -28,6 +28,213 @@ type Clock = {
28
28
  state(): ClockState;
29
29
  };
30
30
 
31
+ /**
32
+ * A deliberate failure injected in front of an operation.
33
+ *
34
+ * This is how a suite reaches the vendor's failure modes without the vendor: the
35
+ * quota error that only appears when a shared sandbox is full, the 429 that only
36
+ * appears under load, the 5xx that proves a retry path works.
37
+ *
38
+ * A rule has three parts. What it **matches** (`operationId`, `method`, `path`, …), **when**
39
+ * it fires among the matching requests (`after`, `every`, `count`, `rate`, `from`, `until`),
40
+ * and what it **does** (`status`, `delay`, `timeout`, `drop`, `truncate`, `effect`).
41
+ */
42
+ type FaultRule = {
43
+ /** Stable id, so a suite can retire exactly the rule it added. Unique within a namespace. */
44
+ id: string;
45
+ /** Fault only this operation. Omit to match every operation. */
46
+ operationId?: string;
47
+ /** Fault only this HTTP method, case-insensitive. Omit to match every method. */
48
+ method?: string;
49
+ /** Fault only this exact path. */
50
+ path?: string;
51
+ /** Fault only paths starting with this prefix. Omit to match every path. */
52
+ pathPrefix?: string;
53
+ /** Fault only paths matching this regular expression (JavaScript syntax, unanchored). */
54
+ pathPattern?: string;
55
+ /** Fault only requests carrying these headers. `"*"` matches any value. */
56
+ requestHeaders?: Record<string, string>;
57
+ /** Fault only requests carrying these query parameters. `"*"` matches any value. */
58
+ query?: Record<string, string>;
59
+ /**
60
+ * Fault only this namespace. Omit (or `"*"`) to fault every namespace — which is what
61
+ * an in-process caller usually wants, and what a parallel worker usually does not:
62
+ * rules added through `POST /__admin/faults` default to the calling namespace.
63
+ */
64
+ namespace?: string;
65
+ /** Let this many matching requests through first. */
66
+ after?: number;
67
+ /** Then fault only every nth matching request: `3` faults the 3rd, 6th, 9th. */
68
+ every?: number;
69
+ /** Retire the rule after this many faults. Omit to keep it until removed. */
70
+ count?: number;
71
+ /** Fault this fraction of the remaining requests, `0`–`1`, drawn from the seed. Default `1`. */
72
+ rate?: number;
73
+ /**
74
+ * Fault only from this instant on the namespace clock. A duration (`"5m"`) counts from the
75
+ * clock when the rule was added. Listed as epoch ms.
76
+ */
77
+ from?: Instant | Duration;
78
+ /** Stop faulting at this instant on the namespace clock. A duration counts from when added. */
79
+ until?: Instant | Duration;
80
+ /**
81
+ * Status of the injected response. Omit for a rule that only delays, times out, drops,
82
+ * truncates, or switches on an `effect`: the request then still reaches the service.
83
+ */
84
+ status?: number;
85
+ /** Response body, serialized as JSON. A string is sent as-is. */
86
+ body?: unknown;
87
+ /** Response headers. */
88
+ headers?: Record<string, string>;
89
+ /** Hold the response back this long. Listed as `delayMs`. */
90
+ delay?: Duration;
91
+ /** `delay`, in milliseconds. */
92
+ delayMs?: number;
93
+ /** Alias of `delayMs`. */
94
+ latencyMs?: number;
95
+ /** Extra seeded delay in `[0, jitter]`. Listed as `jitterMs`. */
96
+ jitter?: Duration;
97
+ jitterMs?: number;
98
+ /**
99
+ * Never answer: hold the request this long, then drop the connection. Listed as
100
+ * `timeoutMs`. With virtual time the wait is spent on the mock clock.
101
+ */
102
+ timeout?: Duration;
103
+ timeoutMs?: number;
104
+ /**
105
+ * Drop the connection instead of answering: an in-process `fetch` rejects with a
106
+ * `TypeError`, and a served mock destroys the socket. Models "unknown outcome" failures.
107
+ */
108
+ drop?: boolean;
109
+ /**
110
+ * Cut the response body off after this many bytes: the status and headers arrive, then
111
+ * the connection dies mid-body.
112
+ */
113
+ truncate?: number;
114
+ /**
115
+ * A named service behaviour to switch on for the matching request instead of (or
116
+ * before) a canned response, e.g. `created_but_500` or `numeric_tracking_id`. Services
117
+ * read it with `faultEffects(request)`.
118
+ */
119
+ effect?: string;
120
+ /** Parameters for `effect`. */
121
+ params?: Record<string, unknown>;
122
+ /** The preset this rule was expanded from, if any. */
123
+ fromPreset?: string;
124
+ };
125
+
126
+ /**
127
+ * The admin controls every mock shares, as one document.
128
+ *
129
+ * The same shape is accepted in three places, and means the same thing in each:
130
+ *
131
+ * - at boot: `createRuntime({ adminControls })`, `serve --admin-controls`,
132
+ * `MOCKINGBIRD_ADMIN_CONTROLS`
133
+ * - over HTTP: `PUT /__admin/controls`
134
+ * - per request: the `x-mockingbird-*` headers in {@link CONTROL_HEADERS}
135
+ *
136
+ * Every control is deterministic: given the same seed, clock and requests, a mock answers
137
+ * the same bytes. Nothing here reads the wall clock or `Math.random`.
138
+ */
139
+ /** Milliseconds, or a string like `"250ms"`, `"2s"`, `"15m"`, `"1h"`, `"3d"`. */
140
+ type Duration = number | string;
141
+ /** Epoch milliseconds, or an ISO-8601 string. */
142
+ type Instant = number | string;
143
+ type ClockControls = {
144
+ /** Move the clock to this instant. */
145
+ now?: Instant;
146
+ /** Stop time. A frozen clock moves only when told to, so timestamps replay exactly. */
147
+ frozen?: boolean;
148
+ /**
149
+ * Step the clock forward by this much before every vendor request. With a frozen clock
150
+ * each request still gets its own, reproducible timestamp.
151
+ */
152
+ autoAdvance?: Duration;
153
+ };
154
+ /** How long a response takes: a fixed delay plus seeded jitter in `[0, jitter]`. */
155
+ type LatencyRuleInput = {
156
+ delay?: Duration;
157
+ jitter?: Duration;
158
+ };
159
+ type LatencyControls = LatencyRuleInput & {
160
+ /** Per-operation overrides, keyed by operationId. A bare duration is its `delay`. */
161
+ operations?: Record<string, Duration | LatencyRuleInput>;
162
+ };
163
+ /** A fault to add: a rule (the id is assigned when omitted), or a named preset with overrides. */
164
+ type FaultInput = (Omit<FaultRule, "id"> & {
165
+ id?: string;
166
+ }) | ({
167
+ preset: string;
168
+ } & Partial<FaultRule>);
169
+ /** Identifies a {@link SnapshotDocument}. */
170
+ declare const SNAPSHOT_FORMAT = "mockingbird.snapshot/v1";
171
+ /**
172
+ * A namespace, as portable JSON: what `GET /__admin/snapshots/:id` returns and what
173
+ * `PUT /__admin/snapshots/:id` and the boot `snapshot` control accept. Only `format` and
174
+ * `records` are required, so a hand-written seed file is a valid snapshot.
175
+ */
176
+ type SnapshotDocument = {
177
+ format: typeof SNAPSHOT_FORMAT;
178
+ /** The service that wrote it. A snapshot is rejected by any other service. */
179
+ service?: string;
180
+ /**
181
+ * Stored rows. `seq` orders a collection; omitted, rows keep their listed order. `raw` is
182
+ * the stored text of a row that is not a plain record, and replaces `value` when present.
183
+ */
184
+ records: {
185
+ collection: string;
186
+ id: string;
187
+ seq?: number;
188
+ value?: unknown;
189
+ raw?: string;
190
+ }[];
191
+ /** Id counters, so the next generated id continues where the snapshot left off. */
192
+ sequences?: {
193
+ name: string;
194
+ kind: string;
195
+ value: number;
196
+ }[];
197
+ clock?: {
198
+ now: number;
199
+ frozen: boolean;
200
+ autoAdvanceMs?: number;
201
+ };
202
+ seed?: number | string;
203
+ /** Positions of the seeded random streams. */
204
+ rng?: {
205
+ state: number;
206
+ faults?: number;
207
+ latency?: number;
208
+ };
209
+ faults?: unknown;
210
+ latency?: unknown;
211
+ virtualTime?: boolean;
212
+ };
213
+ /** Controls for one namespace. */
214
+ type NamespaceControls = {
215
+ /** Seeds every random choice: fault rates, jitter, and whatever the service invents. */
216
+ seed?: number | string;
217
+ clock?: ClockControls;
218
+ /**
219
+ * Spend injected waits (latency, fault delays, timeouts) on the mock clock instead of the
220
+ * wall clock: the request returns at once, the clock has moved, and the journal records
221
+ * the wait exactly. Default `false`: waits are real, so a client's own timeout can fire.
222
+ */
223
+ virtualTime?: boolean;
224
+ /** Baseline response time for every vendor request. A bare duration is its `delay`. */
225
+ latency?: Duration | LatencyControls;
226
+ faults?: readonly FaultInput[];
227
+ /** Start from this state instead of the service's own seed data. */
228
+ snapshot?: SnapshotDocument;
229
+ };
230
+ /** {@link NamespaceControls} for every namespace, with per-namespace overrides. */
231
+ type MockControls = NamespaceControls & {
232
+ /** Vendor credential → namespace, for SDKs that cannot send a namespace header. */
233
+ credentials?: Record<string, string>;
234
+ /** Overrides for named namespaces. A key given here replaces the shared value. */
235
+ namespaces?: Record<string, NamespaceControls>;
236
+ };
237
+
31
238
  type FleetDiagnostics = {
32
239
  activeRequests: number;
33
240
  activeConnections: number;
@@ -66,6 +273,8 @@ type ConfigService = {
66
273
  adminPrefix?: string;
67
274
  adminKey?: string;
68
275
  seed?: string;
276
+ /** Admin controls this service boots with. A key given here replaces the fleet-wide one. */
277
+ adminControls?: MockControls;
69
278
  fixtures?: Record<string, unknown>;
70
279
  baseUrl?: string;
71
280
  password?: string;
package/package.json CHANGED
@@ -185,5 +185,5 @@
185
185
  ],
186
186
  "type": "module",
187
187
  "types": "./dist/index.d.ts",
188
- "version": "3.3.0"
188
+ "version": "3.4.0"
189
189
  }