@crvouga/mockingbird-service-junction 0.3.0 → 0.5.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 +17 -1
- package/README.md +1 -1
- package/dist/{chunk-KLBGX23P.js → chunk-CR35KYXY.js} +46 -4
- package/dist/{chunk-KLBGX23P.js.map → chunk-CR35KYXY.js.map} +2 -2
- package/dist/{chunk-NFWUEPW4.js → chunk-ICINEQ7D.js} +353 -71
- package/dist/chunk-ICINEQ7D.js.map +7 -0
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +131 -7
- package/dist/index.js +1 -1
- package/dist/server.d.ts +262 -14
- package/dist/server.js +2 -2
- package/package.json +6 -3
- package/dist/chunk-NFWUEPW4.js.map +0 -7
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
runCli,
|
|
5
5
|
serveCommand,
|
|
6
6
|
serveTarget
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-CR35KYXY.js";
|
|
8
8
|
import {
|
|
9
9
|
DEFAULT_JUNCTION_BASE_URL,
|
|
10
10
|
createRuntime,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
isSandboxKey,
|
|
13
13
|
pullCorpus,
|
|
14
14
|
verifyAgainstReal
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-ICINEQ7D.js";
|
|
16
16
|
import {
|
|
17
17
|
defaultCorpus
|
|
18
18
|
} from "./chunk-C2UGIPPM.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -98,7 +98,12 @@ type FaultRule = {
|
|
|
98
98
|
* rules added through `POST /__admin/faults` default to the calling namespace.
|
|
99
99
|
*/
|
|
100
100
|
namespace?: string;
|
|
101
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Status of the injected response. Omit for a rule that only delays (`delayMs` /
|
|
103
|
+
* `latencyMs`), only drops the connection (`drop`), or only switches on an `effect`:
|
|
104
|
+
* the request then still reaches the service.
|
|
105
|
+
*/
|
|
106
|
+
status?: number;
|
|
102
107
|
/** Response body, serialized as JSON. A string is sent as-is. */
|
|
103
108
|
body?: unknown;
|
|
104
109
|
headers?: Record<string, string>;
|
|
@@ -108,6 +113,34 @@ type FaultRule = {
|
|
|
108
113
|
rate?: number;
|
|
109
114
|
/** Hold the response back this long, to exercise timeouts. */
|
|
110
115
|
delayMs?: number;
|
|
116
|
+
/** Alias of `delayMs`. */
|
|
117
|
+
latencyMs?: number;
|
|
118
|
+
/**
|
|
119
|
+
* Drop the connection instead of answering: an in-process `fetch` rejects with a
|
|
120
|
+
* `TypeError`, and a served mock destroys the socket. Models "unknown outcome" failures.
|
|
121
|
+
*/
|
|
122
|
+
drop?: boolean;
|
|
123
|
+
/**
|
|
124
|
+
* A named service behaviour to switch on for the matching request instead of (or
|
|
125
|
+
* before) a canned response, e.g. `created_but_500` or `numeric_tracking_id`. Services
|
|
126
|
+
* read it with `faultEffects(request)`.
|
|
127
|
+
*/
|
|
128
|
+
effect?: string;
|
|
129
|
+
/** Parameters for `effect`. */
|
|
130
|
+
params?: Record<string, unknown>;
|
|
131
|
+
/** From the preset this rule was expanded from, if any. */
|
|
132
|
+
preset?: string;
|
|
133
|
+
};
|
|
134
|
+
/** A fault that fired for one request. */
|
|
135
|
+
type FaultHit = {
|
|
136
|
+
id: string;
|
|
137
|
+
/** The injected response; absent when the rule only delays, drops, or sets an effect. */
|
|
138
|
+
response?: Response;
|
|
139
|
+
drop?: boolean;
|
|
140
|
+
effect?: {
|
|
141
|
+
name: string;
|
|
142
|
+
params: Record<string, unknown>;
|
|
143
|
+
};
|
|
111
144
|
};
|
|
112
145
|
/** What a request looks like to the fault matcher. */
|
|
113
146
|
type FaultCandidate = {
|
|
@@ -125,13 +158,11 @@ type FaultRegistry = {
|
|
|
125
158
|
remove(id: string): boolean;
|
|
126
159
|
clear(): void;
|
|
127
160
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
161
|
+
* Every fault this request should get, in rule order, stopping at the first that answers
|
|
162
|
+
* or drops (effect-only and delay-only rules let later rules match too). Consumes one of
|
|
163
|
+
* each matching rule's remaining uses.
|
|
130
164
|
*/
|
|
131
|
-
take(candidate: FaultCandidate): Promise<
|
|
132
|
-
id: string;
|
|
133
|
-
response: Response;
|
|
134
|
-
} | undefined>;
|
|
165
|
+
take(candidate: FaultCandidate): Promise<FaultHit[]>;
|
|
135
166
|
};
|
|
136
167
|
|
|
137
168
|
/** One handled request, as the structured log sees it. */
|
|
@@ -220,6 +251,18 @@ type NamespaceSnapshot = {
|
|
|
220
251
|
}[];
|
|
221
252
|
};
|
|
222
253
|
|
|
254
|
+
/** Credential → namespace mapping behind `PUT /__admin/credentials`. */
|
|
255
|
+
type CredentialRegistry = {
|
|
256
|
+
set(credential: string, namespace: string): void;
|
|
257
|
+
get(credential: string): string | undefined;
|
|
258
|
+
remove(credential: string): boolean;
|
|
259
|
+
clear(): void;
|
|
260
|
+
entries(): {
|
|
261
|
+
credential: string;
|
|
262
|
+
namespace: string;
|
|
263
|
+
}[];
|
|
264
|
+
};
|
|
265
|
+
|
|
223
266
|
/**
|
|
224
267
|
* Anything that can answer a Fetch `Request` with a `Response`.
|
|
225
268
|
*
|
|
@@ -230,6 +273,82 @@ interface FetchAPI$1 {
|
|
|
230
273
|
fetch(request: Request): Promise<Response>;
|
|
231
274
|
}
|
|
232
275
|
|
|
276
|
+
type WebhookEndpoint$1 = {
|
|
277
|
+
/** Stable id; generated when omitted. */
|
|
278
|
+
id?: string;
|
|
279
|
+
url: string;
|
|
280
|
+
secret?: string;
|
|
281
|
+
/** Event types to deliver; omit or include `"*"` for every type. */
|
|
282
|
+
events?: string[];
|
|
283
|
+
/** Deliver only messages whose tags include all of these (e.g. `{ account: "mso" }`). */
|
|
284
|
+
tags?: Record<string, string>;
|
|
285
|
+
/** The public URL the receiver verifies signatures against (Twilio), when it differs. */
|
|
286
|
+
signUrl?: string;
|
|
287
|
+
headers?: Record<string, string>;
|
|
288
|
+
};
|
|
289
|
+
type WebhookMessage = {
|
|
290
|
+
id: string;
|
|
291
|
+
namespace: string;
|
|
292
|
+
type: string;
|
|
293
|
+
body: string;
|
|
294
|
+
contentType: string;
|
|
295
|
+
tags: Record<string, string>;
|
|
296
|
+
/** Wall-clock ISO-8601 time of publication. */
|
|
297
|
+
publishedAt: string;
|
|
298
|
+
};
|
|
299
|
+
type WebhookAttempt$1 = {
|
|
300
|
+
attempt: number;
|
|
301
|
+
at: string;
|
|
302
|
+
status: number | null;
|
|
303
|
+
error: string | null;
|
|
304
|
+
durationMs: number;
|
|
305
|
+
};
|
|
306
|
+
type WebhookDelivery$1 = {
|
|
307
|
+
id: string;
|
|
308
|
+
messageId: string;
|
|
309
|
+
namespace: string;
|
|
310
|
+
type: string;
|
|
311
|
+
endpointId: string;
|
|
312
|
+
url: string;
|
|
313
|
+
state: "pending" | "delivered" | "failed" | "dropped";
|
|
314
|
+
attempts: WebhookAttempt$1[];
|
|
315
|
+
};
|
|
316
|
+
/** A delivery-level fault: what happens to the next `count` messages in a namespace. */
|
|
317
|
+
type WebhookFault = {
|
|
318
|
+
mode: "duplicate" | "reorder" | "drop";
|
|
319
|
+
/** Messages affected; default 1. */
|
|
320
|
+
count?: number;
|
|
321
|
+
};
|
|
322
|
+
type PublishInput = {
|
|
323
|
+
namespace: string;
|
|
324
|
+
type: string;
|
|
325
|
+
/** Exact body; objects are JSON-encoded. */
|
|
326
|
+
body: string | Record<string, unknown> | unknown[];
|
|
327
|
+
/** Default `application/json`, or form-encoded when `form` is given. */
|
|
328
|
+
contentType?: string;
|
|
329
|
+
/** Form parameters, when the vendor posts `application/x-www-form-urlencoded`. */
|
|
330
|
+
form?: Record<string, string>;
|
|
331
|
+
tags?: Record<string, string>;
|
|
332
|
+
/** Message id; generated when omitted. */
|
|
333
|
+
id?: string;
|
|
334
|
+
};
|
|
335
|
+
type WebhookHub = {
|
|
336
|
+
publish(input: PublishInput): WebhookMessage;
|
|
337
|
+
/** Replace a namespace's own endpoints (`PUT /__admin/webhook-endpoints`). */
|
|
338
|
+
setEndpoints(namespace: string, endpoints: WebhookEndpoint$1[]): WebhookEndpoint$1[];
|
|
339
|
+
/** The endpoints a namespace delivers to: its own, plus the global ones. */
|
|
340
|
+
endpoints(namespace: string): WebhookEndpoint$1[];
|
|
341
|
+
messages(namespace?: string): WebhookMessage[];
|
|
342
|
+
deliveries(namespace?: string): WebhookDelivery$1[];
|
|
343
|
+
replay(deliveryId: string): Promise<WebhookDelivery$1 | undefined>;
|
|
344
|
+
/** Run every pending retry (and release held reordered messages) now. */
|
|
345
|
+
flush(): Promise<void>;
|
|
346
|
+
/** Resolve once nothing is in flight. */
|
|
347
|
+
idle(): Promise<void>;
|
|
348
|
+
fault(namespace: string, fault: WebhookFault): void;
|
|
349
|
+
clear(namespace?: string): void;
|
|
350
|
+
};
|
|
351
|
+
|
|
233
352
|
/** What the runtime needs from a service: a Fetch handler it can reset. */
|
|
234
353
|
type ServiceInstance = FetchAPI$1 & {
|
|
235
354
|
reset(): Promise<void>;
|
|
@@ -242,6 +361,11 @@ type ServiceRuntime<T extends ServiceInstance> = FetchAPI$1 & {
|
|
|
242
361
|
readonly metrics: Metrics;
|
|
243
362
|
readonly journal: Journal;
|
|
244
363
|
readonly rng: Rng;
|
|
364
|
+
readonly credentials: CredentialRegistry;
|
|
365
|
+
/** The webhook hub, when the service has outbound webhooks. */
|
|
366
|
+
readonly webhooks: WebhookHub | undefined;
|
|
367
|
+
/** Expand a named preset into fault rules (and webhook faults) for `namespace`. */
|
|
368
|
+
applyPreset(name: string, namespace?: string, overrides?: Partial<FaultRule>): FaultRule[];
|
|
245
369
|
/** The instance behind `namespace` (the default one when omitted), created on first use. */
|
|
246
370
|
instance(namespace?: string): T;
|
|
247
371
|
/** Public names of every namespace created so far. */
|
package/dist/index.js
CHANGED
package/dist/server.d.ts
CHANGED
|
@@ -99,7 +99,12 @@ type FaultRule$1 = {
|
|
|
99
99
|
* rules added through `POST /__admin/faults` default to the calling namespace.
|
|
100
100
|
*/
|
|
101
101
|
namespace?: string;
|
|
102
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Status of the injected response. Omit for a rule that only delays (`delayMs` /
|
|
104
|
+
* `latencyMs`), only drops the connection (`drop`), or only switches on an `effect`:
|
|
105
|
+
* the request then still reaches the service.
|
|
106
|
+
*/
|
|
107
|
+
status?: number;
|
|
103
108
|
/** Response body, serialized as JSON. A string is sent as-is. */
|
|
104
109
|
body?: unknown;
|
|
105
110
|
headers?: Record<string, string>;
|
|
@@ -109,6 +114,34 @@ type FaultRule$1 = {
|
|
|
109
114
|
rate?: number;
|
|
110
115
|
/** Hold the response back this long, to exercise timeouts. */
|
|
111
116
|
delayMs?: number;
|
|
117
|
+
/** Alias of `delayMs`. */
|
|
118
|
+
latencyMs?: number;
|
|
119
|
+
/**
|
|
120
|
+
* Drop the connection instead of answering: an in-process `fetch` rejects with a
|
|
121
|
+
* `TypeError`, and a served mock destroys the socket. Models "unknown outcome" failures.
|
|
122
|
+
*/
|
|
123
|
+
drop?: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* A named service behaviour to switch on for the matching request instead of (or
|
|
126
|
+
* before) a canned response, e.g. `created_but_500` or `numeric_tracking_id`. Services
|
|
127
|
+
* read it with `faultEffects(request)`.
|
|
128
|
+
*/
|
|
129
|
+
effect?: string;
|
|
130
|
+
/** Parameters for `effect`. */
|
|
131
|
+
params?: Record<string, unknown>;
|
|
132
|
+
/** From the preset this rule was expanded from, if any. */
|
|
133
|
+
preset?: string;
|
|
134
|
+
};
|
|
135
|
+
/** A fault that fired for one request. */
|
|
136
|
+
type FaultHit$1 = {
|
|
137
|
+
id: string;
|
|
138
|
+
/** The injected response; absent when the rule only delays, drops, or sets an effect. */
|
|
139
|
+
response?: Response;
|
|
140
|
+
drop?: boolean;
|
|
141
|
+
effect?: {
|
|
142
|
+
name: string;
|
|
143
|
+
params: Record<string, unknown>;
|
|
144
|
+
};
|
|
112
145
|
};
|
|
113
146
|
/** What a request looks like to the fault matcher. */
|
|
114
147
|
type FaultCandidate$1 = {
|
|
@@ -126,13 +159,11 @@ type FaultRegistry$1 = {
|
|
|
126
159
|
remove(id: string): boolean;
|
|
127
160
|
clear(): void;
|
|
128
161
|
/**
|
|
129
|
-
*
|
|
130
|
-
*
|
|
162
|
+
* Every fault this request should get, in rule order, stopping at the first that answers
|
|
163
|
+
* or drops (effect-only and delay-only rules let later rules match too). Consumes one of
|
|
164
|
+
* each matching rule's remaining uses.
|
|
131
165
|
*/
|
|
132
|
-
take(candidate: FaultCandidate$1): Promise<
|
|
133
|
-
id: string;
|
|
134
|
-
response: Response;
|
|
135
|
-
} | undefined>;
|
|
166
|
+
take(candidate: FaultCandidate$1): Promise<FaultHit$1[]>;
|
|
136
167
|
};
|
|
137
168
|
|
|
138
169
|
/** One handled request, as the structured log sees it. */
|
|
@@ -221,6 +252,18 @@ type NamespaceSnapshot$1 = {
|
|
|
221
252
|
}[];
|
|
222
253
|
};
|
|
223
254
|
|
|
255
|
+
/** Credential → namespace mapping behind `PUT /__admin/credentials`. */
|
|
256
|
+
type CredentialRegistry$1 = {
|
|
257
|
+
set(credential: string, namespace: string): void;
|
|
258
|
+
get(credential: string): string | undefined;
|
|
259
|
+
remove(credential: string): boolean;
|
|
260
|
+
clear(): void;
|
|
261
|
+
entries(): {
|
|
262
|
+
credential: string;
|
|
263
|
+
namespace: string;
|
|
264
|
+
}[];
|
|
265
|
+
};
|
|
266
|
+
|
|
224
267
|
/**
|
|
225
268
|
* Anything that can answer a Fetch `Request` with a `Response`.
|
|
226
269
|
*
|
|
@@ -231,6 +274,82 @@ interface FetchAPI$2 {
|
|
|
231
274
|
fetch(request: Request): Promise<Response>;
|
|
232
275
|
}
|
|
233
276
|
|
|
277
|
+
type WebhookEndpoint$2 = {
|
|
278
|
+
/** Stable id; generated when omitted. */
|
|
279
|
+
id?: string;
|
|
280
|
+
url: string;
|
|
281
|
+
secret?: string;
|
|
282
|
+
/** Event types to deliver; omit or include `"*"` for every type. */
|
|
283
|
+
events?: string[];
|
|
284
|
+
/** Deliver only messages whose tags include all of these (e.g. `{ account: "mso" }`). */
|
|
285
|
+
tags?: Record<string, string>;
|
|
286
|
+
/** The public URL the receiver verifies signatures against (Twilio), when it differs. */
|
|
287
|
+
signUrl?: string;
|
|
288
|
+
headers?: Record<string, string>;
|
|
289
|
+
};
|
|
290
|
+
type WebhookMessage$1 = {
|
|
291
|
+
id: string;
|
|
292
|
+
namespace: string;
|
|
293
|
+
type: string;
|
|
294
|
+
body: string;
|
|
295
|
+
contentType: string;
|
|
296
|
+
tags: Record<string, string>;
|
|
297
|
+
/** Wall-clock ISO-8601 time of publication. */
|
|
298
|
+
publishedAt: string;
|
|
299
|
+
};
|
|
300
|
+
type WebhookAttempt$2 = {
|
|
301
|
+
attempt: number;
|
|
302
|
+
at: string;
|
|
303
|
+
status: number | null;
|
|
304
|
+
error: string | null;
|
|
305
|
+
durationMs: number;
|
|
306
|
+
};
|
|
307
|
+
type WebhookDelivery$2 = {
|
|
308
|
+
id: string;
|
|
309
|
+
messageId: string;
|
|
310
|
+
namespace: string;
|
|
311
|
+
type: string;
|
|
312
|
+
endpointId: string;
|
|
313
|
+
url: string;
|
|
314
|
+
state: "pending" | "delivered" | "failed" | "dropped";
|
|
315
|
+
attempts: WebhookAttempt$2[];
|
|
316
|
+
};
|
|
317
|
+
/** A delivery-level fault: what happens to the next `count` messages in a namespace. */
|
|
318
|
+
type WebhookFault$1 = {
|
|
319
|
+
mode: "duplicate" | "reorder" | "drop";
|
|
320
|
+
/** Messages affected; default 1. */
|
|
321
|
+
count?: number;
|
|
322
|
+
};
|
|
323
|
+
type PublishInput$1 = {
|
|
324
|
+
namespace: string;
|
|
325
|
+
type: string;
|
|
326
|
+
/** Exact body; objects are JSON-encoded. */
|
|
327
|
+
body: string | Record<string, unknown> | unknown[];
|
|
328
|
+
/** Default `application/json`, or form-encoded when `form` is given. */
|
|
329
|
+
contentType?: string;
|
|
330
|
+
/** Form parameters, when the vendor posts `application/x-www-form-urlencoded`. */
|
|
331
|
+
form?: Record<string, string>;
|
|
332
|
+
tags?: Record<string, string>;
|
|
333
|
+
/** Message id; generated when omitted. */
|
|
334
|
+
id?: string;
|
|
335
|
+
};
|
|
336
|
+
type WebhookHub$1 = {
|
|
337
|
+
publish(input: PublishInput$1): WebhookMessage$1;
|
|
338
|
+
/** Replace a namespace's own endpoints (`PUT /__admin/webhook-endpoints`). */
|
|
339
|
+
setEndpoints(namespace: string, endpoints: WebhookEndpoint$2[]): WebhookEndpoint$2[];
|
|
340
|
+
/** The endpoints a namespace delivers to: its own, plus the global ones. */
|
|
341
|
+
endpoints(namespace: string): WebhookEndpoint$2[];
|
|
342
|
+
messages(namespace?: string): WebhookMessage$1[];
|
|
343
|
+
deliveries(namespace?: string): WebhookDelivery$2[];
|
|
344
|
+
replay(deliveryId: string): Promise<WebhookDelivery$2 | undefined>;
|
|
345
|
+
/** Run every pending retry (and release held reordered messages) now. */
|
|
346
|
+
flush(): Promise<void>;
|
|
347
|
+
/** Resolve once nothing is in flight. */
|
|
348
|
+
idle(): Promise<void>;
|
|
349
|
+
fault(namespace: string, fault: WebhookFault$1): void;
|
|
350
|
+
clear(namespace?: string): void;
|
|
351
|
+
};
|
|
352
|
+
|
|
234
353
|
/** What the runtime needs from a service: a Fetch handler it can reset. */
|
|
235
354
|
type ServiceInstance$1 = FetchAPI$2 & {
|
|
236
355
|
reset(): Promise<void>;
|
|
@@ -243,6 +362,11 @@ type ServiceRuntime$1<T extends ServiceInstance$1> = FetchAPI$2 & {
|
|
|
243
362
|
readonly metrics: Metrics$1;
|
|
244
363
|
readonly journal: Journal$1;
|
|
245
364
|
readonly rng: Rng$1;
|
|
365
|
+
readonly credentials: CredentialRegistry$1;
|
|
366
|
+
/** The webhook hub, when the service has outbound webhooks. */
|
|
367
|
+
readonly webhooks: WebhookHub$1 | undefined;
|
|
368
|
+
/** Expand a named preset into fault rules (and webhook faults) for `namespace`. */
|
|
369
|
+
applyPreset(name: string, namespace?: string, overrides?: Partial<FaultRule$1>): FaultRule$1[];
|
|
246
370
|
/** The instance behind `namespace` (the default one when omitted), created on first use. */
|
|
247
371
|
instance(namespace?: string): T;
|
|
248
372
|
/** Public names of every namespace created so far. */
|
|
@@ -388,7 +512,12 @@ type FaultRule = {
|
|
|
388
512
|
* rules added through `POST /__admin/faults` default to the calling namespace.
|
|
389
513
|
*/
|
|
390
514
|
namespace?: string;
|
|
391
|
-
|
|
515
|
+
/**
|
|
516
|
+
* Status of the injected response. Omit for a rule that only delays (`delayMs` /
|
|
517
|
+
* `latencyMs`), only drops the connection (`drop`), or only switches on an `effect`:
|
|
518
|
+
* the request then still reaches the service.
|
|
519
|
+
*/
|
|
520
|
+
status?: number;
|
|
392
521
|
/** Response body, serialized as JSON. A string is sent as-is. */
|
|
393
522
|
body?: unknown;
|
|
394
523
|
headers?: Record<string, string>;
|
|
@@ -398,6 +527,34 @@ type FaultRule = {
|
|
|
398
527
|
rate?: number;
|
|
399
528
|
/** Hold the response back this long, to exercise timeouts. */
|
|
400
529
|
delayMs?: number;
|
|
530
|
+
/** Alias of `delayMs`. */
|
|
531
|
+
latencyMs?: number;
|
|
532
|
+
/**
|
|
533
|
+
* Drop the connection instead of answering: an in-process `fetch` rejects with a
|
|
534
|
+
* `TypeError`, and a served mock destroys the socket. Models "unknown outcome" failures.
|
|
535
|
+
*/
|
|
536
|
+
drop?: boolean;
|
|
537
|
+
/**
|
|
538
|
+
* A named service behaviour to switch on for the matching request instead of (or
|
|
539
|
+
* before) a canned response, e.g. `created_but_500` or `numeric_tracking_id`. Services
|
|
540
|
+
* read it with `faultEffects(request)`.
|
|
541
|
+
*/
|
|
542
|
+
effect?: string;
|
|
543
|
+
/** Parameters for `effect`. */
|
|
544
|
+
params?: Record<string, unknown>;
|
|
545
|
+
/** From the preset this rule was expanded from, if any. */
|
|
546
|
+
preset?: string;
|
|
547
|
+
};
|
|
548
|
+
/** A fault that fired for one request. */
|
|
549
|
+
type FaultHit = {
|
|
550
|
+
id: string;
|
|
551
|
+
/** The injected response; absent when the rule only delays, drops, or sets an effect. */
|
|
552
|
+
response?: Response;
|
|
553
|
+
drop?: boolean;
|
|
554
|
+
effect?: {
|
|
555
|
+
name: string;
|
|
556
|
+
params: Record<string, unknown>;
|
|
557
|
+
};
|
|
401
558
|
};
|
|
402
559
|
/** What a request looks like to the fault matcher. */
|
|
403
560
|
type FaultCandidate = {
|
|
@@ -415,13 +572,11 @@ type FaultRegistry = {
|
|
|
415
572
|
remove(id: string): boolean;
|
|
416
573
|
clear(): void;
|
|
417
574
|
/**
|
|
418
|
-
*
|
|
419
|
-
*
|
|
575
|
+
* Every fault this request should get, in rule order, stopping at the first that answers
|
|
576
|
+
* or drops (effect-only and delay-only rules let later rules match too). Consumes one of
|
|
577
|
+
* each matching rule's remaining uses.
|
|
420
578
|
*/
|
|
421
|
-
take(candidate: FaultCandidate): Promise<
|
|
422
|
-
id: string;
|
|
423
|
-
response: Response;
|
|
424
|
-
} | undefined>;
|
|
579
|
+
take(candidate: FaultCandidate): Promise<FaultHit[]>;
|
|
425
580
|
};
|
|
426
581
|
|
|
427
582
|
/** One handled request, as the structured log sees it. */
|
|
@@ -510,6 +665,18 @@ type NamespaceSnapshot = {
|
|
|
510
665
|
}[];
|
|
511
666
|
};
|
|
512
667
|
|
|
668
|
+
/** Credential → namespace mapping behind `PUT /__admin/credentials`. */
|
|
669
|
+
type CredentialRegistry = {
|
|
670
|
+
set(credential: string, namespace: string): void;
|
|
671
|
+
get(credential: string): string | undefined;
|
|
672
|
+
remove(credential: string): boolean;
|
|
673
|
+
clear(): void;
|
|
674
|
+
entries(): {
|
|
675
|
+
credential: string;
|
|
676
|
+
namespace: string;
|
|
677
|
+
}[];
|
|
678
|
+
};
|
|
679
|
+
|
|
513
680
|
/**
|
|
514
681
|
* Anything that can answer a Fetch `Request` with a `Response`.
|
|
515
682
|
*
|
|
@@ -520,6 +687,82 @@ interface FetchAPI$1 {
|
|
|
520
687
|
fetch(request: Request): Promise<Response>;
|
|
521
688
|
}
|
|
522
689
|
|
|
690
|
+
type WebhookEndpoint$1 = {
|
|
691
|
+
/** Stable id; generated when omitted. */
|
|
692
|
+
id?: string;
|
|
693
|
+
url: string;
|
|
694
|
+
secret?: string;
|
|
695
|
+
/** Event types to deliver; omit or include `"*"` for every type. */
|
|
696
|
+
events?: string[];
|
|
697
|
+
/** Deliver only messages whose tags include all of these (e.g. `{ account: "mso" }`). */
|
|
698
|
+
tags?: Record<string, string>;
|
|
699
|
+
/** The public URL the receiver verifies signatures against (Twilio), when it differs. */
|
|
700
|
+
signUrl?: string;
|
|
701
|
+
headers?: Record<string, string>;
|
|
702
|
+
};
|
|
703
|
+
type WebhookMessage = {
|
|
704
|
+
id: string;
|
|
705
|
+
namespace: string;
|
|
706
|
+
type: string;
|
|
707
|
+
body: string;
|
|
708
|
+
contentType: string;
|
|
709
|
+
tags: Record<string, string>;
|
|
710
|
+
/** Wall-clock ISO-8601 time of publication. */
|
|
711
|
+
publishedAt: string;
|
|
712
|
+
};
|
|
713
|
+
type WebhookAttempt$1 = {
|
|
714
|
+
attempt: number;
|
|
715
|
+
at: string;
|
|
716
|
+
status: number | null;
|
|
717
|
+
error: string | null;
|
|
718
|
+
durationMs: number;
|
|
719
|
+
};
|
|
720
|
+
type WebhookDelivery$1 = {
|
|
721
|
+
id: string;
|
|
722
|
+
messageId: string;
|
|
723
|
+
namespace: string;
|
|
724
|
+
type: string;
|
|
725
|
+
endpointId: string;
|
|
726
|
+
url: string;
|
|
727
|
+
state: "pending" | "delivered" | "failed" | "dropped";
|
|
728
|
+
attempts: WebhookAttempt$1[];
|
|
729
|
+
};
|
|
730
|
+
/** A delivery-level fault: what happens to the next `count` messages in a namespace. */
|
|
731
|
+
type WebhookFault = {
|
|
732
|
+
mode: "duplicate" | "reorder" | "drop";
|
|
733
|
+
/** Messages affected; default 1. */
|
|
734
|
+
count?: number;
|
|
735
|
+
};
|
|
736
|
+
type PublishInput = {
|
|
737
|
+
namespace: string;
|
|
738
|
+
type: string;
|
|
739
|
+
/** Exact body; objects are JSON-encoded. */
|
|
740
|
+
body: string | Record<string, unknown> | unknown[];
|
|
741
|
+
/** Default `application/json`, or form-encoded when `form` is given. */
|
|
742
|
+
contentType?: string;
|
|
743
|
+
/** Form parameters, when the vendor posts `application/x-www-form-urlencoded`. */
|
|
744
|
+
form?: Record<string, string>;
|
|
745
|
+
tags?: Record<string, string>;
|
|
746
|
+
/** Message id; generated when omitted. */
|
|
747
|
+
id?: string;
|
|
748
|
+
};
|
|
749
|
+
type WebhookHub = {
|
|
750
|
+
publish(input: PublishInput): WebhookMessage;
|
|
751
|
+
/** Replace a namespace's own endpoints (`PUT /__admin/webhook-endpoints`). */
|
|
752
|
+
setEndpoints(namespace: string, endpoints: WebhookEndpoint$1[]): WebhookEndpoint$1[];
|
|
753
|
+
/** The endpoints a namespace delivers to: its own, plus the global ones. */
|
|
754
|
+
endpoints(namespace: string): WebhookEndpoint$1[];
|
|
755
|
+
messages(namespace?: string): WebhookMessage[];
|
|
756
|
+
deliveries(namespace?: string): WebhookDelivery$1[];
|
|
757
|
+
replay(deliveryId: string): Promise<WebhookDelivery$1 | undefined>;
|
|
758
|
+
/** Run every pending retry (and release held reordered messages) now. */
|
|
759
|
+
flush(): Promise<void>;
|
|
760
|
+
/** Resolve once nothing is in flight. */
|
|
761
|
+
idle(): Promise<void>;
|
|
762
|
+
fault(namespace: string, fault: WebhookFault): void;
|
|
763
|
+
clear(namespace?: string): void;
|
|
764
|
+
};
|
|
765
|
+
|
|
523
766
|
/** What the runtime needs from a service: a Fetch handler it can reset. */
|
|
524
767
|
type ServiceInstance = FetchAPI$1 & {
|
|
525
768
|
reset(): Promise<void>;
|
|
@@ -532,6 +775,11 @@ type ServiceRuntime<T extends ServiceInstance> = FetchAPI$1 & {
|
|
|
532
775
|
readonly metrics: Metrics;
|
|
533
776
|
readonly journal: Journal;
|
|
534
777
|
readonly rng: Rng;
|
|
778
|
+
readonly credentials: CredentialRegistry;
|
|
779
|
+
/** The webhook hub, when the service has outbound webhooks. */
|
|
780
|
+
readonly webhooks: WebhookHub | undefined;
|
|
781
|
+
/** Expand a named preset into fault rules (and webhook faults) for `namespace`. */
|
|
782
|
+
applyPreset(name: string, namespace?: string, overrides?: Partial<FaultRule>): FaultRule[];
|
|
535
783
|
/** The instance behind `namespace` (the default one when omitted), created on first use. */
|
|
536
784
|
instance(namespace?: string): T;
|
|
537
785
|
/** Public names of every namespace created so far. */
|
package/dist/server.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crvouga/mockingbird-service-junction",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Stateful mock of the Junction (Vital) user API driven by its OpenAPI contract and verified by differential property tests against the Junction sandbox.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -51,7 +51,10 @@
|
|
|
51
51
|
"server": "node",
|
|
52
52
|
"cli": "node"
|
|
53
53
|
},
|
|
54
|
-
"layer": "service"
|
|
54
|
+
"layer": "service",
|
|
55
|
+
"category": "health",
|
|
56
|
+
"displayName": "Junction",
|
|
57
|
+
"status": "ready"
|
|
55
58
|
},
|
|
56
59
|
"scripts": {
|
|
57
60
|
"generate": "bun ../../openapi/codegen/dist/cli.js",
|
|
@@ -75,7 +78,7 @@
|
|
|
75
78
|
"verify": "bun src/cli.ts verify"
|
|
76
79
|
},
|
|
77
80
|
"dependencies": {
|
|
78
|
-
"@crvouga/mockingbird-service-sqlite": "0.
|
|
81
|
+
"@crvouga/mockingbird-service-sqlite": "0.2.0",
|
|
79
82
|
"hono": "4.11.9"
|
|
80
83
|
},
|
|
81
84
|
"devDependencies": {
|