@shivaedev/test-story 0.13.0 → 0.15.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/README.md +28 -3
- package/dist/server/fakeHttpService.d.ts +16 -4
- package/dist/server/fakeHttpService.d.ts.map +1 -1
- package/dist/server/fakeHttpService.js +5 -4
- package/dist/server/fakeHttpService.js.map +1 -1
- package/dist/server/internal/fakeHttpAnswer.d.ts +5 -0
- package/dist/server/internal/fakeHttpAnswer.d.ts.map +1 -0
- package/dist/server/internal/fakeHttpAnswer.js +37 -0
- package/dist/server/internal/fakeHttpAnswer.js.map +1 -0
- package/dist/server/internal/fakeHttpControls.d.ts +8 -7
- package/dist/server/internal/fakeHttpControls.d.ts.map +1 -1
- package/dist/server/internal/fakeHttpControls.js +20 -16
- package/dist/server/internal/fakeHttpControls.js.map +1 -1
- package/dist/server/internal/fakeHttpGates.d.ts +5 -3
- package/dist/server/internal/fakeHttpGates.d.ts.map +1 -1
- package/dist/server/internal/fakeHttpGates.js +13 -9
- package/dist/server/internal/fakeHttpGates.js.map +1 -1
- package/dist/server/internal/fakeHttpReceive.d.ts.map +1 -1
- package/dist/server/internal/fakeHttpReceive.js +3 -18
- package/dist/server/internal/fakeHttpReceive.js.map +1 -1
- package/dist/server/internal/fakeHttpScripts.d.ts +7 -0
- package/dist/server/internal/fakeHttpScripts.d.ts.map +1 -0
- package/dist/server/internal/fakeHttpScripts.js +35 -0
- package/dist/server/internal/fakeHttpScripts.js.map +1 -0
- package/dist/server/internal/fakeHttpSelection.d.ts +6 -0
- package/dist/server/internal/fakeHttpSelection.d.ts.map +1 -0
- package/dist/server/internal/fakeHttpSelection.js +9 -0
- package/dist/server/internal/fakeHttpSelection.js.map +1 -0
- package/dist/server/internal/fakeHttpSend.d.ts +4 -1
- package/dist/server/internal/fakeHttpSend.d.ts.map +1 -1
- package/dist/server/internal/fakeHttpSend.js +8 -0
- package/dist/server/internal/fakeHttpSend.js.map +1 -1
- package/dist/server/internal/screenAccessibility.d.ts +9 -0
- package/dist/server/internal/screenAccessibility.d.ts.map +1 -0
- package/dist/server/internal/screenAccessibility.js +44 -0
- package/dist/server/internal/screenAccessibility.js.map +1 -0
- package/dist/server/internal/screenElements.d.ts +6 -5
- package/dist/server/internal/screenElements.d.ts.map +1 -1
- package/dist/server/internal/screenElements.js +9 -11
- package/dist/server/internal/screenElements.js.map +1 -1
- package/dist/server/screenDriver.d.ts +10 -6
- package/dist/server/screenDriver.d.ts.map +1 -1
- package/dist/server/screenDriver.js +10 -7
- package/dist/server/screenDriver.js.map +1 -1
- package/package.json +2 -2
- package/src/server/fakeHttpService.ts +23 -4
- package/src/server/internal/fakeHttpAnswer.ts +48 -0
- package/src/server/internal/fakeHttpControls.ts +23 -19
- package/src/server/internal/fakeHttpGates.ts +16 -11
- package/src/server/internal/fakeHttpReceive.ts +4 -20
- package/src/server/internal/fakeHttpScripts.ts +40 -0
- package/src/server/internal/fakeHttpSelection.ts +10 -0
- package/src/server/internal/fakeHttpSend.ts +10 -1
- package/src/server/internal/screenAccessibility.ts +50 -0
- package/src/server/internal/screenElements.ts +18 -16
- package/src/server/screenDriver.ts +16 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.15.0 - 2026-10-19
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Scope `fakeHttpService` stalls, immediate faults and one-shot replies by decoded path parameters with `FakeHttpScope`.
|
|
8
|
+
- Script future status, drop and reset faults with `scriptFault` and `FakeHttpScriptOptions`, optionally repeat until the returned `FakeHttpScript.lift` control or strike after the route's reply applies its effects.
|
|
9
|
+
- Declare per-route guards alongside the service guard; rejected requests receive 401 before holds, arrival scripts or faults are consumed.
|
|
10
|
+
|
|
11
|
+
## 0.14.0 - 2026-10-19
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `screenDriver` actions and text/value readouts accept `ScreenTarget` selectors or `{ role, name }` targets, with exact string or regular-expression accessible names, hidden-element exclusion, candidate diagnostics and role/name narration.
|
|
16
|
+
- `screenDriver.accessible(element)` computes an existing element's accessible name and role without narration; `accessible(selector)` keeps its narrated readout.
|
|
17
|
+
|
|
3
18
|
## 0.13.0 - 2026-10-19
|
|
4
19
|
|
|
5
20
|
### Added
|
package/README.md
CHANGED
|
@@ -288,7 +288,7 @@ const api = yield* fakeHttpService({
|
|
|
288
288
|
});
|
|
289
289
|
```
|
|
290
290
|
|
|
291
|
-
Point the application's real HTTP client at `api.url`, or use the kit's `outbound` map to route its external hostname there. A service-wide `guard` returns a boolean; a missing or wrong credential gets 401 before holds or scripted replies are consumed. Omit the guard for a public fake. Unmatched paths and methods get 404.
|
|
291
|
+
Point the application's real HTTP client at `api.url`, or use the kit's `outbound` map to route its external hostname there. A service-wide `guard` returns a boolean; a missing or wrong credential gets 401 before holds or scripted replies are consumed. Each route can also declare a `guard(request)`; both guards must accept the request before holds, arrival scripts or faults run. A route's guard applies only to that route, so an installation token check can sit beside public routes. Omit the guard for a public fake. Unmatched paths and methods get 404.
|
|
292
292
|
|
|
293
293
|
In `verbs`, bind `api.narrate(story.tell)` to tell received requests, replies, rejections and hold controls through the story. An optional `tell` callback can also be supplied at acquisition. The service keeps request headers and bytes in its log; narration of normal requests includes only method, path and status.
|
|
294
294
|
|
|
@@ -322,6 +322,27 @@ const bytes = yield* Fiber.join(pending);
|
|
|
322
322
|
|
|
323
323
|
`fault(route, fault)` acts on an arrived request in the required phase. `{ kind: "status", reply: { status: 503, body: { error: "unavailable" } } }` replaces a held reply before headers. `{ kind: "drop" }` drops a connection before headers. `{ kind: "reset" }` terminates a reply after headers, including after body chunks have arrived. Wait for the hold, stall or client observation before injecting the fault. A fault fails with guidance when no request is in the required phase; it does not queue a failure for a future call. `failOnce` continues to queue a status reply for the next request.
|
|
324
324
|
|
|
325
|
+
`stall(route, scope)`, `fault(route, fault, scope)`, `failOnce(route, reply, scope)` and `replyOnce(route, reply, scope)` accept an optional `{ params: { number: "7" } }`. Every supplied parameter must equal the decoded request parameter. Other resources on the same named route answer normally and do not consume the scoped reply. A scoped stall still returns `waiting` and `release`.
|
|
326
|
+
|
|
327
|
+
Use `scriptFault(route, fault, options)` to install a fault for future authorized matching requests. It returns a control with a narrated `lift` effect. The default strikes once before the route's reply runs; `{ repeat: true }` keeps it installed until lifted. Status faults can repeat too.
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
const outage = yield* api.scriptFault("pull", { kind: "drop" }, {
|
|
331
|
+
params: { number: "7" },
|
|
332
|
+
repeat: true,
|
|
333
|
+
});
|
|
334
|
+
yield* application.loadPull("7");
|
|
335
|
+
yield* outage.lift;
|
|
336
|
+
|
|
337
|
+
yield* api.scriptFault("comment", {
|
|
338
|
+
kind: "status",
|
|
339
|
+
reply: { status: 503, body: { error: "try later" } },
|
|
340
|
+
}, { params: { number: "7" }, phase: "after" });
|
|
341
|
+
yield* application.postComment("7", "Ready for review");
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
With `phase: "after"`, the route's reply function runs and applies its effects before the fault strikes. A status fault replaces the reply, a drop closes before headers, and a reset sends the route's status and headers then closes its unfinished body. The comment therefore exists even though the client sees a failure. This lets an application story check retry behavior after a side effect. The default `phase: "before"` skips the reply function when the fault strikes.
|
|
345
|
+
|
|
325
346
|
All controls validate route names and narrate through the service's story sink. Closing the scope closes active connections and releases holds, stalls and arrival scripts.
|
|
326
347
|
|
|
327
348
|
A `pages` route accepts a one-based `?page=` query and returns `{ items, next }`, with `next: null` on the last page. No query means page 1; a page past the end returns an empty list; a non-positive or non-integer page gets 400. `api.requests` reads a snapshot of the request log, including rejected and held requests, with `method`, `path` (including query), raw `body` Buffer, `headers`, `params` and `query`.
|
|
@@ -581,8 +602,12 @@ verbs: ({ view, requests }, story) => screenDriver({
|
|
|
581
602
|
|
|
582
603
|
`text(selector?)` returns rendered text from the selected element or the whole root. `value(selector)` returns the current input or textarea value. Both narrate what they read through `tell` and leave assertions to the spec. Entry narration omits the supplied value.
|
|
583
604
|
|
|
605
|
+
`click`, `enter`, `submit`, `text` and `value` also accept a `ScreenTarget` object such as `{ role: "button", name: "Save" }` or `{ role: "textbox", name: /email/i }`. The role and computed accessible name must match exactly when strings are used; regular expressions match the name. Like Testing Library's role queries, these targets exclude hidden elements, including `aria-hidden` ancestors. Missing or ambiguous targets fail with the requested role, name and the visible candidates on screen. Narration names the role and name instead of a selector.
|
|
606
|
+
|
|
584
607
|
`accessible(selector)` returns `{ name, role }` computed by `dom-accessibility-api`, including native roles and label references that take precedence over `aria-label`. It narrates the readout and uses the same single rendered target checks as other readouts. Assert on this result when checking a control's accessible name; reading its raw attributes does not compute that name. This DOM computation does not inspect a browser's accessibility tree or prove speech from a screen reader.
|
|
585
608
|
|
|
609
|
+
`accessible(element)` uses the same computation on an `Element` the kit already holds and tells no story line.
|
|
610
|
+
|
|
586
611
|
For React, pass `act: async (action) => { await act(action); }` using React's `act`. The adapter wraps each action and flushes renderer work before checking the pending signal, including while waiting for an asynchronous response. The [React driver stories](https://github.com/ShivaeDev/platform/blob/main/packages/effect-react/src/screenDriver.dom.spec.tsx) cover a native pending atom, a rendered response and a controlled field without React warnings. The driver itself has no renderer dependency.
|
|
587
612
|
|
|
588
613
|
Settlement has a live-time limit of 3 seconds, configurable with the driver's `timeoutMs` or per call with `settle({ timeoutMs })`, and fails with the current screen text and advice when that limit expires. Use the existing DOM environment and scoped `routeOutbound` routing to reach `fakeHttpService`; the driver does not install another transport. The [screen stories](src/server/screenSignals.dom.spec.ts) hold a real HTTP response across repeated renders and sign in by filling and submitting a form against a fake identity provider. These fixtures prove DOM interactions and loopback HTTP, not a browser or a production authentication system.
|
|
@@ -597,11 +622,11 @@ Import each module with the prefix `@shivaedev/test-story/`; there is no root en
|
|
|
597
622
|
| `domFetch.ts` | `domFetch(baseUrl)` |
|
|
598
623
|
| `seedCookies.ts` | `seedCookies(cookies)`, `DomCookie` |
|
|
599
624
|
| `routeOutbound.ts` | `routeOutbound(routes)` |
|
|
600
|
-
| `screenDriver.ts` | `screenDriver(options)`, `ScreenDriverOptions`, `ScreenDriver` |
|
|
625
|
+
| `screenDriver.ts` | `screenDriver(options)`, `ScreenDriverOptions`, `ScreenDriver`, `ScreenTarget` |
|
|
601
626
|
| `domNavigate.ts` | `domNavigate(url?)` |
|
|
602
627
|
| `genreTags.ts` | `genreTags(...kitNames)`, `GenreTag` |
|
|
603
628
|
| `domEnvironment.config.ts` | Default Vitest environment preserving Node host globals and happy-dom document transport |
|
|
604
|
-
| `fakeHttpService.ts` | `fakeHttpService(options)`, `FakeHttpRoute`, `FakeHttpRequest`, `FakeHttpReply`, `FakeHttpFault`, `FakeHttpHold`, `FakeHttpServiceOptions` |
|
|
629
|
+
| `fakeHttpService.ts` | `fakeHttpService(options)`, `FakeHttpRoute`, `FakeHttpRequest`, `FakeHttpReply`, `FakeHttpFault`, `FakeHttpHold`, `FakeHttpScope`, `FakeHttpScriptOptions`, `FakeHttpScript`, `FakeHttpServiceOptions` |
|
|
605
630
|
| `forwardingProxy.ts` | `forwardingProxy(upstream, options?)`, `ForwardingProxyOptions` |
|
|
606
631
|
| `otlpReceiver.ts` | `otlpReceiver(options?)`, `OtlpSpan`, `OtlpExport`, `OtlpReceiverOptions` |
|
|
607
632
|
|
|
@@ -20,7 +20,18 @@ export type FakeHttpFault = {
|
|
|
20
20
|
} | {
|
|
21
21
|
readonly kind: "drop" | "reset";
|
|
22
22
|
};
|
|
23
|
+
export interface FakeHttpScope {
|
|
24
|
+
readonly params?: Readonly<Record<string, string>>;
|
|
25
|
+
}
|
|
26
|
+
export interface FakeHttpScriptOptions extends FakeHttpScope {
|
|
27
|
+
readonly phase?: "before" | "after";
|
|
28
|
+
readonly repeat?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface FakeHttpScript {
|
|
31
|
+
readonly lift: Effect.Effect<void>;
|
|
32
|
+
}
|
|
23
33
|
export type FakeHttpRoute = {
|
|
34
|
+
readonly guard?: (request: FakeHttpRequest) => boolean;
|
|
24
35
|
readonly method?: string;
|
|
25
36
|
readonly path: string;
|
|
26
37
|
} & ({
|
|
@@ -40,14 +51,15 @@ export interface FakeHttpHold {
|
|
|
40
51
|
readonly waiting: Effect.Effect<void>;
|
|
41
52
|
}
|
|
42
53
|
export declare const fakeHttpService: (options: FakeHttpServiceOptions) => Effect.Effect<{
|
|
43
|
-
failOnce: (route: string, reply: FakeHttpReply) => Effect.Effect<void, never, never>;
|
|
44
|
-
fault: (route: string, fault: FakeHttpFault) => Effect.Effect<void, never, never>;
|
|
54
|
+
failOnce: (route: string, reply: FakeHttpReply, scope?: FakeHttpScope) => Effect.Effect<void, never, never>;
|
|
55
|
+
fault: (route: string, fault: FakeHttpFault, scope?: FakeHttpScope) => Effect.Effect<void, never, never>;
|
|
45
56
|
hold: (route: string) => Effect.Effect<FakeHttpHold, never, never>;
|
|
46
57
|
narrate: (sink: (line: string) => void) => void;
|
|
47
58
|
releaseOnArrival: (route: string, arrival: string) => Effect.Effect<void, never, never>;
|
|
48
|
-
replyOnce: (route: string, reply: FakeHttpReply) => Effect.Effect<void, never, never>;
|
|
59
|
+
replyOnce: (route: string, reply: FakeHttpReply, scope?: FakeHttpScope) => Effect.Effect<void, never, never>;
|
|
49
60
|
readonly requests: readonly FakeHttpRequest[];
|
|
50
|
-
|
|
61
|
+
scriptFault: (route: string, fault: FakeHttpFault, scriptOptions?: FakeHttpScriptOptions) => Effect.Effect<FakeHttpScript, never, never>;
|
|
62
|
+
stall: (route: string, scope?: FakeHttpScope) => Effect.Effect<FakeHttpHold, never, never>;
|
|
51
63
|
url: string;
|
|
52
64
|
}, import("effect/Cause").UnknownError, import("effect/Scope").Scope>;
|
|
53
65
|
//# sourceMappingURL=fakeHttpService.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fakeHttpService.d.ts","sourceRoot":"","sources":["../../src/server/fakeHttpService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAKhC,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,UAAU,CAAC,GAAG,aAAa,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC;IACrF,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAE7H,MAAM,MAAM,aAAa,GAAG;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB,GAAG,CACD;IAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,aAAa,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GACvF;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,SAAS,OAAO,EAAE,CAAC,EAAE,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,CAC7E,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC;IACvD,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;IACzD,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;CACtC;AAED,eAAO,MAAM,eAAe;sBAgBR,MAAM,SAAS,aAAa;
|
|
1
|
+
{"version":3,"file":"fakeHttpService.d.ts","sourceRoot":"","sources":["../../src/server/fakeHttpService.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,8 +19,8 @@ export const fakeHttpService = Effect.fnUntraced(function* (options) {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
return {
|
|
22
|
-
failOnce: (route, reply) => Effect.sync(() => checkRoute(route)).pipe(Effect.andThen(controls.queue(route, reply))),
|
|
23
|
-
fault: (route, fault) => Effect.sync(() => checkRoute(route)).pipe(Effect.andThen(controls.fault(route, fault))),
|
|
22
|
+
failOnce: (route, reply, scope) => Effect.sync(() => checkRoute(route)).pipe(Effect.andThen(controls.queue(route, reply, scope))),
|
|
23
|
+
fault: (route, fault, scope) => Effect.sync(() => checkRoute(route)).pipe(Effect.andThen(controls.fault(route, fault, scope))),
|
|
24
24
|
hold: (route) => Effect.sync(() => checkRoute(route)).pipe(Effect.andThen(controls.hold(route))),
|
|
25
25
|
narrate: (sink) => {
|
|
26
26
|
narrate = sink;
|
|
@@ -29,11 +29,12 @@ export const fakeHttpService = Effect.fnUntraced(function* (options) {
|
|
|
29
29
|
checkRoute(route);
|
|
30
30
|
checkRoute(arrival);
|
|
31
31
|
}).pipe(Effect.andThen(controls.releaseOnArrival(route, arrival))),
|
|
32
|
-
replyOnce: (route, reply) => Effect.sync(() => checkRoute(route)).pipe(Effect.andThen(controls.queue(route, reply))),
|
|
32
|
+
replyOnce: (route, reply, scope) => Effect.sync(() => checkRoute(route)).pipe(Effect.andThen(controls.queue(route, reply, scope))),
|
|
33
33
|
get requests() {
|
|
34
34
|
return requests.slice();
|
|
35
35
|
},
|
|
36
|
-
|
|
36
|
+
scriptFault: (route, fault, scriptOptions) => Effect.sync(() => checkRoute(route)).pipe(Effect.andThen(controls.scriptFault(route, fault, scriptOptions))),
|
|
37
|
+
stall: (route, scope) => Effect.sync(() => checkRoute(route)).pipe(Effect.andThen(controls.stall(route, scope))),
|
|
37
38
|
url,
|
|
38
39
|
};
|
|
39
40
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fakeHttpService.js","sourceRoot":"","sources":["../../src/server/fakeHttpService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA4B,MAAM,WAAW,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"fakeHttpService.js","sourceRoot":"","sources":["../../src/server/fakeHttpService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ServerResponse } from "node:http";
|
|
2
|
+
import type { FakeHttpRequest, FakeHttpRoute } from "#fakeHttpService.ts";
|
|
3
|
+
import type { fakeHttpControls } from "#internal/fakeHttpControls.ts";
|
|
4
|
+
export declare function fakeHttpAnswer(name: string, route: FakeHttpRoute, request: FakeHttpRequest, response: ServerResponse, controls: ReturnType<typeof fakeHttpControls>, tell: (line: string) => void): Promise<void>;
|
|
5
|
+
//# sourceMappingURL=fakeHttpAnswer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fakeHttpAnswer.d.ts","sourceRoot":"","sources":["../../../src/server/internal/fakeHttpAnswer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAiB,eAAe,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AActE,wBAAsB,cAAc,CACnC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,cAAc,EACxB,QAAQ,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,EAC7C,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,iBAyB5B"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { fakeHttpSend, fakeHttpStrike } from "#internal/fakeHttpSend.ts";
|
|
2
|
+
function pageReply(route, request) {
|
|
3
|
+
if (route.reply) {
|
|
4
|
+
return route.reply(request);
|
|
5
|
+
}
|
|
6
|
+
const page = Number(request.query.get("page") ?? "1");
|
|
7
|
+
if (!Number.isInteger(page) || page < 1) {
|
|
8
|
+
return { body: { error: "page must be a positive integer" }, status: 400 };
|
|
9
|
+
}
|
|
10
|
+
return { body: { items: route.pages[page - 1] ?? [], next: page < route.pages.length ? page + 1 : null } };
|
|
11
|
+
}
|
|
12
|
+
export async function fakeHttpAnswer(name, route, request, response, controls, tell) {
|
|
13
|
+
const { method, path } = request;
|
|
14
|
+
controls.arrive(name, request, response);
|
|
15
|
+
if (!(await controls.pause(name, response))) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const before = controls.takeFault(name, request, "before");
|
|
19
|
+
if (before && before.kind !== "status") {
|
|
20
|
+
tell(`fake HTTP ${name} fault ${before.kind} before reply`);
|
|
21
|
+
await fakeHttpStrike(response, before);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
let reply = before?.reply ?? pageReply(route, request);
|
|
25
|
+
const after = before ? undefined : controls.takeFault(name, request, "after");
|
|
26
|
+
if (after) {
|
|
27
|
+
tell(`fake HTTP ${name} fault ${after.kind} after reply`);
|
|
28
|
+
if (after.kind !== "status") {
|
|
29
|
+
await fakeHttpStrike(response, after, reply);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
reply = after.reply;
|
|
33
|
+
}
|
|
34
|
+
tell(`fake HTTP answered ${method} ${path} with ${reply.status ?? 200}`);
|
|
35
|
+
await fakeHttpSend(response, reply, controls.stallBody(name, request));
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=fakeHttpAnswer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fakeHttpAnswer.js","sourceRoot":"","sources":["../../../src/server/internal/fakeHttpAnswer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEzE,SAAS,SAAS,CAAC,KAAoB,EAAE,OAAwB;IAChE,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;IACtD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QACzC,OAAO,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,iCAAiC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IAC5E,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;AAC5G,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CACnC,IAAY,EACZ,KAAoB,EACpB,OAAwB,EACxB,QAAwB,EACxB,QAA6C,EAC7C,IAA4B;IAE5B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IACzC,IAAI,CAAC,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC;QAC7C,OAAO;IACR,CAAC;IACD,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC3D,IAAI,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACxC,IAAI,CAAC,aAAa,IAAI,UAAU,MAAM,CAAC,IAAI,eAAe,CAAC,CAAC;QAC5D,MAAM,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACvC,OAAO;IACR,CAAC;IACD,IAAI,KAAK,GAAG,MAAM,EAAE,KAAK,IAAI,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC9E,IAAI,KAAK,EAAE,CAAC;QACX,IAAI,CAAC,aAAa,IAAI,UAAU,KAAK,CAAC,IAAI,cAAc,CAAC,CAAC;QAC1D,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,MAAM,cAAc,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;YAC7C,OAAO;QACR,CAAC;QACD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IACD,IAAI,CAAC,sBAAsB,MAAM,IAAI,IAAI,SAAS,KAAK,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC;IACzE,MAAM,YAAY,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AACxE,CAAC"}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import type { ServerResponse } from "node:http";
|
|
2
2
|
import { Effect } from "effect";
|
|
3
|
-
import type { FakeHttpFault, FakeHttpReply } from "#fakeHttpService.ts";
|
|
3
|
+
import type { FakeHttpFault, FakeHttpReply, FakeHttpRequest, FakeHttpScope, FakeHttpScriptOptions } from "#fakeHttpService.ts";
|
|
4
4
|
export declare function fakeHttpControls(tell: (line: string) => void): {
|
|
5
|
-
arrive: (route: string, response: ServerResponse) => void;
|
|
5
|
+
arrive: (route: string, request: FakeHttpRequest, response: ServerResponse) => void;
|
|
6
6
|
close: () => void;
|
|
7
|
-
fault: (route: string, fault: FakeHttpFault) => Effect.Effect<void, never, never>;
|
|
7
|
+
fault: (route: string, fault: FakeHttpFault, scope?: FakeHttpScope) => Effect.Effect<void, never, never>;
|
|
8
8
|
hold: (route: string) => Effect.Effect<import("#fakeHttpService.ts").FakeHttpHold, never, never>;
|
|
9
9
|
pause: (route: string, response: ServerResponse) => Promise<boolean>;
|
|
10
|
-
queue: (route: string, reply: FakeHttpReply) => Effect.Effect<void, never, never>;
|
|
10
|
+
queue: (route: string, reply: FakeHttpReply, scope?: FakeHttpScope) => Effect.Effect<void, never, never>;
|
|
11
11
|
releaseOnArrival: (route: string, arrival: string) => Effect.Effect<void, never, never>;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
scriptFault: (route: string, fault: FakeHttpFault, options?: FakeHttpScriptOptions) => Effect.Effect<import("#fakeHttpService.ts").FakeHttpScript, never, never>;
|
|
13
|
+
stall: (route: string, scope?: FakeHttpScope) => Effect.Effect<import("#fakeHttpService.ts").FakeHttpHold, never, never>;
|
|
14
|
+
stallBody: (route: string, request: FakeHttpRequest) => (() => Promise<void>) | undefined;
|
|
15
|
+
takeFault: (route: string, request: FakeHttpRequest, phase: "before" | "after") => FakeHttpFault | undefined;
|
|
15
16
|
};
|
|
16
17
|
//# sourceMappingURL=fakeHttpControls.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fakeHttpControls.d.ts","sourceRoot":"","sources":["../../../src/server/internal/fakeHttpControls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"fakeHttpControls.d.ts","sourceRoot":"","sources":["../../../src/server/internal/fakeHttpControls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAM/H,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI;IAO3D,MAAM,UAAU,MAAM,WAAW,eAAe,YAAY,cAAc;IAQ1E,KAAK;IASL,KAAK,UAAU,MAAM,SAAS,aAAa,UAAU,aAAa;IAqBlE,IAAI,UAAU,MAAM;IACpB,KAAK,UAAgB,MAAM,YAAY,cAAc;IAIrD,KAAK,UAAU,MAAM,SAAS,aAAa,UAAU,aAAa;IAKlE,gBAAgB,UAAU,MAAM,WAAW,MAAM;IAWjD,WAAW,UAAU,MAAM,SAAS,aAAa,YAAY,qBAAqB;IAClF,KAAK,UAAU,MAAM,UAAU,aAAa;IAC5C,SAAS,UAAU,MAAM,WAAW,eAAe;IAInD,SAAS;EAEV"}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import { fakeHttpGates } from "#internal/fakeHttpGates.ts";
|
|
3
|
+
import { fakeHttpScripts } from "#internal/fakeHttpScripts.ts";
|
|
4
|
+
import { fakeHttpSelection } from "#internal/fakeHttpSelection.ts";
|
|
3
5
|
import { fakeHttpConnection, fakeHttpSend } from "#internal/fakeHttpSend.ts";
|
|
4
6
|
export function fakeHttpControls(tell) {
|
|
5
|
-
const
|
|
7
|
+
const scripts = fakeHttpScripts(tell);
|
|
6
8
|
const holds = fakeHttpGates(tell, "hold");
|
|
7
9
|
const stalls = fakeHttpGates(tell, "stall");
|
|
8
10
|
const arrivals = new Map();
|
|
9
11
|
const active = new Map();
|
|
10
12
|
return {
|
|
11
|
-
arrive: (route, response) => {
|
|
12
|
-
active.set(response, route);
|
|
13
|
+
arrive: (route, request, response) => {
|
|
14
|
+
active.set(response, { request, route });
|
|
13
15
|
response.once("close", () => active.delete(response));
|
|
14
16
|
for (const release of arrivals.get(route) ?? []) {
|
|
15
17
|
release();
|
|
@@ -23,14 +25,17 @@ export function fakeHttpControls(tell) {
|
|
|
23
25
|
holds.close();
|
|
24
26
|
stalls.close();
|
|
25
27
|
arrivals.clear();
|
|
26
|
-
|
|
28
|
+
scripts.clear();
|
|
27
29
|
},
|
|
28
|
-
fault: (route, fault) => Effect.promise(async () => {
|
|
29
|
-
const
|
|
30
|
+
fault: (route, fault, scope) => Effect.promise(async () => {
|
|
31
|
+
const selection = fakeHttpSelection(route, scope);
|
|
32
|
+
const response = [...active].find(([candidate, entry]) => selection.matches(entry.route, entry.request)
|
|
33
|
+
&& !(candidate.destroyed || candidate.writableEnded)
|
|
34
|
+
&& candidate.headersSent === (fault.kind === "reset"))?.[0];
|
|
30
35
|
if (!response) {
|
|
31
36
|
throw new Error(`Fake HTTP route "${route}" has no request ready for a ${fault.kind} fault.\nhelp: Wait for a hold before a status or drop fault, or for response headers before a reset fault.`);
|
|
32
37
|
}
|
|
33
|
-
tell(`fake HTTP ${
|
|
38
|
+
tell(`fake HTTP ${selection.label} fault ${fault.kind}`);
|
|
34
39
|
if (fault.kind === "status") {
|
|
35
40
|
await fakeHttpSend(response, fault.reply);
|
|
36
41
|
}
|
|
@@ -43,11 +48,9 @@ export function fakeHttpControls(tell) {
|
|
|
43
48
|
await fakeHttpConnection(response).pause(holds.get(route)?.enter());
|
|
44
49
|
return !(response.destroyed || response.writableEnded || response.headersSent);
|
|
45
50
|
},
|
|
46
|
-
queue: (route, reply) => Effect.sync(() => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
replies.set(route, queue);
|
|
50
|
-
tell(`fake HTTP ${route} will answer once with ${reply.status ?? 200}`);
|
|
51
|
+
queue: (route, reply, scope) => Effect.sync(() => {
|
|
52
|
+
scripts.make(route, { kind: "status", reply }, scope, false);
|
|
53
|
+
tell(`fake HTTP ${fakeHttpSelection(route, scope).label} will answer once with ${reply.status ?? 200}`);
|
|
51
54
|
}),
|
|
52
55
|
releaseOnArrival: (route, arrival) => Effect.sync(() => {
|
|
53
56
|
const hold = holds.get(route);
|
|
@@ -59,12 +62,13 @@ export function fakeHttpControls(tell) {
|
|
|
59
62
|
arrivals.set(arrival, releases);
|
|
60
63
|
tell(`fake HTTP ${route} will release when ${arrival} arrives`);
|
|
61
64
|
}),
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
scriptFault: (route, fault, options) => Effect.sync(() => scripts.make(route, fault, options)),
|
|
66
|
+
stall: (route, scope) => Effect.sync(() => stalls.make(route, scope)),
|
|
67
|
+
stallBody: (route, request) => {
|
|
68
|
+
const stall = stalls.get(route, request);
|
|
65
69
|
return stall ? () => stall.enter() : undefined;
|
|
66
70
|
},
|
|
67
|
-
|
|
71
|
+
takeFault: scripts.take,
|
|
68
72
|
};
|
|
69
73
|
}
|
|
70
74
|
//# sourceMappingURL=fakeHttpControls.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fakeHttpControls.js","sourceRoot":"","sources":["../../../src/server/internal/fakeHttpControls.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE7E,MAAM,UAAU,gBAAgB,CAAC,IAA4B;IAC5D,MAAM,OAAO,GAAG,
|
|
1
|
+
{"version":3,"file":"fakeHttpControls.js","sourceRoot":"","sources":["../../../src/server/internal/fakeHttpControls.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type { FakeHttpHold } from "#fakeHttpService.ts";
|
|
1
|
+
import type { FakeHttpHold, FakeHttpRequest, FakeHttpScope } from "#fakeHttpService.ts";
|
|
2
|
+
import { fakeHttpSelection } from "#internal/fakeHttpSelection.ts";
|
|
2
3
|
export declare function fakeHttpGates(tell: (line: string) => void, kind: "hold" | "stall"): {
|
|
3
4
|
close: () => void;
|
|
4
|
-
get: (route: string) => {
|
|
5
|
+
get: (route: string, request?: FakeHttpRequest) => {
|
|
5
6
|
enter: () => Promise<void>;
|
|
6
7
|
release: () => void;
|
|
8
|
+
matches: ReturnType<typeof fakeHttpSelection>["matches"];
|
|
7
9
|
} | undefined;
|
|
8
|
-
make: (route: string) => FakeHttpHold;
|
|
10
|
+
make: (route: string, scope?: FakeHttpScope) => FakeHttpHold;
|
|
9
11
|
};
|
|
10
12
|
//# sourceMappingURL=fakeHttpGates.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fakeHttpGates.d.ts","sourceRoot":"","sources":["../../../src/server/internal/fakeHttpGates.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"fakeHttpGates.d.ts","sourceRoot":"","sources":["../../../src/server/internal/fakeHttpGates.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAEnE,wBAAgB,aAAa,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO;IAGhF,KAAK;IAKL,GAAG,UAAU,MAAM,YAAY,eAAe;eAPR,MAAM,OAAO,CAAC,IAAI,CAAC;iBAAW,MAAM,IAAI;iBAAW,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC,SAAS,CAAC;;IASxI,IAAI,UAAU,MAAM,UAAU,aAAa,KAAG,YAAY;EA6B3D"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Deferred, Effect } from "effect";
|
|
2
|
+
import { fakeHttpSelection } from "#internal/fakeHttpSelection.ts";
|
|
2
3
|
export function fakeHttpGates(tell, kind) {
|
|
3
4
|
const gates = new Map();
|
|
4
5
|
return {
|
|
@@ -7,30 +8,33 @@ export function fakeHttpGates(tell, kind) {
|
|
|
7
8
|
gate.release();
|
|
8
9
|
}
|
|
9
10
|
},
|
|
10
|
-
get: (route) => gates.get(route),
|
|
11
|
-
make: (route) => {
|
|
12
|
-
|
|
11
|
+
get: (route, request) => request ? [...gates.values()].find((gate) => gate.matches(route, request)) : gates.get(route),
|
|
12
|
+
make: (route, scope) => {
|
|
13
|
+
const selection = fakeHttpSelection(route, scope);
|
|
14
|
+
const label = selection.label;
|
|
15
|
+
if (gates.has(label)) {
|
|
13
16
|
throw new Error(`Fake HTTP route "${route}" already has a ${kind}.\nhelp: Release its current ${kind} before scripting another.`);
|
|
14
17
|
}
|
|
15
18
|
const waiting = Deferred.makeUnsafe();
|
|
16
19
|
const released = Promise.withResolvers();
|
|
17
20
|
const gate = {
|
|
18
21
|
enter: () => {
|
|
19
|
-
tell(kind === "hold" ? `fake HTTP ${
|
|
22
|
+
tell(kind === "hold" ? `fake HTTP ${label} has a waiting request` : `fake HTTP ${label} body stalled`);
|
|
20
23
|
Deferred.doneUnsafe(waiting, Effect.void);
|
|
21
24
|
return released.promise;
|
|
22
25
|
},
|
|
26
|
+
matches: selection.matches,
|
|
23
27
|
release: () => {
|
|
24
|
-
if (gates.get(
|
|
28
|
+
if (gates.get(label) !== gate) {
|
|
25
29
|
return;
|
|
26
30
|
}
|
|
27
|
-
gates.delete(
|
|
31
|
+
gates.delete(label);
|
|
28
32
|
released.resolve();
|
|
29
|
-
tell(kind === "hold" ? `fake HTTP ${
|
|
33
|
+
tell(kind === "hold" ? `fake HTTP ${label} released` : `fake HTTP ${label} body released`);
|
|
30
34
|
},
|
|
31
35
|
};
|
|
32
|
-
gates.set(
|
|
33
|
-
tell(kind === "hold" ? `fake HTTP ${
|
|
36
|
+
gates.set(label, gate);
|
|
37
|
+
tell(kind === "hold" ? `fake HTTP ${label} held` : `fake HTTP ${label} body will stall`);
|
|
34
38
|
return { release: Effect.sync(gate.release), waiting: Deferred.await(waiting) };
|
|
35
39
|
},
|
|
36
40
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fakeHttpGates.js","sourceRoot":"","sources":["../../../src/server/internal/fakeHttpGates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"fakeHttpGates.js","sourceRoot":"","sources":["../../../src/server/internal/fakeHttpGates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAEnE,MAAM,UAAU,aAAa,CAAC,IAA4B,EAAE,IAAsB;IACjF,MAAM,KAAK,GAAG,IAAI,GAAG,EAAyH,CAAC;IAC/I,OAAO;QACN,KAAK,EAAE,GAAG,EAAE;YACX,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;gBACnC,IAAI,CAAC,OAAO,EAAE,CAAC;YAChB,CAAC;QACF,CAAC;QACD,GAAG,EAAE,CAAC,KAAa,EAAE,OAAyB,EAAE,EAAE,CACjD,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;QAC9F,IAAI,EAAE,CAAC,KAAa,EAAE,KAAqB,EAAgB,EAAE;YAC5D,MAAM,SAAS,GAAG,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAClD,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC;YAC9B,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBACtB,MAAM,IAAI,KAAK,CAAC,oBAAoB,KAAK,mBAAmB,IAAI,gCAAgC,IAAI,4BAA4B,CAAC,CAAC;YACnI,CAAC;YACD,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAU,EAAQ,CAAC;YAC5C,MAAM,QAAQ,GAAG,OAAO,CAAC,aAAa,EAAQ,CAAC;YAC/C,MAAM,IAAI,GAAG;gBACZ,KAAK,EAAE,GAAG,EAAE;oBACX,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,aAAa,KAAK,wBAAwB,CAAC,CAAC,CAAC,aAAa,KAAK,eAAe,CAAC,CAAC;oBACvG,QAAQ,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;oBAC1C,OAAO,QAAQ,CAAC,OAAO,CAAC;gBACzB,CAAC;gBACD,OAAO,EAAE,SAAS,CAAC,OAAO;gBAC1B,OAAO,EAAE,GAAG,EAAE;oBACb,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;wBAC/B,OAAO;oBACR,CAAC;oBACD,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBACpB,QAAQ,CAAC,OAAO,EAAE,CAAC;oBACnB,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,aAAa,KAAK,WAAW,CAAC,CAAC,CAAC,aAAa,KAAK,gBAAgB,CAAC,CAAC;gBAC5F,CAAC;aACD,CAAC;YACF,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YACvB,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,aAAa,KAAK,kBAAkB,CAAC,CAAC;YACzF,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACjF,CAAC;KACD,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fakeHttpReceive.d.ts","sourceRoot":"","sources":["../../../src/server/internal/fakeHttpReceive.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"fakeHttpReceive.d.ts","sourceRoot":"","sources":["../../../src/server/internal/fakeHttpReceive.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAiB,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAElG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAmCtE,wBAAsB,eAAe,CACpC,QAAQ,EAAE,eAAe,EACzB,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,sBAAsB,EAC/B,QAAQ,EAAE,eAAe,EAAE,EAC3B,QAAQ,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,EAC7C,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,iBAiD5B"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fakeHttpAnswer } from "#internal/fakeHttpAnswer.ts";
|
|
1
2
|
import { fakeHttpSend } from "#internal/fakeHttpSend.ts";
|
|
2
3
|
function match(route, method, pathname) {
|
|
3
4
|
if ((route.method ?? "GET").toUpperCase() !== method) {
|
|
@@ -31,16 +32,6 @@ function decodeParams(params) {
|
|
|
31
32
|
return false;
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
|
-
function pageReply(route, request) {
|
|
35
|
-
if (route.reply) {
|
|
36
|
-
return route.reply(request);
|
|
37
|
-
}
|
|
38
|
-
const page = Number(request.query.get("page") ?? "1");
|
|
39
|
-
if (!Number.isInteger(page) || page < 1) {
|
|
40
|
-
return { body: { error: "page must be a positive integer" }, status: 400 };
|
|
41
|
-
}
|
|
42
|
-
return { body: { items: route.pages[page - 1] ?? [], next: page < route.pages.length ? page + 1 : null } };
|
|
43
|
-
}
|
|
44
35
|
export async function fakeHttpReceive(incoming, response, options, requests, controls, tell) {
|
|
45
36
|
try {
|
|
46
37
|
const path = incoming.url ?? "/";
|
|
@@ -70,7 +61,7 @@ export async function fakeHttpReceive(incoming, response, options, requests, con
|
|
|
70
61
|
await fakeHttpSend(response, { status: 400 });
|
|
71
62
|
return;
|
|
72
63
|
}
|
|
73
|
-
if (options.guard && !options.guard(request)) {
|
|
64
|
+
if ((options.guard && !options.guard(request)) || (found?.route.guard && !found.route.guard(request))) {
|
|
74
65
|
tell(`fake HTTP rejected ${method} ${path} with 401`);
|
|
75
66
|
await fakeHttpSend(response, { status: 401 });
|
|
76
67
|
return;
|
|
@@ -80,13 +71,7 @@ export async function fakeHttpReceive(incoming, response, options, requests, con
|
|
|
80
71
|
await fakeHttpSend(response, { status: 404 });
|
|
81
72
|
return;
|
|
82
73
|
}
|
|
83
|
-
|
|
84
|
-
if (!(await controls.pause(found.name, response))) {
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
const reply = controls.takeReply(found.name) ?? pageReply(found.route, request);
|
|
88
|
-
tell(`fake HTTP answered ${method} ${path} with ${reply.status ?? 200}`);
|
|
89
|
-
await fakeHttpSend(response, reply, controls.stallBody(found.name));
|
|
74
|
+
await fakeHttpAnswer(found.name, found.route, request, response, controls, tell);
|
|
90
75
|
}
|
|
91
76
|
catch (error) {
|
|
92
77
|
tell(`fake HTTP could not answer: ${String(error)}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fakeHttpReceive.js","sourceRoot":"","sources":["../../../src/server/internal/fakeHttpReceive.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fakeHttpReceive.js","sourceRoot":"","sources":["../../../src/server/internal/fakeHttpReceive.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAE7D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAEzD,SAAS,KAAK,CAAC,KAAoB,EAAE,MAAc,EAAE,QAAgB;IACpE,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;QACtD,OAAO;IACR,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC;QACxC,OAAO;IACR,CAAC;IACD,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QAC/C,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACtC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;QACjC,CAAC;aAAM,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YAC7B,OAAO;QACR,CAAC;IACF,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC;AAED,SAAS,YAAY,CAAC,MAA8B;IACnD,IAAI,CAAC;QACJ,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACpD,MAAM,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,QAAyB,EACzB,QAAwB,EACxB,OAA+B,EAC/B,QAA2B,EAC3B,QAA6C,EAC7C,IAA4B;IAE5B,IAAI,CAAC;QACJ,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,IAAI,GAAG,CAAC;QACjC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,IAAI,KAAK,CAAC;QACxC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE;YACtE,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;YAClD,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAChD,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACN,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;YACpC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QACjC,CAAC;QACD,MAAM,MAAM,GAAG,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC;QACnC,MAAM,OAAO,GAAoB;YAChC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;YAC3B,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,MAAM;YACN,MAAM;YACN,IAAI;YACJ,KAAK,EAAE,GAAG,CAAC,YAAY;SACvB,CAAC;QACF,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,IAAI,CAAC,sBAAsB,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC,sBAAsB,MAAM,IAAI,IAAI,WAAW,CAAC,CAAC;YACtD,MAAM,YAAY,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YAC9C,OAAO;QACR,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;YACvG,IAAI,CAAC,sBAAsB,MAAM,IAAI,IAAI,WAAW,CAAC,CAAC;YACtD,MAAM,YAAY,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YAC9C,OAAO;QACR,CAAC;QACD,IAAI,CAAC,KAAK,EAAE,CAAC;YACZ,IAAI,CAAC,sBAAsB,MAAM,IAAI,IAAI,WAAW,CAAC,CAAC;YACtD,MAAM,YAAY,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YAC9C,OAAO;QACR,CAAC;QACD,MAAM,cAAc,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IAClF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,CAAC,+BAA+B,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACrD,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC;YAC1B,QAAQ,CAAC,OAAO,EAAE,CAAC;QACpB,CAAC;aAAM,CAAC;YACP,MAAM,YAAY,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAC/C,CAAC;IACF,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FakeHttpFault, FakeHttpRequest, FakeHttpScript, FakeHttpScriptOptions } from "#fakeHttpService.ts";
|
|
2
|
+
export declare function fakeHttpScripts(tell: (line: string) => void): {
|
|
3
|
+
clear: () => void;
|
|
4
|
+
make: (route: string, fault: FakeHttpFault, options?: FakeHttpScriptOptions, narrate?: boolean) => FakeHttpScript;
|
|
5
|
+
take: (route: string, request: FakeHttpRequest, phase: "before" | "after") => FakeHttpFault | undefined;
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=fakeHttpScripts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fakeHttpScripts.d.ts","sourceRoot":"","sources":["../../../src/server/internal/fakeHttpScripts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAGjH,wBAAgB,eAAe,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI;IAO1D,KAAK;IACL,IAAI,UAAU,MAAM,SAAS,aAAa,YAAW,qBAAqB,wBAAwB,cAAc;IAehH,IAAI,UAAU,MAAM,WAAW,eAAe,SAAS,QAAQ,GAAG,OAAO;EAY1E"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { fakeHttpSelection } from "#internal/fakeHttpSelection.ts";
|
|
3
|
+
export function fakeHttpScripts(tell) {
|
|
4
|
+
const scripts = new Set();
|
|
5
|
+
return {
|
|
6
|
+
clear: () => scripts.clear(),
|
|
7
|
+
make: (route, fault, options = {}, narrate = true) => {
|
|
8
|
+
const selection = fakeHttpSelection(route, options);
|
|
9
|
+
const script = { fault, options, selection };
|
|
10
|
+
scripts.add(script);
|
|
11
|
+
if (narrate) {
|
|
12
|
+
tell(`fake HTTP ${selection.label} will fault ${fault.kind} ${options.phase ?? "before"} reply ${options.repeat ? "until lifted" : "once"}`);
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
lift: Effect.sync(() => {
|
|
16
|
+
if (scripts.delete(script)) {
|
|
17
|
+
tell(`fake HTTP ${selection.label} fault ${fault.kind} lifted`);
|
|
18
|
+
}
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
take: (route, request, phase) => {
|
|
23
|
+
for (const script of scripts) {
|
|
24
|
+
if ((script.options.phase ?? "before") === phase && script.selection.matches(route, request)) {
|
|
25
|
+
if (!script.options.repeat) {
|
|
26
|
+
scripts.delete(script);
|
|
27
|
+
}
|
|
28
|
+
return script.fault;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=fakeHttpScripts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fakeHttpScripts.js","sourceRoot":"","sources":["../../../src/server/internal/fakeHttpScripts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAEnE,MAAM,UAAU,eAAe,CAAC,IAA4B;IAC3D,MAAM,OAAO,GAAG,IAAI,GAAG,EAInB,CAAC;IACL,OAAO;QACN,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE;QAC5B,IAAI,EAAE,CAAC,KAAa,EAAE,KAAoB,EAAE,OAAO,GAA0B,EAAE,EAAE,OAAO,GAAG,IAAI,EAAkB,EAAE;YAClH,MAAM,SAAS,GAAG,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YACpD,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;YAC7C,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACpB,IAAI,OAAO,EAAE,CAAC;gBACb,IAAI,CAAC,aAAa,SAAS,CAAC,KAAK,eAAe,KAAK,CAAC,IAAI,IAAI,OAAO,CAAC,KAAK,IAAI,QAAQ,UAAU,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;YAC9I,CAAC;YACD,OAAO;gBACN,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;oBACtB,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;wBAC5B,IAAI,CAAC,aAAa,SAAS,CAAC,KAAK,UAAU,KAAK,CAAC,IAAI,SAAS,CAAC,CAAC;oBACjE,CAAC;gBACF,CAAC,CAAC;aACF,CAAC;QACH,CAAC;QACD,IAAI,EAAE,CAAC,KAAa,EAAE,OAAwB,EAAE,KAAyB,EAAE,EAAE;YAC5E,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC9B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,QAAQ,CAAC,KAAK,KAAK,IAAI,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;oBAC9F,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;wBAC5B,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;oBACxB,CAAC;oBACD,OAAO,MAAM,CAAC,KAAK,CAAC;gBACrB,CAAC;YACF,CAAC;YACD,OAAO,SAAS,CAAC;QAClB,CAAC;KACD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { FakeHttpRequest, FakeHttpScope } from "#fakeHttpService.ts";
|
|
2
|
+
export declare function fakeHttpSelection(route: string, scope?: FakeHttpScope): {
|
|
3
|
+
label: string;
|
|
4
|
+
matches: (name: string, request: FakeHttpRequest) => boolean;
|
|
5
|
+
};
|
|
6
|
+
//# sourceMappingURL=fakeHttpSelection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fakeHttpSelection.d.ts","sourceRoot":"","sources":["../../../src/server/internal/fakeHttpSelection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE1E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,aAAkB;;oBAKxD,MAAM,WAAW,eAAe;EAEjD"}
|