@schmock/core 2.2.3 → 2.3.1
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/binary.d.ts +8 -0
- package/dist/binary.d.ts.map +1 -0
- package/dist/binary.js +9 -0
- package/dist/builder.d.ts +31 -10
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +804 -203
- 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 +52 -5
- package/dist/http-helpers.d.ts.map +1 -1
- package/dist/http-helpers.js +153 -37
- package/dist/index.d.ts +381 -60
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -3
- package/dist/interceptor.d.ts +11 -1
- package/dist/interceptor.d.ts.map +1 -1
- package/dist/interceptor.js +354 -156
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +12 -3
- package/dist/plugin-pipeline.d.ts +11 -4
- package/dist/plugin-pipeline.d.ts.map +1 -1
- package/dist/plugin-pipeline.js +117 -39
- 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 +85 -18
- 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 -8
- package/src/audit-core-builder.test.ts +0 -218
- package/src/audit-response-guard.test.ts +0 -38
- package/src/builder.test.ts +0 -289
- package/src/builder.ts +0 -701
- 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.ts +0 -113
- package/src/index.ts +0 -151
- package/src/interceptor.test.ts +0 -291
- package/src/interceptor.ts +0 -320
- package/src/namespace.test.ts +0 -274
- package/src/parser.property.test.ts +0 -594
- package/src/parser.test.ts +0 -148
- package/src/parser.ts +0 -64
- package/src/plugin-pipeline.ts +0 -103
- package/src/plugin-system.test.ts +0 -602
- package/src/response-parser.ts +0 -75
- 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 -165
- package/src/smart-defaults.test.ts +0 -361
- package/src/steps/async-support.steps.ts +0 -400
- package/src/steps/audit-core-builder.steps.ts +0 -188
- package/src/steps/audit-onerror-tuple.steps.ts +0 -99
- 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 -345
- package/src/steps/fluent-api.steps.ts +0 -255
- package/src/steps/http-methods.steps.ts +0 -331
- package/src/steps/interceptor.steps.ts +0 -260
- package/src/steps/lifecycle-events.steps.ts +0 -142
- package/src/steps/performance-reliability.steps.ts +0 -423
- package/src/steps/plugin-integration.steps.ts +0 -280
- package/src/steps/request-history.steps.ts +0 -276
- 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 -233
- package/src/steps/state-concurrency.steps.ts +0 -739
- package/src/steps/stateful-workflows.steps.ts +0 -353
- package/src/types.ts +0 -36
package/src/factory.test.ts
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { schmock } from "./index";
|
|
3
|
-
|
|
4
|
-
describe("schmock factory function", () => {
|
|
5
|
-
describe("factory behavior", () => {
|
|
6
|
-
it("creates callable mock instance with no config", () => {
|
|
7
|
-
const mock = schmock();
|
|
8
|
-
|
|
9
|
-
expect(typeof mock).toBe("function");
|
|
10
|
-
expect(typeof mock.handle).toBe("function");
|
|
11
|
-
expect(typeof mock.pipe).toBe("function");
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("creates callable mock instance with config", () => {
|
|
15
|
-
const config = { debug: true, namespace: "/api" };
|
|
16
|
-
const mock = schmock(config);
|
|
17
|
-
|
|
18
|
-
expect(typeof mock).toBe("function");
|
|
19
|
-
expect(typeof mock.handle).toBe("function");
|
|
20
|
-
expect(typeof mock.pipe).toBe("function");
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("supports method chaining from factory call", () => {
|
|
24
|
-
const mock = schmock();
|
|
25
|
-
const result = mock("GET /test", "response");
|
|
26
|
-
|
|
27
|
-
expect(result).toBe(mock); // Should return same instance for chaining
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("supports plugin chaining", () => {
|
|
31
|
-
const mock = schmock();
|
|
32
|
-
const plugin = {
|
|
33
|
-
name: "test",
|
|
34
|
-
process: (ctx: any, res: any) => ({ context: ctx, response: res }),
|
|
35
|
-
};
|
|
36
|
-
const result = mock.pipe(plugin);
|
|
37
|
-
|
|
38
|
-
expect(result).toBe(mock); // Should return same instance for chaining
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe("callable instance behavior", () => {
|
|
43
|
-
it("defines routes when called as function", async () => {
|
|
44
|
-
const mock = schmock();
|
|
45
|
-
mock("GET /test", "hello");
|
|
46
|
-
|
|
47
|
-
const response = await mock.handle("GET", "/test");
|
|
48
|
-
expect(response.body).toBe("hello");
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("allows method chaining after route definition", async () => {
|
|
52
|
-
const mock = schmock();
|
|
53
|
-
const plugin = {
|
|
54
|
-
name: "test",
|
|
55
|
-
process: (ctx: any, res: any) => ({ context: ctx, response: res }),
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
mock("GET /test", "hello").pipe(plugin);
|
|
59
|
-
|
|
60
|
-
const response = await mock.handle("GET", "/test");
|
|
61
|
-
expect(response.body).toBe("hello");
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it("passes config to route definition", async () => {
|
|
65
|
-
const mock = schmock();
|
|
66
|
-
mock("GET /test", { data: "test" }, { contentType: "text/plain" });
|
|
67
|
-
|
|
68
|
-
const response = await mock.handle("GET", "/test");
|
|
69
|
-
expect(response.headers["content-type"]).toBe("text/plain");
|
|
70
|
-
expect(response.body).toBe('{"data":"test"}'); // Stringified because contentType is text/plain
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
describe("binding and method preservation", () => {
|
|
75
|
-
it("preserves handle method binding", async () => {
|
|
76
|
-
const mock = schmock();
|
|
77
|
-
mock("GET /test", "response");
|
|
78
|
-
|
|
79
|
-
const handleMethod = mock.handle;
|
|
80
|
-
const response = await handleMethod("GET", "/test");
|
|
81
|
-
|
|
82
|
-
expect(response.body).toBe("response");
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("allows destructuring of methods", async () => {
|
|
86
|
-
const mock = schmock();
|
|
87
|
-
mock("GET /test", "response");
|
|
88
|
-
|
|
89
|
-
const { handle } = mock;
|
|
90
|
-
const response = await handle("GET", "/test");
|
|
91
|
-
|
|
92
|
-
expect(response.body).toBe("response");
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("pipes maintain chain references", () => {
|
|
96
|
-
const mock = schmock();
|
|
97
|
-
const plugin1 = {
|
|
98
|
-
name: "plugin1",
|
|
99
|
-
process: (ctx: any, res: any) => ({ context: ctx, response: res }),
|
|
100
|
-
};
|
|
101
|
-
const plugin2 = {
|
|
102
|
-
name: "plugin2",
|
|
103
|
-
process: (ctx: any, res: any) => ({ context: ctx, response: res }),
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const chain = mock.pipe(plugin1).pipe(plugin2);
|
|
107
|
-
|
|
108
|
-
expect(chain).toBe(mock);
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
describe("type compatibility", () => {
|
|
113
|
-
it("works with TypeScript function signature", () => {
|
|
114
|
-
// Test that the factory function matches expected TypeScript types
|
|
115
|
-
const mock = schmock({ debug: false });
|
|
116
|
-
mock("GET /users", () => [{ id: 1 }], {
|
|
117
|
-
contentType: "application/json",
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
expect(typeof mock).toBe("function");
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it("handles optional config parameter", () => {
|
|
124
|
-
const mock1 = schmock();
|
|
125
|
-
const mock2 = schmock({});
|
|
126
|
-
const mock3 = schmock({ debug: true });
|
|
127
|
-
|
|
128
|
-
expect(typeof mock1).toBe("function");
|
|
129
|
-
expect(typeof mock2).toBe("function");
|
|
130
|
-
expect(typeof mock3).toBe("function");
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
});
|
package/src/helpers.test.ts
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
badRequest,
|
|
4
|
-
created,
|
|
5
|
-
forbidden,
|
|
6
|
-
noContent,
|
|
7
|
-
notFound,
|
|
8
|
-
paginate,
|
|
9
|
-
serverError,
|
|
10
|
-
unauthorized,
|
|
11
|
-
} from "./helpers.js";
|
|
12
|
-
|
|
13
|
-
describe("notFound", () => {
|
|
14
|
-
it("returns 404 with default message", () => {
|
|
15
|
-
expect(notFound()).toEqual([404, { message: "Not Found" }]);
|
|
16
|
-
});
|
|
17
|
-
it("returns 404 with custom string message", () => {
|
|
18
|
-
expect(notFound("User not found")).toEqual([
|
|
19
|
-
404,
|
|
20
|
-
{ message: "User not found" },
|
|
21
|
-
]);
|
|
22
|
-
});
|
|
23
|
-
it("returns 404 with custom object", () => {
|
|
24
|
-
expect(notFound({ code: "NOT_FOUND", detail: "gone" })).toEqual([
|
|
25
|
-
404,
|
|
26
|
-
{ code: "NOT_FOUND", detail: "gone" },
|
|
27
|
-
]);
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
describe("badRequest", () => {
|
|
32
|
-
it("returns 400 with default message", () => {
|
|
33
|
-
expect(badRequest()).toEqual([400, { message: "Bad Request" }]);
|
|
34
|
-
});
|
|
35
|
-
it("returns 400 with custom string", () => {
|
|
36
|
-
expect(badRequest("Invalid email")).toEqual([
|
|
37
|
-
400,
|
|
38
|
-
{ message: "Invalid email" },
|
|
39
|
-
]);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe("unauthorized", () => {
|
|
44
|
-
it("returns 401 with default message", () => {
|
|
45
|
-
expect(unauthorized()).toEqual([401, { message: "Unauthorized" }]);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
describe("forbidden", () => {
|
|
50
|
-
it("returns 403 with default message", () => {
|
|
51
|
-
expect(forbidden()).toEqual([403, { message: "Forbidden" }]);
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
describe("serverError", () => {
|
|
56
|
-
it("returns 500 with default message", () => {
|
|
57
|
-
expect(serverError()).toEqual([500, { message: "Internal Server Error" }]);
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe("created", () => {
|
|
62
|
-
it("returns 201 with body", () => {
|
|
63
|
-
expect(created({ id: 1, name: "John" })).toEqual([
|
|
64
|
-
201,
|
|
65
|
-
{ id: 1, name: "John" },
|
|
66
|
-
]);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe("noContent", () => {
|
|
71
|
-
it("returns 204 with null body", () => {
|
|
72
|
-
expect(noContent()).toEqual([204, null]);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
describe("paginate", () => {
|
|
77
|
-
const items = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }];
|
|
78
|
-
|
|
79
|
-
it("returns first page with default pageSize", () => {
|
|
80
|
-
const result = paginate(items);
|
|
81
|
-
expect(result).toEqual({
|
|
82
|
-
data: items,
|
|
83
|
-
page: 1,
|
|
84
|
-
pageSize: 10,
|
|
85
|
-
total: 5,
|
|
86
|
-
totalPages: 1,
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("paginates correctly with custom options", () => {
|
|
91
|
-
const result = paginate(items, { page: 2, pageSize: 2 });
|
|
92
|
-
expect(result).toEqual({
|
|
93
|
-
data: [{ id: 3 }, { id: 4 }],
|
|
94
|
-
page: 2,
|
|
95
|
-
pageSize: 2,
|
|
96
|
-
total: 5,
|
|
97
|
-
totalPages: 3,
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("returns empty data for page beyond range", () => {
|
|
102
|
-
const result = paginate(items, { page: 10, pageSize: 2 });
|
|
103
|
-
expect(result.data).toEqual([]);
|
|
104
|
-
expect(result.total).toBe(5);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("page=0 falls back to page 1 (falsy default)", () => {
|
|
108
|
-
const result = paginate(items, { page: 0, pageSize: 2 });
|
|
109
|
-
// page=0 is falsy, so options.page || 1 => 1
|
|
110
|
-
expect(result.page).toBe(1);
|
|
111
|
-
expect(result.data).toEqual([{ id: 1 }, { id: 2 }]);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("pageSize=0 falls back to default 10 (falsy default)", () => {
|
|
115
|
-
const result = paginate(items, { pageSize: 0 });
|
|
116
|
-
// pageSize=0 is falsy, so options.pageSize || 10 => 10
|
|
117
|
-
expect(result.pageSize).toBe(10);
|
|
118
|
-
expect(result.data).toEqual(items);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it("handles empty array", () => {
|
|
122
|
-
const result = paginate([], { page: 1, pageSize: 5 });
|
|
123
|
-
expect(result).toEqual({
|
|
124
|
-
data: [],
|
|
125
|
-
page: 1,
|
|
126
|
-
pageSize: 5,
|
|
127
|
-
total: 0,
|
|
128
|
-
totalPages: 0,
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it("negative page produces empty data (start index < 0)", () => {
|
|
133
|
-
const result = paginate(items, { page: -1, pageSize: 2 });
|
|
134
|
-
// (page - 1) * pageSize = (-1 - 1) * 2 = -4
|
|
135
|
-
// items.slice(-4, -2) => items from index 1 to 3
|
|
136
|
-
expect(result.page).toBe(-1);
|
|
137
|
-
expect(result.pageSize).toBe(2);
|
|
138
|
-
expect(result.total).toBe(5);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it("negative pageSize returns empty data", () => {
|
|
142
|
-
const result = paginate(items, { page: 1, pageSize: -5 });
|
|
143
|
-
// start = 0, end = 0 + (-5) = -5 => items.slice(0, -5) => []
|
|
144
|
-
expect(result.data).toEqual([]);
|
|
145
|
-
expect(result.pageSize).toBe(-5);
|
|
146
|
-
});
|
|
147
|
-
});
|
package/src/helpers.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/// <reference path="../schmock.d.ts" />
|
|
2
|
-
|
|
3
|
-
export function notFound(
|
|
4
|
-
message: string | object = "Not Found",
|
|
5
|
-
): [number, object] {
|
|
6
|
-
const body = typeof message === "string" ? { message } : message;
|
|
7
|
-
return [404, body];
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function badRequest(
|
|
11
|
-
message: string | object = "Bad Request",
|
|
12
|
-
): [number, object] {
|
|
13
|
-
const body = typeof message === "string" ? { message } : message;
|
|
14
|
-
return [400, body];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function unauthorized(
|
|
18
|
-
message: string | object = "Unauthorized",
|
|
19
|
-
): [number, object] {
|
|
20
|
-
const body = typeof message === "string" ? { message } : message;
|
|
21
|
-
return [401, body];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function forbidden(
|
|
25
|
-
message: string | object = "Forbidden",
|
|
26
|
-
): [number, object] {
|
|
27
|
-
const body = typeof message === "string" ? { message } : message;
|
|
28
|
-
return [403, body];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function serverError(
|
|
32
|
-
message: string | object = "Internal Server Error",
|
|
33
|
-
): [number, object] {
|
|
34
|
-
const body = typeof message === "string" ? { message } : message;
|
|
35
|
-
return [500, body];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function created(body: object): [number, object] {
|
|
39
|
-
return [201, body];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function noContent(): [number, null] {
|
|
43
|
-
return [204, null];
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function paginate<T>(
|
|
47
|
-
items: T[],
|
|
48
|
-
options: Schmock.PaginateOptions = {},
|
|
49
|
-
): Schmock.PaginatedResponse<T> {
|
|
50
|
-
const page = options.page || 1;
|
|
51
|
-
const pageSize = options.pageSize || 10;
|
|
52
|
-
const total = items.length;
|
|
53
|
-
const totalPages = Math.ceil(total / pageSize);
|
|
54
|
-
const start = (page - 1) * pageSize;
|
|
55
|
-
const end = start + pageSize;
|
|
56
|
-
const data = items.slice(start, end);
|
|
57
|
-
return { data, page, pageSize, total, totalPages };
|
|
58
|
-
}
|
package/src/http-helpers.ts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Convert Node.js IncomingMessage headers to a flat Record<string, string>.
|
|
5
|
-
* Drops array-valued headers (keeps only string values).
|
|
6
|
-
*/
|
|
7
|
-
export function parseNodeHeaders(req: IncomingMessage): Record<string, string> {
|
|
8
|
-
const headers: Record<string, string> = {};
|
|
9
|
-
for (const [key, value] of Object.entries(req.headers)) {
|
|
10
|
-
if (typeof value === "string") {
|
|
11
|
-
headers[key] = value;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
return headers;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Extract query parameters from a URL as a flat Record<string, string>.
|
|
19
|
-
*/
|
|
20
|
-
export function parseNodeQuery(url: URL): Record<string, string> {
|
|
21
|
-
const query: Record<string, string> = {};
|
|
22
|
-
url.searchParams.forEach((value, key) => {
|
|
23
|
-
query[key] = value;
|
|
24
|
-
});
|
|
25
|
-
return query;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Default body size limit: 10 MB */
|
|
29
|
-
const DEFAULT_MAX_BODY_SIZE = 10 * 1024 * 1024;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Collect and parse the request body from a Node.js IncomingMessage.
|
|
33
|
-
* Returns parsed JSON if content-type includes "json", otherwise the raw string.
|
|
34
|
-
* Returns undefined for empty bodies.
|
|
35
|
-
* @param req - Node.js IncomingMessage
|
|
36
|
-
* @param headers - Parsed request headers
|
|
37
|
-
* @param maxBodySize - Maximum body size in bytes (default: 10 MB)
|
|
38
|
-
*/
|
|
39
|
-
export function collectBody(
|
|
40
|
-
req: IncomingMessage,
|
|
41
|
-
headers: Record<string, string>,
|
|
42
|
-
maxBodySize = DEFAULT_MAX_BODY_SIZE,
|
|
43
|
-
): Promise<unknown> {
|
|
44
|
-
return new Promise((resolve, reject) => {
|
|
45
|
-
const chunks: Buffer[] = [];
|
|
46
|
-
let totalSize = 0;
|
|
47
|
-
|
|
48
|
-
req.on("error", reject);
|
|
49
|
-
|
|
50
|
-
req.on("data", (chunk: Buffer) => {
|
|
51
|
-
totalSize += chunk.length;
|
|
52
|
-
if (totalSize > maxBodySize) {
|
|
53
|
-
req.destroy();
|
|
54
|
-
reject(
|
|
55
|
-
Object.assign(new Error("Request body too large"), { status: 413 }),
|
|
56
|
-
);
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
chunks.push(chunk);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
req.on("end", () => {
|
|
63
|
-
const raw = Buffer.concat(chunks).toString();
|
|
64
|
-
if (!raw) {
|
|
65
|
-
resolve(undefined);
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
const contentType = headers["content-type"] ?? "";
|
|
69
|
-
if (contentType.includes("json")) {
|
|
70
|
-
try {
|
|
71
|
-
resolve(JSON.parse(raw));
|
|
72
|
-
} catch {
|
|
73
|
-
resolve(raw);
|
|
74
|
-
}
|
|
75
|
-
} else {
|
|
76
|
-
resolve(raw);
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Write a Schmock Response to a Node.js ServerResponse.
|
|
84
|
-
* Serializes non-string bodies as JSON and sets content-type when missing.
|
|
85
|
-
*/
|
|
86
|
-
export function writeSchmockResponse(
|
|
87
|
-
res: ServerResponse,
|
|
88
|
-
response: Schmock.Response,
|
|
89
|
-
extraHeaders?: Record<string, string>,
|
|
90
|
-
): void {
|
|
91
|
-
const responseHeaders: Record<string, string> = {
|
|
92
|
-
...response.headers,
|
|
93
|
-
...extraHeaders,
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
if (
|
|
97
|
-
!responseHeaders["content-type"] &&
|
|
98
|
-
response.body !== undefined &&
|
|
99
|
-
typeof response.body !== "string"
|
|
100
|
-
) {
|
|
101
|
-
responseHeaders["content-type"] = "application/json";
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const responseBody =
|
|
105
|
-
response.body === undefined
|
|
106
|
-
? undefined
|
|
107
|
-
: typeof response.body === "string"
|
|
108
|
-
? response.body
|
|
109
|
-
: JSON.stringify(response.body);
|
|
110
|
-
|
|
111
|
-
res.writeHead(response.status, responseHeaders);
|
|
112
|
-
res.end(responseBody);
|
|
113
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { CallableMockInstance } from "./builder.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Create a new Schmock mock instance with callable API.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```typescript
|
|
8
|
-
* // New callable API (default)
|
|
9
|
-
* const mock = schmock({ debug: true })
|
|
10
|
-
* mock('GET /users', () => [{ id: 1, name: 'John' }])
|
|
11
|
-
* .pipe(authPlugin())
|
|
12
|
-
*
|
|
13
|
-
* const response = await mock.handle('GET', '/users')
|
|
14
|
-
* ```
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```typescript
|
|
18
|
-
* // Simple usage with defaults
|
|
19
|
-
* const mock = schmock()
|
|
20
|
-
* mock('GET /users', [{ id: 1, name: 'John' }])
|
|
21
|
-
* ```
|
|
22
|
-
*
|
|
23
|
-
* @param config Optional global configuration
|
|
24
|
-
* @returns A callable mock instance
|
|
25
|
-
*/
|
|
26
|
-
export function schmock(
|
|
27
|
-
config?: Schmock.GlobalConfig,
|
|
28
|
-
): Schmock.CallableMockInstance {
|
|
29
|
-
// Always use new callable API
|
|
30
|
-
const instance = new CallableMockInstance(config || {});
|
|
31
|
-
|
|
32
|
-
// Callable proxy: a function with attached methods
|
|
33
|
-
const callableInstance: Schmock.CallableMockInstance = Object.assign(
|
|
34
|
-
(
|
|
35
|
-
route: Schmock.RouteKey,
|
|
36
|
-
generator: Schmock.Generator,
|
|
37
|
-
routeConfig: Schmock.RouteConfig = {},
|
|
38
|
-
) => {
|
|
39
|
-
instance.defineRoute(route, generator, routeConfig);
|
|
40
|
-
return callableInstance;
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
pipe: (plugin: Schmock.Plugin) => {
|
|
44
|
-
instance.pipe(plugin);
|
|
45
|
-
return callableInstance;
|
|
46
|
-
},
|
|
47
|
-
handle: instance.handle.bind(instance),
|
|
48
|
-
history: instance.history.bind(instance),
|
|
49
|
-
called: instance.called.bind(instance),
|
|
50
|
-
callCount: instance.callCount.bind(instance),
|
|
51
|
-
lastRequest: instance.lastRequest.bind(instance),
|
|
52
|
-
reset: instance.reset.bind(instance),
|
|
53
|
-
resetHistory: instance.resetHistory.bind(instance),
|
|
54
|
-
resetState: instance.resetState.bind(instance),
|
|
55
|
-
on<E extends Schmock.SchmockEvent>(
|
|
56
|
-
event: E,
|
|
57
|
-
listener: (data: Schmock.SchmockEventMap[E]) => void,
|
|
58
|
-
) {
|
|
59
|
-
instance.on(event, listener);
|
|
60
|
-
return callableInstance;
|
|
61
|
-
},
|
|
62
|
-
off<E extends Schmock.SchmockEvent>(
|
|
63
|
-
event: E,
|
|
64
|
-
listener: (data: Schmock.SchmockEventMap[E]) => void,
|
|
65
|
-
) {
|
|
66
|
-
instance.off(event, listener);
|
|
67
|
-
return callableInstance;
|
|
68
|
-
},
|
|
69
|
-
getRoutes: instance.getRoutes.bind(instance),
|
|
70
|
-
getState: instance.getState.bind(instance),
|
|
71
|
-
listen: instance.listen.bind(instance),
|
|
72
|
-
close: instance.close.bind(instance),
|
|
73
|
-
intercept: (options?: Schmock.InterceptOptions) =>
|
|
74
|
-
instance.intercept(options),
|
|
75
|
-
},
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
instance.setCallableRef(callableInstance);
|
|
79
|
-
|
|
80
|
-
return callableInstance;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Re-export constants and utilities
|
|
84
|
-
export {
|
|
85
|
-
HTTP_METHODS,
|
|
86
|
-
isHttpMethod,
|
|
87
|
-
isRouteNotFound,
|
|
88
|
-
isStatusTuple,
|
|
89
|
-
ROUTE_NOT_FOUND_CODE,
|
|
90
|
-
toHttpMethod,
|
|
91
|
-
toRouteKey,
|
|
92
|
-
} from "./constants.js";
|
|
93
|
-
// Re-export errors
|
|
94
|
-
export {
|
|
95
|
-
PluginError,
|
|
96
|
-
ResourceLimitError,
|
|
97
|
-
ResponseGenerationError,
|
|
98
|
-
RouteDefinitionError,
|
|
99
|
-
RouteNotFoundError,
|
|
100
|
-
RouteParseError,
|
|
101
|
-
SchemaGenerationError,
|
|
102
|
-
SchemaValidationError,
|
|
103
|
-
SchmockError,
|
|
104
|
-
} from "./errors.js";
|
|
105
|
-
// Re-export response helpers
|
|
106
|
-
export {
|
|
107
|
-
badRequest,
|
|
108
|
-
created,
|
|
109
|
-
forbidden,
|
|
110
|
-
noContent,
|
|
111
|
-
notFound,
|
|
112
|
-
paginate,
|
|
113
|
-
serverError,
|
|
114
|
-
unauthorized,
|
|
115
|
-
} from "./helpers.js";
|
|
116
|
-
// Re-export HTTP server helpers
|
|
117
|
-
export {
|
|
118
|
-
collectBody,
|
|
119
|
-
parseNodeHeaders,
|
|
120
|
-
parseNodeQuery,
|
|
121
|
-
writeSchmockResponse,
|
|
122
|
-
} from "./http-helpers.js";
|
|
123
|
-
// Re-export types
|
|
124
|
-
// Re-export interceptor
|
|
125
|
-
export { createFetchInterceptor } from "./interceptor.js";
|
|
126
|
-
// Re-export types
|
|
127
|
-
export type {
|
|
128
|
-
AdapterRequest,
|
|
129
|
-
AdapterResponse,
|
|
130
|
-
CallableMockInstance,
|
|
131
|
-
Generator,
|
|
132
|
-
GeneratorFunction,
|
|
133
|
-
GlobalConfig,
|
|
134
|
-
HttpMethod,
|
|
135
|
-
InterceptHandle,
|
|
136
|
-
InterceptOptions,
|
|
137
|
-
Plugin,
|
|
138
|
-
PluginContext,
|
|
139
|
-
PluginResult,
|
|
140
|
-
RequestContext,
|
|
141
|
-
RequestOptions,
|
|
142
|
-
RequestRecord,
|
|
143
|
-
Response,
|
|
144
|
-
ResponseBody,
|
|
145
|
-
ResponseResult,
|
|
146
|
-
RouteConfig,
|
|
147
|
-
RouteInfo,
|
|
148
|
-
RouteKey,
|
|
149
|
-
ServerInfo,
|
|
150
|
-
StaticData,
|
|
151
|
-
} from "./types.js";
|