@warp-drive/holodeck 0.0.1-beta.0 → 0.0.3-beta.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/README.md +9 -9
- package/declarations/index.d.ts +52 -0
- package/declarations/mock.d.ts +65 -0
- package/dist/index.js +184 -24
- package/dist/mock.js +89 -7
- package/package.json +32 -18
- package/server/bun-worker.js +3 -0
- package/server/bun.js +393 -0
- package/server/compat-shim.js +95 -0
- package/server/ensure-cert.js +4 -0
- package/server/index.js +24 -421
- package/server/node-compat-start.js +12 -0
- package/server/node-worker.js +3 -0
- package/server/node.js +401 -0
- package/server/utils.js +128 -0
- package/dist/index.js.map +0 -1
- package/dist/mock.js.map +0 -1
- package/server/start-node.js +0 -3
- package/server/worker.js +0 -3
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
<p align="center">
|
|
2
2
|
<img
|
|
3
3
|
class="project-logo"
|
|
4
|
-
src="./logos/
|
|
4
|
+
src="./logos/warp-drive-logo-dark.svg#gh-light-mode-only"
|
|
5
5
|
alt="WarpDrive"
|
|
6
|
-
width="
|
|
6
|
+
width="200px"
|
|
7
7
|
title="WarpDrive" />
|
|
8
8
|
<img
|
|
9
9
|
class="project-logo"
|
|
10
|
-
src="./logos/
|
|
10
|
+
src="./logos/warp-drive-logo-gold.svg#gh-dark-mode-only"
|
|
11
11
|
alt="WarpDrive"
|
|
12
|
-
width="
|
|
12
|
+
width="200px"
|
|
13
13
|
title="WarpDrive" />
|
|
14
14
|
</p>
|
|
15
15
|
|
|
@@ -163,10 +163,10 @@ const MockHost = `https://${window.location.hostname}:${Number(window.location.p
|
|
|
163
163
|
setConfig({ host: MockHost });
|
|
164
164
|
|
|
165
165
|
QUnit.hooks.beforeEach(function (assert) {
|
|
166
|
-
setTestId(assert.test.testId);
|
|
166
|
+
setTestId(this, assert.test.testId);
|
|
167
167
|
});
|
|
168
168
|
QUnit.hooks.afterEach(function (assert) {
|
|
169
|
-
setTestId(null);
|
|
169
|
+
setTestId(this, null);
|
|
170
170
|
});
|
|
171
171
|
```
|
|
172
172
|
|
|
@@ -225,7 +225,7 @@ holodeck can be launched and cleaned up using the lifecycle hooks in the launch
|
|
|
225
225
|
for diagnostic in `diagnostic.js`:
|
|
226
226
|
|
|
227
227
|
```ts
|
|
228
|
-
import launch from '@warp-drive/diagnostic/server
|
|
228
|
+
import { launch } from '@warp-drive/diagnostic/server';
|
|
229
229
|
import holodeck from '@warp-drive/holodeck';
|
|
230
230
|
|
|
231
231
|
await launch({
|
|
@@ -249,7 +249,7 @@ await launch({
|
|
|
249
249
|
img.project-logo {
|
|
250
250
|
padding: 0 5em 1em 5em;
|
|
251
251
|
width: 100px;
|
|
252
|
-
border-bottom: 2px solid #
|
|
252
|
+
border-bottom: 2px solid #bbb;
|
|
253
253
|
margin: 0 auto;
|
|
254
254
|
display: block;
|
|
255
255
|
}
|
|
@@ -265,7 +265,7 @@ await launch({
|
|
|
265
265
|
display: inline-block;
|
|
266
266
|
padding: .2rem 0;
|
|
267
267
|
color: #000;
|
|
268
|
-
border-bottom: 3px solid #
|
|
268
|
+
border-bottom: 3px solid #bbb;
|
|
269
269
|
}
|
|
270
270
|
|
|
271
271
|
details > details {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Handler, NextFn } from "@warp-drive/core/request";
|
|
2
|
+
import type { RequestContext, StructuredDataDocument } from "@warp-drive/core/types/request";
|
|
3
|
+
import type { Store } from "@warp-drive/legacy/store";
|
|
4
|
+
import type { ScaffoldGenerator } from "./mock.js";
|
|
5
|
+
/**
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
export declare function setConfig({ host }: {
|
|
9
|
+
host: string;
|
|
10
|
+
}): void;
|
|
11
|
+
/**
|
|
12
|
+
* @public
|
|
13
|
+
*/
|
|
14
|
+
export declare function setTestId(context: object, str: string | null): void;
|
|
15
|
+
/**
|
|
16
|
+
* @public
|
|
17
|
+
*/
|
|
18
|
+
export declare function setIsRecording(value: boolean): void;
|
|
19
|
+
/**
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
22
|
+
export declare function getIsRecording(): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* A request handler that intercepts requests and routes them through
|
|
25
|
+
* the Holodeck mock server.
|
|
26
|
+
*
|
|
27
|
+
* This handler modifies the request URL to include test identifiers
|
|
28
|
+
* and manages request counts for accurate mocking.
|
|
29
|
+
*
|
|
30
|
+
* Requires that the test context be configured with a testId using `setTestId`.
|
|
31
|
+
*
|
|
32
|
+
* @param owner - the test context object used to retrieve the test ID.
|
|
33
|
+
*/
|
|
34
|
+
export declare class MockServerHandler implements Handler {
|
|
35
|
+
owner: object;
|
|
36
|
+
constructor(owner: object);
|
|
37
|
+
request<T>(context: RequestContext, next: NextFn<T>): Promise<StructuredDataDocument<T>>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Creates an adapterFor function that wraps the provided adapterFor function
|
|
41
|
+
* to override the adapter's _fetchRequest method to route requests through
|
|
42
|
+
* the Holodeck mock server.
|
|
43
|
+
*
|
|
44
|
+
* @param owner - The test context object used to retrieve the test ID.
|
|
45
|
+
*/
|
|
46
|
+
export declare function installAdapterFor(owner: object, store: Store): void;
|
|
47
|
+
/**
|
|
48
|
+
* Mock a request by sending the scaffold to the mock server.
|
|
49
|
+
*
|
|
50
|
+
* @public
|
|
51
|
+
*/
|
|
52
|
+
export declare function mock(owner: object, generate: ScaffoldGenerator, isRecording?: boolean): Promise<void>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @public
|
|
3
|
+
*/
|
|
4
|
+
export interface Scaffold {
|
|
5
|
+
status: number;
|
|
6
|
+
statusText?: string;
|
|
7
|
+
headers: Record<string, string>;
|
|
8
|
+
body: Record<string, string> | string | null;
|
|
9
|
+
method: string;
|
|
10
|
+
url: string;
|
|
11
|
+
response: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
export type ScaffoldGenerator = () => Scaffold;
|
|
17
|
+
/**
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export type ResponseGenerator = () => Record<string, unknown>;
|
|
21
|
+
/**
|
|
22
|
+
* Sets up Mocking for a GET request on the mock server
|
|
23
|
+
* for the supplied url.
|
|
24
|
+
*
|
|
25
|
+
* The response body is generated by the supplied response function.
|
|
26
|
+
*
|
|
27
|
+
* Available options:
|
|
28
|
+
* - status: the status code to return (default: 200)
|
|
29
|
+
* - headers: the headers to return (default: {})
|
|
30
|
+
* - body: the body to match against for the request (default: null)
|
|
31
|
+
* - RECORD: whether to record the request (default: false)
|
|
32
|
+
*
|
|
33
|
+
* @param url the url to mock, relative to the mock server host (e.g. `users/1`)
|
|
34
|
+
* @param response a function which generates the response to return
|
|
35
|
+
* @param options status, headers for the response, body to match against for the request, and whether to record the request
|
|
36
|
+
* @return
|
|
37
|
+
*/
|
|
38
|
+
export declare function GET(owner: object, url: string, response: ResponseGenerator, options?: Partial<Omit<Scaffold, "response" | "url" | "method">> & {
|
|
39
|
+
RECORD?: boolean;
|
|
40
|
+
}): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Mock a POST request
|
|
43
|
+
*/
|
|
44
|
+
export declare function POST(owner: object, url: string, response: ResponseGenerator, options?: Partial<Omit<Scaffold, "response" | "url" | "method">> & {
|
|
45
|
+
RECORD?: boolean;
|
|
46
|
+
}): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* mock a PUT request
|
|
49
|
+
*/
|
|
50
|
+
export declare function PUT(owner: object, url: string, response: ResponseGenerator, options?: Partial<Omit<Scaffold, "response" | "url" | "method">> & {
|
|
51
|
+
RECORD?: boolean;
|
|
52
|
+
}): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* mock a PATCH request
|
|
55
|
+
*
|
|
56
|
+
*/
|
|
57
|
+
export declare function PATCH(owner: object, url: string, response: ResponseGenerator, options?: Partial<Omit<Scaffold, "response" | "url" | "method">> & {
|
|
58
|
+
RECORD?: boolean;
|
|
59
|
+
}): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* mock a DELETE request
|
|
62
|
+
*/
|
|
63
|
+
export declare function DELETE(owner: object, url: string, response: ResponseGenerator, options?: Partial<Omit<Scaffold, "response" | "url" | "method">> & {
|
|
64
|
+
RECORD?: boolean;
|
|
65
|
+
}): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
|
+
import { SHOULD_RECORD } from '@warp-drive/core/build-config/env';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @module
|
|
5
|
+
* @mergeModuleWith <project>
|
|
6
|
+
*/
|
|
1
7
|
const TEST_IDS = new WeakMap();
|
|
2
|
-
let HOST = '
|
|
8
|
+
let HOST = '/';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
|
|
3
14
|
function setConfig({
|
|
4
15
|
host
|
|
5
16
|
}) {
|
|
6
17
|
HOST = host.endsWith('/') ? host : `${host}/`;
|
|
7
18
|
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
|
|
8
24
|
function setTestId(context, str) {
|
|
9
25
|
if (str && TEST_IDS.has(context)) {
|
|
10
26
|
throw new Error(`MockServerHandler is already configured with a testId.`);
|
|
@@ -12,37 +28,72 @@ function setTestId(context, str) {
|
|
|
12
28
|
if (str) {
|
|
13
29
|
TEST_IDS.set(context, {
|
|
14
30
|
id: str,
|
|
15
|
-
|
|
16
|
-
|
|
31
|
+
mock: {
|
|
32
|
+
GET: {},
|
|
33
|
+
PUT: {},
|
|
34
|
+
PATCH: {},
|
|
35
|
+
DELETE: {},
|
|
36
|
+
POST: {},
|
|
37
|
+
QUERY: {},
|
|
38
|
+
OPTIONS: {},
|
|
39
|
+
HEAD: {},
|
|
40
|
+
CONNECT: {},
|
|
41
|
+
TRACE: {}
|
|
42
|
+
},
|
|
43
|
+
request: {
|
|
44
|
+
GET: {},
|
|
45
|
+
PUT: {},
|
|
46
|
+
PATCH: {},
|
|
47
|
+
DELETE: {},
|
|
48
|
+
POST: {},
|
|
49
|
+
QUERY: {},
|
|
50
|
+
OPTIONS: {},
|
|
51
|
+
HEAD: {},
|
|
52
|
+
CONNECT: {},
|
|
53
|
+
TRACE: {}
|
|
54
|
+
}
|
|
17
55
|
});
|
|
18
56
|
} else {
|
|
19
57
|
TEST_IDS.delete(context);
|
|
20
58
|
}
|
|
21
59
|
}
|
|
22
|
-
|
|
60
|
+
const shouldRecord = SHOULD_RECORD ? true : false;
|
|
61
|
+
let IS_RECORDING = null;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @public
|
|
65
|
+
*/
|
|
23
66
|
function setIsRecording(value) {
|
|
24
|
-
IS_RECORDING = Boolean(value);
|
|
67
|
+
IS_RECORDING = value === null ? value : Boolean(value);
|
|
25
68
|
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @public
|
|
72
|
+
*/
|
|
26
73
|
function getIsRecording() {
|
|
27
|
-
return IS_RECORDING;
|
|
74
|
+
return IS_RECORDING === null ? shouldRecord : IS_RECORDING;
|
|
28
75
|
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A request handler that intercepts requests and routes them through
|
|
79
|
+
* the Holodeck mock server.
|
|
80
|
+
*
|
|
81
|
+
* This handler modifies the request URL to include test identifiers
|
|
82
|
+
* and manages request counts for accurate mocking.
|
|
83
|
+
*
|
|
84
|
+
* Requires that the test context be configured with a testId using `setTestId`.
|
|
85
|
+
*
|
|
86
|
+
* @param owner - the test context object used to retrieve the test ID.
|
|
87
|
+
*/
|
|
29
88
|
class MockServerHandler {
|
|
30
89
|
constructor(owner) {
|
|
31
90
|
this.owner = owner;
|
|
32
91
|
}
|
|
33
92
|
async request(context, next) {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
const request = Object.assign({}, context.request);
|
|
39
|
-
const isRecording = request.url.endsWith('/__record');
|
|
40
|
-
const firstChar = request.url.includes('?') ? '&' : '?';
|
|
41
|
-
const queryForTest = `${firstChar}__xTestId=${test.id}&__xTestRequestNumber=${isRecording ? test.mock++ : test.request++}`;
|
|
42
|
-
request.url = request.url + queryForTest;
|
|
43
|
-
request.mode = 'cors';
|
|
44
|
-
request.credentials = 'omit';
|
|
45
|
-
request.referrerPolicy = '';
|
|
93
|
+
const {
|
|
94
|
+
request,
|
|
95
|
+
queryForTest
|
|
96
|
+
} = setupHolodeckFetch(this.owner, Object.assign({}, context.request));
|
|
46
97
|
try {
|
|
47
98
|
const future = next(request);
|
|
48
99
|
context.setStream(future.getStream());
|
|
@@ -55,17 +106,127 @@ class MockServerHandler {
|
|
|
55
106
|
}
|
|
56
107
|
}
|
|
57
108
|
}
|
|
58
|
-
|
|
109
|
+
function setupHolodeckFetch(owner, request) {
|
|
59
110
|
const test = TEST_IDS.get(owner);
|
|
60
111
|
if (!test) {
|
|
61
|
-
throw new Error(`
|
|
112
|
+
throw new Error(`MockServerHandler is not configured with a testId. Use setTestId to set the testId for each test`);
|
|
113
|
+
}
|
|
114
|
+
const url = request.url;
|
|
115
|
+
const firstChar = url.includes('?') ? '&' : '?';
|
|
116
|
+
const method = request.method?.toUpperCase() ?? 'GET';
|
|
117
|
+
|
|
118
|
+
// enable custom methods
|
|
119
|
+
if (!test.request[method]) {
|
|
120
|
+
// eslint-disable-next-line no-console
|
|
121
|
+
console.log(`⚠️ Using custom HTTP method ${method} for response to request ${url}`);
|
|
122
|
+
test.request[method] = {};
|
|
123
|
+
}
|
|
124
|
+
if (!(url in test.request[method])) {
|
|
125
|
+
test.request[method][url] = 0;
|
|
126
|
+
}
|
|
127
|
+
const queryForTest = `${firstChar}__xTestId=${test.id}&__xTestRequestNumber=${test.request[method][url]++}`;
|
|
128
|
+
request.url = url + queryForTest;
|
|
129
|
+
request.method = method;
|
|
130
|
+
request.mode = 'cors';
|
|
131
|
+
request.credentials = 'omit';
|
|
132
|
+
request.referrerPolicy = '';
|
|
133
|
+
|
|
134
|
+
// since holodeck currently runs on a separate port
|
|
135
|
+
// and we don't want to trigger cors pre-flight
|
|
136
|
+
// we convert PUT to POST to keep the request in the
|
|
137
|
+
// "simple" cors category.
|
|
138
|
+
// if (request.method === 'PUT') {
|
|
139
|
+
// request.method = 'POST';
|
|
140
|
+
// }
|
|
141
|
+
|
|
142
|
+
const headers = new Headers(request.headers);
|
|
143
|
+
if (headers.has('Content-Type')) {
|
|
144
|
+
// under the rules of simple-cors, content-type can only be
|
|
145
|
+
// one of three things, none of which are what folks typically
|
|
146
|
+
// set this to. Since holodeck always expects body to be JSON
|
|
147
|
+
// this "just works".
|
|
148
|
+
headers.set('Content-Type', 'text/plain');
|
|
149
|
+
request.headers = headers;
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
request,
|
|
153
|
+
queryForTest
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function upgradeStore(store) {
|
|
157
|
+
if (typeof store.adapterFor !== 'function') {
|
|
158
|
+
throw new Error('Store is not compatible with Holodeck. Missing adapterFor method.');
|
|
62
159
|
}
|
|
63
|
-
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Creates an adapterFor function that wraps the provided adapterFor function
|
|
164
|
+
* to override the adapter's _fetchRequest method to route requests through
|
|
165
|
+
* the Holodeck mock server.
|
|
166
|
+
*
|
|
167
|
+
* @param owner - The test context object used to retrieve the test ID.
|
|
168
|
+
*/
|
|
169
|
+
function installAdapterFor(owner, store) {
|
|
170
|
+
upgradeStore(store);
|
|
171
|
+
const fn = store.adapterFor;
|
|
172
|
+
function holodeckAdapterFor(modelName, _allowMissing) {
|
|
173
|
+
const adapter = fn.call(this, modelName, _allowMissing);
|
|
174
|
+
if (adapter) {
|
|
175
|
+
if (!adapter.hasOverriddenFetch) {
|
|
176
|
+
adapter.hasOverriddenFetch = true;
|
|
177
|
+
adapter.useFetch = true;
|
|
178
|
+
const originalFetch = adapter._fetchRequest?.bind(adapter);
|
|
179
|
+
adapter._fetchRequest = function (options) {
|
|
180
|
+
if (!originalFetch) {
|
|
181
|
+
throw new Error(`Adapter ${String(modelName)} does not implement _fetchRequest`);
|
|
182
|
+
}
|
|
183
|
+
const {
|
|
184
|
+
request
|
|
185
|
+
} = setupHolodeckFetch(owner, options);
|
|
186
|
+
return originalFetch(request);
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return adapter;
|
|
191
|
+
}
|
|
192
|
+
store.adapterFor = holodeckAdapterFor;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Mock a request by sending the scaffold to the mock server.
|
|
197
|
+
*
|
|
198
|
+
* @public
|
|
199
|
+
*/
|
|
200
|
+
async function mock(owner, generate, isRecording) {
|
|
64
201
|
if (getIsRecording() || isRecording) {
|
|
202
|
+
const test = TEST_IDS.get(owner);
|
|
203
|
+
if (!test) {
|
|
204
|
+
throw new Error(`Cannot call "mock" before configuring a testId. Use setTestId to set the testId for each test`);
|
|
205
|
+
}
|
|
206
|
+
const requestToMock = generate();
|
|
207
|
+
const {
|
|
208
|
+
url: mockUrl,
|
|
209
|
+
method
|
|
210
|
+
} = requestToMock;
|
|
211
|
+
if (!mockUrl || !method) {
|
|
212
|
+
throw new Error(`MockError: Cannot mock a request without providing a URL and Method`);
|
|
213
|
+
}
|
|
214
|
+
const mockMethod = method?.toUpperCase() ?? 'GET';
|
|
215
|
+
|
|
216
|
+
// enable custom methods
|
|
217
|
+
if (!test.mock[mockMethod]) {
|
|
218
|
+
// eslint-disable-next-line no-console
|
|
219
|
+
console.log(`⚠️ Using custom HTTP method ${mockMethod} for response to request ${mockUrl}`);
|
|
220
|
+
test.mock[mockMethod] = {};
|
|
221
|
+
}
|
|
222
|
+
if (!(mockUrl in test.mock[mockMethod])) {
|
|
223
|
+
test.mock[mockMethod][mockUrl] = 0;
|
|
224
|
+
}
|
|
225
|
+
const testMockNum = test.mock[mockMethod][mockUrl]++;
|
|
65
226
|
const url = `${HOST}__record?__xTestId=${test.id}&__xTestRequestNumber=${testMockNum}`;
|
|
66
227
|
await fetch(url, {
|
|
67
228
|
method: 'POST',
|
|
68
|
-
body: JSON.stringify(
|
|
229
|
+
body: JSON.stringify(requestToMock),
|
|
69
230
|
mode: 'cors',
|
|
70
231
|
credentials: 'omit',
|
|
71
232
|
referrerPolicy: ''
|
|
@@ -73,5 +234,4 @@ async function mock(owner, generate, isRecording) {
|
|
|
73
234
|
}
|
|
74
235
|
}
|
|
75
236
|
|
|
76
|
-
export { MockServerHandler, getIsRecording, mock, setConfig, setIsRecording, setTestId };
|
|
77
|
-
//# sourceMappingURL=index.js.map
|
|
237
|
+
export { MockServerHandler, getIsRecording, installAdapterFor, mock, setConfig, setIsRecording, setTestId };
|
package/dist/mock.js
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { mock, getIsRecording } from './index.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @public
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @public
|
|
13
|
+
*/
|
|
14
|
+
|
|
3
15
|
/**
|
|
4
16
|
* Sets up Mocking for a GET request on the mock server
|
|
5
17
|
* for the supplied url.
|
|
@@ -28,11 +40,81 @@ function GET(owner, url, response, options) {
|
|
|
28
40
|
response: response()
|
|
29
41
|
}), getIsRecording() || (options?.RECORD ?? false));
|
|
30
42
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
const STATUS_TEXT_FOR = new Map([[200, 'OK'], [201, 'Created'], [202, 'Accepted'], [203, 'Non-Authoritative Information'], [204, 'No Content'], [205, 'Reset Content'], [206, 'Partial Content'], [207, 'Multi-Status'], [208, 'Already Reported'], [226, 'IM Used'], [300, 'Multiple Choices'], [301, 'Moved Permanently'], [302, 'Found'], [303, 'See Other'], [304, 'Not Modified'], [307, 'Temporary Redirect'], [308, 'Permanent Redirect'], [400, 'Bad Request'], [401, 'Unauthorized'], [402, 'Payment Required'], [403, 'Forbidden'], [404, 'Not Found'], [405, 'Method Not Allowed'], [406, 'Not Acceptable'], [407, 'Proxy Authentication Required'], [408, 'Request Timeout'], [409, 'Conflict'], [410, 'Gone'], [411, 'Length Required'], [412, 'Precondition Failed'], [413, 'Payload Too Large'], [414, 'URI Too Long'], [415, 'Unsupported Media Type'], [416, 'Range Not Satisfiable'], [417, 'Expectation Failed'], [419, 'Page Expired'], [420, 'Enhance Your Calm'], [421, 'Misdirected Request'], [422, 'Unprocessable Entity'], [423, 'Locked'], [424, 'Failed Dependency'], [425, 'Too Early'], [426, 'Upgrade Required'], [428, 'Precondition Required'], [429, 'Too Many Requests'], [430, 'Request Header Fields Too Large'], [431, 'Request Header Fields Too Large'], [450, 'Blocked By Windows Parental Controls'], [451, 'Unavailable For Legal Reasons'], [500, 'Internal Server Error'], [501, 'Not Implemented'], [502, 'Bad Gateway'], [503, 'Service Unavailable'], [504, 'Gateway Timeout'], [505, 'HTTP Version Not Supported'], [506, 'Variant Also Negotiates'], [507, 'Insufficient Storage'], [508, 'Loop Detected'], [509, 'Bandwidth Limit Exceeded'], [510, 'Not Extended'], [511, 'Network Authentication Required']]);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Mock a POST request
|
|
47
|
+
*/
|
|
48
|
+
function POST(owner, url, response, options) {
|
|
49
|
+
return mock(owner, () => {
|
|
50
|
+
const body = response();
|
|
51
|
+
const status = options?.status ?? (body ? 201 : 204);
|
|
52
|
+
return {
|
|
53
|
+
status: status,
|
|
54
|
+
statusText: options?.statusText ?? STATUS_TEXT_FOR.get(status) ?? '',
|
|
55
|
+
headers: options?.headers ?? {},
|
|
56
|
+
body: options?.body ?? null,
|
|
57
|
+
method: 'POST',
|
|
58
|
+
url,
|
|
59
|
+
response: body
|
|
60
|
+
};
|
|
61
|
+
}, getIsRecording() || (options?.RECORD ?? false));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* mock a PUT request
|
|
66
|
+
*/
|
|
67
|
+
function PUT(owner, url, response, options) {
|
|
68
|
+
return mock(owner, () => {
|
|
69
|
+
const body = response();
|
|
70
|
+
const status = options?.status ?? (body ? 200 : 204);
|
|
71
|
+
return {
|
|
72
|
+
status: status,
|
|
73
|
+
statusText: options?.statusText ?? STATUS_TEXT_FOR.get(status) ?? '',
|
|
74
|
+
headers: options?.headers ?? {},
|
|
75
|
+
body: options?.body ?? null,
|
|
76
|
+
method: 'PUT',
|
|
77
|
+
url,
|
|
78
|
+
response: body
|
|
79
|
+
};
|
|
80
|
+
}, getIsRecording() || (options?.RECORD ?? false));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* mock a PATCH request
|
|
84
|
+
*
|
|
85
|
+
*/
|
|
86
|
+
function PATCH(owner, url, response, options) {
|
|
87
|
+
return mock(owner, () => {
|
|
88
|
+
const body = response();
|
|
89
|
+
const status = options?.status ?? (body ? 200 : 204);
|
|
90
|
+
return {
|
|
91
|
+
status: status,
|
|
92
|
+
statusText: options?.statusText ?? STATUS_TEXT_FOR.get(status) ?? '',
|
|
93
|
+
headers: options?.headers ?? {},
|
|
94
|
+
body: options?.body ?? null,
|
|
95
|
+
method: 'PATCH',
|
|
96
|
+
url,
|
|
97
|
+
response: body
|
|
98
|
+
};
|
|
99
|
+
}, getIsRecording() || (options?.RECORD ?? false));
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* mock a DELETE request
|
|
103
|
+
*/
|
|
104
|
+
function DELETE(owner, url, response, options) {
|
|
105
|
+
return mock(owner, () => {
|
|
106
|
+
const body = response();
|
|
107
|
+
const status = options?.status ?? (body ? 200 : 204);
|
|
108
|
+
return {
|
|
109
|
+
status: status,
|
|
110
|
+
statusText: options?.statusText ?? STATUS_TEXT_FOR.get(status) ?? '',
|
|
111
|
+
headers: options?.headers ?? {},
|
|
112
|
+
body: options?.body ?? null,
|
|
113
|
+
method: 'DELETE',
|
|
114
|
+
url,
|
|
115
|
+
response: body
|
|
116
|
+
};
|
|
117
|
+
}, getIsRecording() || (options?.RECORD ?? false));
|
|
118
|
+
}
|
|
36
119
|
|
|
37
|
-
export { DELETE, GET, PATCH, POST, PUT
|
|
38
|
-
//# sourceMappingURL=mock.js.map
|
|
120
|
+
export { DELETE, GET, PATCH, POST, PUT };
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@warp-drive/holodeck",
|
|
3
3
|
"description": "⚡️ Simple, Fast HTTP Mocking for Tests",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.3-beta.1",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Chris Thoburn <runspired@users.noreply.github.com>",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "git+ssh://git@github.com:
|
|
9
|
+
"url": "git+ssh://git@github.com:warp-drive-data/warp-drive.git",
|
|
10
10
|
"directory": "packages/holodeck"
|
|
11
11
|
},
|
|
12
|
-
"homepage": "https://github.com/
|
|
13
|
-
"bugs": "https://github.com/
|
|
12
|
+
"homepage": "https://github.com/warp-drive-data/warp-drive",
|
|
13
|
+
"bugs": "https://github.com/warp-drive-data/warp-drive/issues",
|
|
14
14
|
"engines": {
|
|
15
15
|
"node": ">= 18.20.8"
|
|
16
16
|
},
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"extends": "../../package.json"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"chalk": "^5.
|
|
25
|
-
"hono": "^4.
|
|
26
|
-
"@hono/node-server": "^1.
|
|
24
|
+
"chalk": "^5.5.0",
|
|
25
|
+
"hono": "^4.9.4",
|
|
26
|
+
"@hono/node-server": "^1.19.0"
|
|
27
27
|
},
|
|
28
28
|
"type": "module",
|
|
29
29
|
"files": [
|
|
@@ -32,25 +32,36 @@
|
|
|
32
32
|
"README.md",
|
|
33
33
|
"LICENSE.md",
|
|
34
34
|
"server",
|
|
35
|
+
"declarations",
|
|
35
36
|
"logos"
|
|
36
37
|
],
|
|
37
38
|
"bin": {
|
|
38
39
|
"ensure-cert": "./server/ensure-cert.js"
|
|
39
40
|
},
|
|
40
41
|
"peerDependencies": {
|
|
41
|
-
"@
|
|
42
|
-
"@warp-drive/
|
|
42
|
+
"@warp-drive/utilities": "5.8.0-beta.0",
|
|
43
|
+
"@warp-drive/legacy": "5.8.0-beta.0",
|
|
44
|
+
"@warp-drive/core": "5.8.0-beta.0"
|
|
45
|
+
},
|
|
46
|
+
"peerDependenciesMeta": {
|
|
47
|
+
"@warp-drive/utilities": {
|
|
48
|
+
"optional": true
|
|
49
|
+
},
|
|
50
|
+
"@warp-drive/legacy": {
|
|
51
|
+
"optional": true
|
|
52
|
+
}
|
|
43
53
|
},
|
|
44
54
|
"devDependencies": {
|
|
45
|
-
"@babel/core": "^7.
|
|
46
|
-
"@babel/plugin-transform-typescript": "^7.
|
|
47
|
-
"@babel/preset-env": "^7.
|
|
48
|
-
"@babel/preset-typescript": "^7.27.
|
|
49
|
-
"@babel/runtime": "^7.
|
|
50
|
-
"@
|
|
51
|
-
"@warp-drive/
|
|
52
|
-
"@warp-drive/
|
|
53
|
-
"
|
|
55
|
+
"@babel/core": "^7.28.3",
|
|
56
|
+
"@babel/plugin-transform-typescript": "^7.28.0",
|
|
57
|
+
"@babel/preset-env": "^7.28.3",
|
|
58
|
+
"@babel/preset-typescript": "^7.27.1",
|
|
59
|
+
"@babel/runtime": "^7.28.3",
|
|
60
|
+
"@warp-drive/utilities": "5.8.0-beta.0",
|
|
61
|
+
"@warp-drive/legacy": "5.8.0-beta.0",
|
|
62
|
+
"@warp-drive/core": "5.8.0-beta.0",
|
|
63
|
+
"@warp-drive/internal-config": "5.8.0-beta.0",
|
|
64
|
+
"vite": "^7.1.3"
|
|
54
65
|
},
|
|
55
66
|
"exports": {
|
|
56
67
|
".": {
|
|
@@ -58,14 +69,17 @@
|
|
|
58
69
|
"bun": "./server/index.js",
|
|
59
70
|
"deno": "./server/index.js",
|
|
60
71
|
"browser": {
|
|
72
|
+
"types": "./declarations/index.d.ts",
|
|
61
73
|
"default": "./dist/index.js"
|
|
62
74
|
},
|
|
63
75
|
"import": {
|
|
76
|
+
"types": "./declarations/index.d.ts",
|
|
64
77
|
"default": "./dist/index.js"
|
|
65
78
|
},
|
|
66
79
|
"default": "./server/index.js"
|
|
67
80
|
},
|
|
68
81
|
"./mock": {
|
|
82
|
+
"types": "./declarations/mock.d.ts",
|
|
69
83
|
"default": "./dist/mock.js"
|
|
70
84
|
}
|
|
71
85
|
},
|