@schmock/core 2.3.0 → 2.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.
- package/LICENSE +21 -0
- package/README.md +35 -0
- package/dist/abort.d.ts +3 -0
- package/dist/abort.d.ts.map +1 -0
- package/dist/abort.js +30 -0
- package/dist/builder.d.ts +31 -10
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +760 -190
- package/dist/constants.d.ts +38 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +123 -2
- package/dist/errors.d.ts +14 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +41 -6
- package/dist/helpers.d.ts +9 -0
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +18 -2
- package/dist/http-helpers.d.ts +39 -4
- package/dist/http-helpers.d.ts.map +1 -1
- package/dist/http-helpers.js +147 -49
- package/dist/index.d.ts +292 -34
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/interceptor.d.ts +11 -1
- package/dist/interceptor.d.ts.map +1 -1
- package/dist/interceptor.js +316 -169
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +12 -3
- package/dist/plugin-pipeline.d.ts +3 -3
- package/dist/plugin-pipeline.d.ts.map +1 -1
- package/dist/plugin-pipeline.js +29 -14
- package/dist/response-normalizer.d.ts +16 -0
- package/dist/response-normalizer.d.ts.map +1 -0
- package/dist/response-normalizer.js +316 -0
- package/dist/response-parser.d.ts.map +1 -1
- package/dist/response-parser.js +39 -2
- package/dist/route-matcher.d.ts +3 -0
- package/dist/route-matcher.d.ts.map +1 -1
- package/dist/route-matcher.js +12 -6
- package/dist/types.d.ts +7 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +18 -6
- package/src/audit-core-builder.test.ts +0 -218
- package/src/audit-response-guard.test.ts +0 -38
- package/src/binary.test.ts +0 -126
- package/src/binary.ts +0 -11
- package/src/builder.test.ts +0 -289
- package/src/builder.ts +0 -748
- package/src/constants.test.ts +0 -99
- package/src/constants.ts +0 -73
- package/src/debug.test.ts +0 -241
- package/src/delay.test.ts +0 -319
- package/src/dist-shape.test.ts +0 -35
- package/src/errors.test.ts +0 -223
- package/src/errors.ts +0 -130
- package/src/factory.test.ts +0 -133
- package/src/helpers.test.ts +0 -147
- package/src/helpers.ts +0 -58
- package/src/http-helpers.test.ts +0 -39
- package/src/http-helpers.ts +0 -149
- package/src/index.ts +0 -152
- package/src/interceptor.test.ts +0 -488
- package/src/interceptor.ts +0 -407
- package/src/namespace.test.ts +0 -274
- package/src/parser.property.test.ts +0 -630
- package/src/parser.test.ts +0 -148
- package/src/parser.ts +0 -64
- package/src/plugin-pipeline.ts +0 -208
- package/src/plugin-system.test.ts +0 -697
- package/src/response-parser.ts +0 -114
- package/src/response-parsing.test.ts +0 -333
- package/src/route-matcher.ts +0 -69
- package/src/route-matching.test.ts +0 -394
- package/src/server.test.ts +0 -234
- package/src/smart-defaults.test.ts +0 -361
- package/src/steps/async-support.steps.ts +0 -421
- package/src/steps/audit-core-builder.steps.ts +0 -188
- package/src/steps/audit-onerror-tuple.steps.ts +0 -83
- package/src/steps/basic-usage.steps.ts +0 -245
- package/src/steps/developer-experience.steps.ts +0 -204
- package/src/steps/error-handling.steps.ts +0 -555
- package/src/steps/fetch-interceptor.steps.ts +0 -457
- package/src/steps/fluent-api.steps.ts +0 -255
- package/src/steps/http-methods.steps.ts +0 -342
- package/src/steps/lifecycle-events.steps.ts +0 -149
- package/src/steps/performance-reliability.steps.ts +0 -90
- package/src/steps/plugin-integration.steps.ts +0 -334
- package/src/steps/request-history.steps.ts +0 -458
- package/src/steps/response-delay.steps.ts +0 -88
- package/src/steps/route-key-format.steps.ts +0 -99
- package/src/steps/standalone-server.steps.ts +0 -227
- package/src/steps/state-concurrency.steps.ts +0 -167
- package/src/types.ts +0 -36
package/dist/constants.d.ts
CHANGED
|
@@ -1,9 +1,46 @@
|
|
|
1
1
|
import type { HttpMethod } from "./types.js";
|
|
2
2
|
export declare const ROUTE_NOT_FOUND_CODE: "ROUTE_NOT_FOUND";
|
|
3
|
+
interface ResponseLike {
|
|
4
|
+
status: number;
|
|
5
|
+
body: unknown;
|
|
6
|
+
}
|
|
7
|
+
export declare function markRouteNotFound<T extends ResponseLike>(response: T): T;
|
|
8
|
+
export declare function markResponseException<T extends ResponseLike>(response: T, error: Error): T;
|
|
9
|
+
export declare function getResponseException(response: ResponseLike): Error | undefined;
|
|
3
10
|
export declare const HTTP_METHODS: readonly HttpMethod[];
|
|
4
11
|
export declare function isHttpMethod(method: string): method is HttpMethod;
|
|
5
12
|
export declare function toHttpMethod(method: string): HttpMethod;
|
|
6
13
|
export declare function normalizePath(path: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Put a path into the single canonical (percent-encoded) transport form.
|
|
16
|
+
*
|
|
17
|
+
* A path reaches core either as `url.pathname` (already encoded by the URL
|
|
18
|
+
* parser) or as whatever string a `handle()` caller typed, so route paths and
|
|
19
|
+
* request paths must be encoded the same way before they can be compared.
|
|
20
|
+
* Encoding is idempotent: an existing valid `%XX` triplet is normalized to
|
|
21
|
+
* uppercase rather than re-encoded, so applying this at route-parse time and
|
|
22
|
+
* again at request time is safe.
|
|
23
|
+
*
|
|
24
|
+
* This deliberately never throws and never resolves `.`/`..` — an unreachable
|
|
25
|
+
* spelling is preferable to silently rewriting the caller's path.
|
|
26
|
+
*/
|
|
27
|
+
export declare function canonicalizePath(path: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Decode a single captured path parameter.
|
|
30
|
+
*
|
|
31
|
+
* Decoding happens AFTER segmentation, so an encoded separator (`%2F`) stays
|
|
32
|
+
* inside one parameter and reaches the generator as "/" — the same as Express.
|
|
33
|
+
* A malformed sequence (`%ZZ`) is handed back untouched rather than throwing.
|
|
34
|
+
*/
|
|
35
|
+
export declare function decodePathSegment(segment: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* Build a route key from a method and a path.
|
|
38
|
+
*
|
|
39
|
+
* `Schmock.RouteKey` requires a leading slash, so a path without one is
|
|
40
|
+
* normalized rather than rejected — callers feed spec paths and pathnames that
|
|
41
|
+
* are already absolute, and silently producing an unreachable route key would
|
|
42
|
+
* be worse than adding the slash.
|
|
43
|
+
*/
|
|
7
44
|
export declare function toRouteKey(method: HttpMethod, path: string): Schmock.RouteKey;
|
|
8
45
|
/**
|
|
9
46
|
* Check if a Schmock response is a route-not-found response.
|
|
@@ -23,4 +60,5 @@ export declare function isRouteNotFound(response: {
|
|
|
23
60
|
* status() helper or return an object response when the data could collide.
|
|
24
61
|
*/
|
|
25
62
|
export declare function isStatusTuple(value: unknown): value is [number, unknown] | [number, unknown, Record<string, string>];
|
|
63
|
+
export {};
|
|
26
64
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,eAAO,MAAM,oBAAoB,EAAG,iBAA0B,CAAC;AAE/D,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAQpC,CAAC;AAEX,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,UAAU,CAEjE;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAMvD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,eAAO,MAAM,oBAAoB,EAAG,iBAA0B,CAAC;AAE/D,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf;AA+BD,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,YAAY,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CAGxE;AAED,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,YAAY,EAC1D,QAAQ,EAAE,CAAC,EACX,KAAK,EAAE,KAAK,GACX,CAAC,CAGH;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,YAAY,GACrB,KAAK,GAAG,SAAS,CAGnB;AAED,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAQpC,CAAC;AAEX,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,UAAU,CAEjE;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAMvD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAwBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA+BrD;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMzD;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAK7E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf,GAAG,OAAO,CAWV;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAQxE"}
|
package/dist/constants.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
export const ROUTE_NOT_FOUND_CODE = "ROUTE_NOT_FOUND";
|
|
2
|
+
const RESPONSE_ORIGIN = Symbol.for("@schmock/core.response-origin");
|
|
3
|
+
function setResponseOrigin(response, origin) {
|
|
4
|
+
Object.defineProperty(response, RESPONSE_ORIGIN, {
|
|
5
|
+
configurable: true,
|
|
6
|
+
value: origin,
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
function getResponseOrigin(response) {
|
|
10
|
+
const origin = Reflect.get(response, RESPONSE_ORIGIN);
|
|
11
|
+
if (typeof origin !== "object" || origin === null || !("kind" in origin)) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
if (origin.kind === "route-not-found")
|
|
15
|
+
return { kind: "route-not-found" };
|
|
16
|
+
if (origin.kind === "exception" &&
|
|
17
|
+
"error" in origin &&
|
|
18
|
+
origin.error instanceof Error) {
|
|
19
|
+
return { kind: "exception", error: origin.error };
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
export function markRouteNotFound(response) {
|
|
24
|
+
setResponseOrigin(response, { kind: "route-not-found" });
|
|
25
|
+
return response;
|
|
26
|
+
}
|
|
27
|
+
export function markResponseException(response, error) {
|
|
28
|
+
setResponseOrigin(response, { kind: "exception", error });
|
|
29
|
+
return response;
|
|
30
|
+
}
|
|
31
|
+
export function getResponseException(response) {
|
|
32
|
+
const origin = getResponseOrigin(response);
|
|
33
|
+
return origin?.kind === "exception" ? origin.error : undefined;
|
|
34
|
+
}
|
|
2
35
|
export const HTTP_METHODS = [
|
|
3
36
|
"GET",
|
|
4
37
|
"POST",
|
|
@@ -21,15 +54,103 @@ export function toHttpMethod(method) {
|
|
|
21
54
|
export function normalizePath(path) {
|
|
22
55
|
return path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
|
|
23
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* ASCII characters the URL parser percent-encodes inside a path even though
|
|
59
|
+
* they are printable. `?` and `#` are delimiters there rather than encoded, but
|
|
60
|
+
* encoding them here is what makes a route containing one reachable at all.
|
|
61
|
+
*/
|
|
62
|
+
const PATH_ENCODED_ASCII = new Set([
|
|
63
|
+
" ",
|
|
64
|
+
'"',
|
|
65
|
+
"#",
|
|
66
|
+
"<",
|
|
67
|
+
">",
|
|
68
|
+
"?",
|
|
69
|
+
"^",
|
|
70
|
+
"`",
|
|
71
|
+
"{",
|
|
72
|
+
"}",
|
|
73
|
+
]);
|
|
74
|
+
const PERCENT_TRIPLET = /^%[0-9A-Fa-f]{2}$/;
|
|
75
|
+
/** UTF-8 for U+FFFD, the URL parser's substitute for a lone surrogate. */
|
|
76
|
+
const ENCODED_REPLACEMENT_CHARACTER = "%EF%BF%BD";
|
|
77
|
+
/**
|
|
78
|
+
* Put a path into the single canonical (percent-encoded) transport form.
|
|
79
|
+
*
|
|
80
|
+
* A path reaches core either as `url.pathname` (already encoded by the URL
|
|
81
|
+
* parser) or as whatever string a `handle()` caller typed, so route paths and
|
|
82
|
+
* request paths must be encoded the same way before they can be compared.
|
|
83
|
+
* Encoding is idempotent: an existing valid `%XX` triplet is normalized to
|
|
84
|
+
* uppercase rather than re-encoded, so applying this at route-parse time and
|
|
85
|
+
* again at request time is safe.
|
|
86
|
+
*
|
|
87
|
+
* This deliberately never throws and never resolves `.`/`..` — an unreachable
|
|
88
|
+
* spelling is preferable to silently rewriting the caller's path.
|
|
89
|
+
*/
|
|
90
|
+
export function canonicalizePath(path) {
|
|
91
|
+
let result = "";
|
|
92
|
+
for (let index = 0; index < path.length;) {
|
|
93
|
+
if (path[index] === "%" &&
|
|
94
|
+
PERCENT_TRIPLET.test(path.slice(index, index + 3))) {
|
|
95
|
+
result += path.slice(index, index + 3).toUpperCase();
|
|
96
|
+
index += 3;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const codePoint = path.codePointAt(index) ?? 0;
|
|
100
|
+
const character = String.fromCodePoint(codePoint);
|
|
101
|
+
index += character.length;
|
|
102
|
+
if (codePoint <= 0x1f ||
|
|
103
|
+
codePoint > 0x7e ||
|
|
104
|
+
PATH_ENCODED_ASCII.has(character)) {
|
|
105
|
+
try {
|
|
106
|
+
result += encodeURIComponent(character);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
result += ENCODED_REPLACEMENT_CHARACTER;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
result += character;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Decode a single captured path parameter.
|
|
120
|
+
*
|
|
121
|
+
* Decoding happens AFTER segmentation, so an encoded separator (`%2F`) stays
|
|
122
|
+
* inside one parameter and reaches the generator as "/" — the same as Express.
|
|
123
|
+
* A malformed sequence (`%ZZ`) is handed back untouched rather than throwing.
|
|
124
|
+
*/
|
|
125
|
+
export function decodePathSegment(segment) {
|
|
126
|
+
try {
|
|
127
|
+
return decodeURIComponent(segment);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return segment;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Build a route key from a method and a path.
|
|
135
|
+
*
|
|
136
|
+
* `Schmock.RouteKey` requires a leading slash, so a path without one is
|
|
137
|
+
* normalized rather than rejected — callers feed spec paths and pathnames that
|
|
138
|
+
* are already absolute, and silently producing an unreachable route key would
|
|
139
|
+
* be worse than adding the slash.
|
|
140
|
+
*/
|
|
24
141
|
export function toRouteKey(method, path) {
|
|
25
|
-
|
|
26
|
-
|
|
142
|
+
// Built from the slash rather than concatenated onto it, so the template
|
|
143
|
+
// literal proves the leading slash `RouteKey` requires without an assertion.
|
|
144
|
+
const relativePath = path.startsWith("/") ? path.slice(1) : path;
|
|
145
|
+
return `${method} /${relativePath}`;
|
|
27
146
|
}
|
|
28
147
|
/**
|
|
29
148
|
* Check if a Schmock response is a route-not-found response.
|
|
30
149
|
* Used by adapters to decide whether to pass through to the real backend.
|
|
31
150
|
*/
|
|
32
151
|
export function isRouteNotFound(response) {
|
|
152
|
+
if (getResponseOrigin(response)?.kind === "route-not-found")
|
|
153
|
+
return true;
|
|
33
154
|
const { status, body } = response;
|
|
34
155
|
return (status === 404 &&
|
|
35
156
|
body !== null &&
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Describe a thrown value.
|
|
3
|
+
*
|
|
4
|
+
* Non-Error throws keep whatever they carry — a bare string, a primitive or the
|
|
5
|
+
* JSON of a plain object — because "Unknown error" is useless while debugging a
|
|
6
|
+
* mock. Only `null`/`undefined` (which carry nothing) and values that cannot be
|
|
7
|
+
* stringified safely fall back to "Unknown error"; a throwing `toString`, a
|
|
8
|
+
* throwing getter or a circular reference must never turn the error path into a
|
|
9
|
+
* second exception. Long stringifications are truncated so 500 bodies and debug
|
|
10
|
+
* logs stay bounded.
|
|
11
|
+
*/
|
|
1
12
|
export declare function errorMessage(error: unknown): string;
|
|
2
13
|
/**
|
|
3
14
|
* Base error class for all Schmock errors
|
|
@@ -20,10 +31,10 @@ export declare class RouteParseError extends SchmockError {
|
|
|
20
31
|
constructor(routeKey: string, reason: string);
|
|
21
32
|
}
|
|
22
33
|
/**
|
|
23
|
-
* Error thrown when response
|
|
34
|
+
* Error thrown when a response cannot be represented safely by transports
|
|
24
35
|
*/
|
|
25
|
-
export declare class
|
|
26
|
-
constructor(
|
|
36
|
+
export declare class InvalidResponseError extends SchmockError {
|
|
37
|
+
constructor(reason: string, context?: Record<string, unknown>);
|
|
27
38
|
}
|
|
28
39
|
/**
|
|
29
40
|
* Error thrown when a plugin fails
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AASA;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAanD;AAED;;GAEG;AACH,qBAAa,YAAa,SAAQ,KAAK;aAGnB,IAAI,EAAE,MAAM;aACZ,OAAO,CAAC,EAAE,OAAO;gBAFjC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,OAAO,YAAA;CAQpC;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;gBACtC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAOzC;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,YAAY;gBACnC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAQ7C;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,YAAY;gBACxC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;CAOlE;AAED;;GAEG;AACH,qBAAa,WAAY,SAAQ,YAAY;gBAC/B,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK;CAO7C;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,YAAY;gBACxC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAQ7C;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,YAAY;gBACzC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;CAQnE;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,YAAY;gBACzC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,OAAO;CAQ1D;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;gBACtC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAQ7D"}
|
package/dist/errors.js
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
|
+
/** Longest stringification of a non-Error throw kept in a message. */
|
|
2
|
+
const MAX_THROWN_VALUE_LENGTH = 200;
|
|
3
|
+
function truncateThrownValue(text) {
|
|
4
|
+
return text.length > MAX_THROWN_VALUE_LENGTH
|
|
5
|
+
? `${text.slice(0, MAX_THROWN_VALUE_LENGTH)}…`
|
|
6
|
+
: text;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Describe a thrown value.
|
|
10
|
+
*
|
|
11
|
+
* Non-Error throws keep whatever they carry — a bare string, a primitive or the
|
|
12
|
+
* JSON of a plain object — because "Unknown error" is useless while debugging a
|
|
13
|
+
* mock. Only `null`/`undefined` (which carry nothing) and values that cannot be
|
|
14
|
+
* stringified safely fall back to "Unknown error"; a throwing `toString`, a
|
|
15
|
+
* throwing getter or a circular reference must never turn the error path into a
|
|
16
|
+
* second exception. Long stringifications are truncated so 500 bodies and debug
|
|
17
|
+
* logs stay bounded.
|
|
18
|
+
*/
|
|
1
19
|
export function errorMessage(error) {
|
|
2
|
-
|
|
20
|
+
if (error instanceof Error)
|
|
21
|
+
return error.message;
|
|
22
|
+
if (error === null || error === undefined)
|
|
23
|
+
return "Unknown error";
|
|
24
|
+
if (typeof error === "string")
|
|
25
|
+
return truncateThrownValue(error);
|
|
26
|
+
try {
|
|
27
|
+
const text = typeof error === "object" ? JSON.stringify(error) : String(error);
|
|
28
|
+
if (typeof text !== "string" || text.length === 0)
|
|
29
|
+
return "Unknown error";
|
|
30
|
+
return truncateThrownValue(text);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return "Unknown error";
|
|
34
|
+
}
|
|
3
35
|
}
|
|
4
36
|
/**
|
|
5
37
|
* Base error class for all Schmock errors
|
|
@@ -39,12 +71,15 @@ export class RouteParseError extends SchmockError {
|
|
|
39
71
|
}
|
|
40
72
|
}
|
|
41
73
|
/**
|
|
42
|
-
* Error thrown when response
|
|
74
|
+
* Error thrown when a response cannot be represented safely by transports
|
|
43
75
|
*/
|
|
44
|
-
export class
|
|
45
|
-
constructor(
|
|
46
|
-
super(`
|
|
47
|
-
|
|
76
|
+
export class InvalidResponseError extends SchmockError {
|
|
77
|
+
constructor(reason, context = {}) {
|
|
78
|
+
super(`Invalid response: ${reason}`, "INVALID_RESPONSE", {
|
|
79
|
+
...context,
|
|
80
|
+
reason,
|
|
81
|
+
});
|
|
82
|
+
this.name = "InvalidResponseError";
|
|
48
83
|
}
|
|
49
84
|
}
|
|
50
85
|
/**
|
package/dist/helpers.d.ts
CHANGED
|
@@ -5,5 +5,14 @@ export declare function forbidden(message?: string | object): [number, object];
|
|
|
5
5
|
export declare function serverError(message?: string | object): [number, object];
|
|
6
6
|
export declare function created(body: object): [number, object];
|
|
7
7
|
export declare function noContent(): [number, null];
|
|
8
|
+
/**
|
|
9
|
+
* Slice `items` into a page envelope.
|
|
10
|
+
*
|
|
11
|
+
* `page` and `pageSize` are normalized to positive integers (falling back to
|
|
12
|
+
* page 1 and a page size of 10) so a fractional, negative, NaN or infinite
|
|
13
|
+
* option can never produce a nonsensical slice or a negative `totalPages`. The
|
|
14
|
+
* returned envelope always echoes the NORMALIZED values, so it is internally
|
|
15
|
+
* consistent with `data`.
|
|
16
|
+
*/
|
|
8
17
|
export declare function paginate<T>(items: T[], options?: Schmock.PaginateOptions): Schmock.PaginatedResponse<T>;
|
|
9
18
|
//# sourceMappingURL=helpers.d.ts.map
|
package/dist/helpers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAEA,wBAAgB,QAAQ,CACtB,OAAO,GAAE,MAAM,GAAG,MAAoB,GACrC,CAAC,MAAM,EAAE,MAAM,CAAC,CAGlB;AAED,wBAAgB,UAAU,CACxB,OAAO,GAAE,MAAM,GAAG,MAAsB,GACvC,CAAC,MAAM,EAAE,MAAM,CAAC,CAGlB;AAED,wBAAgB,YAAY,CAC1B,OAAO,GAAE,MAAM,GAAG,MAAuB,GACxC,CAAC,MAAM,EAAE,MAAM,CAAC,CAGlB;AAED,wBAAgB,SAAS,CACvB,OAAO,GAAE,MAAM,GAAG,MAAoB,GACrC,CAAC,MAAM,EAAE,MAAM,CAAC,CAGlB;AAED,wBAAgB,WAAW,CACzB,OAAO,GAAE,MAAM,GAAG,MAAgC,GACjD,CAAC,MAAM,EAAE,MAAM,CAAC,CAGlB;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAEtD;AAED,wBAAgB,SAAS,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAE1C;
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAEA,wBAAgB,QAAQ,CACtB,OAAO,GAAE,MAAM,GAAG,MAAoB,GACrC,CAAC,MAAM,EAAE,MAAM,CAAC,CAGlB;AAED,wBAAgB,UAAU,CACxB,OAAO,GAAE,MAAM,GAAG,MAAsB,GACvC,CAAC,MAAM,EAAE,MAAM,CAAC,CAGlB;AAED,wBAAgB,YAAY,CAC1B,OAAO,GAAE,MAAM,GAAG,MAAuB,GACxC,CAAC,MAAM,EAAE,MAAM,CAAC,CAGlB;AAED,wBAAgB,SAAS,CACvB,OAAO,GAAE,MAAM,GAAG,MAAoB,GACrC,CAAC,MAAM,EAAE,MAAM,CAAC,CAGlB;AAED,wBAAgB,WAAW,CACzB,OAAO,GAAE,MAAM,GAAG,MAAgC,GACjD,CAAC,MAAM,EAAE,MAAM,CAAC,CAGlB;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAEtD;AAED,wBAAgB,SAAS,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAE1C;AAWD;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EACxB,KAAK,EAAE,CAAC,EAAE,EACV,OAAO,GAAE,OAAO,CAAC,eAAoB,GACpC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAS9B"}
|
package/dist/helpers.js
CHANGED
|
@@ -25,9 +25,25 @@ export function created(body) {
|
|
|
25
25
|
export function noContent() {
|
|
26
26
|
return [204, null];
|
|
27
27
|
}
|
|
28
|
+
/** Default page size used when `pageSize` is absent or not a positive integer. */
|
|
29
|
+
const DEFAULT_PAGE_SIZE = 10;
|
|
30
|
+
function positiveInteger(value, fallback) {
|
|
31
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 1
|
|
32
|
+
? value
|
|
33
|
+
: fallback;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Slice `items` into a page envelope.
|
|
37
|
+
*
|
|
38
|
+
* `page` and `pageSize` are normalized to positive integers (falling back to
|
|
39
|
+
* page 1 and a page size of 10) so a fractional, negative, NaN or infinite
|
|
40
|
+
* option can never produce a nonsensical slice or a negative `totalPages`. The
|
|
41
|
+
* returned envelope always echoes the NORMALIZED values, so it is internally
|
|
42
|
+
* consistent with `data`.
|
|
43
|
+
*/
|
|
28
44
|
export function paginate(items, options = {}) {
|
|
29
|
-
const page = options.page
|
|
30
|
-
const pageSize = options.pageSize
|
|
45
|
+
const page = positiveInteger(options.page, 1);
|
|
46
|
+
const pageSize = positiveInteger(options.pageSize, DEFAULT_PAGE_SIZE);
|
|
31
47
|
const total = items.length;
|
|
32
48
|
const totalPages = Math.ceil(total / pageSize);
|
|
33
49
|
const start = (page - 1) * pageSize;
|
package/dist/http-helpers.d.ts
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
|
-
import type { ServerResponse } from "node:http";
|
|
2
1
|
interface RequestWithHeaders {
|
|
3
2
|
readonly headers: {
|
|
4
3
|
readonly [header: string]: string | string[] | undefined;
|
|
5
4
|
};
|
|
6
5
|
}
|
|
7
6
|
interface BodyReadable {
|
|
7
|
+
on(event: "aborted", listener: () => void): this;
|
|
8
|
+
on(event: "close", listener: () => void): this;
|
|
8
9
|
on(event: "error", listener: (error: Error) => void): this;
|
|
9
|
-
on(event: "data", listener: (chunk:
|
|
10
|
+
on(event: "data", listener: (chunk: Uint8Array) => void): this;
|
|
10
11
|
on(event: "end", listener: () => void): this;
|
|
11
12
|
destroy(error?: Error): this;
|
|
12
13
|
}
|
|
14
|
+
interface ResponseWritable {
|
|
15
|
+
writeHead(status: number, headers: Record<string, string>): this;
|
|
16
|
+
end(body?: string | Uint8Array): this;
|
|
17
|
+
}
|
|
18
|
+
export type HttpIngressErrorCode = "MALFORMED_JSON" | "PAYLOAD_TOO_LARGE";
|
|
19
|
+
/** An HTTP client error raised while collecting an incoming request body. */
|
|
20
|
+
export declare class HttpIngressError extends Error {
|
|
21
|
+
readonly status: 400 | 413;
|
|
22
|
+
readonly code: HttpIngressErrorCode;
|
|
23
|
+
constructor(status: 400 | 413, code: HttpIngressErrorCode, message: string);
|
|
24
|
+
}
|
|
13
25
|
/**
|
|
14
26
|
* Convert Node.js IncomingMessage headers to a flat Record<string, string>.
|
|
15
27
|
* Drops array-valued headers (keeps only string values).
|
|
@@ -21,17 +33,40 @@ export declare function parseNodeHeaders(req: RequestWithHeaders): Record<string
|
|
|
21
33
|
export declare function parseNodeQuery(url: URL): Record<string, string>;
|
|
22
34
|
/**
|
|
23
35
|
* Collect and parse the request body from a Node.js IncomingMessage.
|
|
24
|
-
* Returns parsed JSON
|
|
36
|
+
* Returns parsed JSON for application/json and +json media types, otherwise the
|
|
37
|
+
* raw string.
|
|
25
38
|
* Returns undefined for empty bodies.
|
|
26
39
|
* @param req - Node.js IncomingMessage
|
|
27
40
|
* @param headers - Parsed request headers
|
|
28
41
|
* @param maxBodySize - Maximum body size in bytes (default: 10 MB)
|
|
29
42
|
*/
|
|
30
43
|
export declare function collectBody(req: BodyReadable, headers: Record<string, string>, maxBodySize?: number): Promise<unknown>;
|
|
44
|
+
interface RejectedRequestReadable {
|
|
45
|
+
resume(): unknown;
|
|
46
|
+
on(event: "data", listener: (chunk: Uint8Array) => void): unknown;
|
|
47
|
+
off(event: "data", listener: (chunk: Uint8Array) => void): unknown;
|
|
48
|
+
once(event: "end" | "close", listener: () => void): unknown;
|
|
49
|
+
}
|
|
50
|
+
interface RejectedResponseWritable extends ResponseWritable {
|
|
51
|
+
readonly writableEnded: boolean;
|
|
52
|
+
write(chunk: string | Uint8Array): unknown;
|
|
53
|
+
once(event: "close", listener: () => void): unknown;
|
|
54
|
+
}
|
|
31
55
|
/**
|
|
32
56
|
* Write a Schmock Response to a Node.js ServerResponse.
|
|
33
57
|
* Serializes non-string bodies as JSON and sets content-type when missing.
|
|
34
58
|
*/
|
|
35
|
-
export declare function writeSchmockResponse(res:
|
|
59
|
+
export declare function writeSchmockResponse(res: ResponseWritable, response: Schmock.Response, extraHeaders?: Record<string, string>): void;
|
|
60
|
+
/**
|
|
61
|
+
* Write a rejection (e.g. 413) while the client may still be uploading.
|
|
62
|
+
*
|
|
63
|
+
* Ending the response immediately makes Node tear the socket down while
|
|
64
|
+
* request bytes are in flight; the resulting TCP reset discards the
|
|
65
|
+
* already-written error from the client's receive buffer, so the client
|
|
66
|
+
* observes ECONNRESET instead of the response. Instead the response body is
|
|
67
|
+
* flushed right away and the end is deferred — a lingering close — until the
|
|
68
|
+
* request finishes, goes idle, or exhausts the grace cap.
|
|
69
|
+
*/
|
|
70
|
+
export declare function writeRejectedSchmockResponse(req: RejectedRequestReadable, res: RejectedResponseWritable, response: Schmock.Response, extraHeaders?: Record<string, string>): void;
|
|
36
71
|
export {};
|
|
37
72
|
//# sourceMappingURL=http-helpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-helpers.d.ts","sourceRoot":"","sources":["../src/http-helpers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"http-helpers.d.ts","sourceRoot":"","sources":["../src/http-helpers.ts"],"names":[],"mappings":"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"}
|