@warp-drive/holodeck 0.1.0-beta.1 → 0.1.0-beta.2

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 ADDED
@@ -0,0 +1,139 @@
1
+ # @warp-drive/holodeck Changelog
2
+
3
+ ## v5.8.0 (2025-10-07)
4
+
5
+ #### :rocket: Enhancement
6
+
7
+ * [#10121](https://github.com/warp-drive-data/warp-drive/pull/10121) feat: legacy ModelFragments support ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
8
+
9
+ #### :house: Internal
10
+
11
+ * [#10377](https://github.com/warp-drive-data/warp-drive/pull/10377) chore: refactor holodeck to use node ([@runspired](https://github.com/runspired))
12
+ * [#10376](https://github.com/warp-drive-data/warp-drive/pull/10376) chore: prepare holodeck for publishing ([@runspired](https://github.com/runspired))
13
+ * [#10375](https://github.com/warp-drive-data/warp-drive/pull/10375) chore: make non-GET holodeck replay work ([@runspired](https://github.com/runspired))
14
+ * [#10373](https://github.com/warp-drive-data/warp-drive/pull/10373) chore: add holodeck client support for PUT/PATCH/POST/DELETE ([@runspired](https://github.com/runspired))
15
+ * [#10372](https://github.com/warp-drive-data/warp-drive/pull/10372) Missing Context Param in Code Example for setTestId ([@lucasxzh](https://github.com/lucasxzh))
16
+ * [#10349](https://github.com/warp-drive-data/warp-drive/pull/10349) chore: update all emberjs/data links to warp-drive-data/warp-drive ([@runspired](https://github.com/runspired))
17
+
18
+ #### Committers: (3)
19
+
20
+ Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
21
+ Chris Thoburn ([@runspired](https://github.com/runspired))
22
+ [@lucasxzh](https://github.com/lucasxzh)
23
+
24
+ ## v5.7.0 (2025-08-26)
25
+
26
+ #### :house: Internal
27
+
28
+ * [#10279](https://github.com/warp-drive-data/warp-drive/pull/10279) chore: add support for LegacyNetworkHandler to holodeck ([@runspired](https://github.com/runspired))
29
+ * [#10226](https://github.com/warp-drive-data/warp-drive/pull/10226) chore: use https for diagnostic by default, print url last ([@runspired](https://github.com/runspired))
30
+ * [#10124](https://github.com/warp-drive-data/warp-drive/pull/10124) chore: mark a few types as private ([@runspired](https://github.com/runspired))
31
+
32
+ #### Committers: (1)
33
+
34
+ Chris Thoburn ([@runspired](https://github.com/runspired))
35
+
36
+ ## v5.6.0 (2025-07-01)
37
+
38
+ #### :memo: Documentation
39
+
40
+ * [#9991](https://github.com/warp-drive-data/warp-drive/pull/9991) chore: improve output of typedoc ([@runspired](https://github.com/runspired))
41
+
42
+ #### :house: Internal
43
+
44
+ * [#10042](https://github.com/warp-drive-data/warp-drive/pull/10042) chore: upgrade to vite7 ([@runspired](https://github.com/runspired))
45
+ * [#10040](https://github.com/warp-drive-data/warp-drive/pull/10040) chore: isolated declarations ([@runspired](https://github.com/runspired))
46
+
47
+ #### Committers: (1)
48
+
49
+ Chris Thoburn ([@runspired](https://github.com/runspired))
50
+
51
+ ## v5.4.0 (2025-04-11)
52
+
53
+ #### :memo: Documentation
54
+
55
+ * [#9648](https://github.com/warp-drive-data/warp-drive/pull/9648) docs: cleanup usage pattern for RequestManager configuration on store ([@runspired](https://github.com/runspired))
56
+
57
+ #### :rocket: Enhancement
58
+
59
+ * [#9530](https://github.com/warp-drive-data/warp-drive/pull/9530) feat: <:idle> state for Requests ([@runspired](https://github.com/runspired))
60
+ * [#9503](https://github.com/warp-drive-data/warp-drive/pull/9503) feat: request deduping & <Request /> invalidate subscriptions ([@runspired](https://github.com/runspired))
61
+
62
+ #### :bug: Bug Fix
63
+
64
+ * [#9530](https://github.com/warp-drive-data/warp-drive/pull/9530) feat: <:idle> state for Requests ([@runspired](https://github.com/runspired))
65
+
66
+ #### :house: Internal
67
+
68
+ * [#9759](https://github.com/warp-drive-data/warp-drive/pull/9759) chore: Improve contributing experience ([@runspired](https://github.com/runspired))
69
+ * [#9758](https://github.com/warp-drive-data/warp-drive/pull/9758) chore: update contributing docs ([@runspired](https://github.com/runspired))
70
+ * [#9752](https://github.com/warp-drive-data/warp-drive/pull/9752) chore: tooling upgrades to support WarpDrive package unification ([@runspired](https://github.com/runspired))
71
+ * [#9705](https://github.com/warp-drive-data/warp-drive/pull/9705) chore: make diagnostic shutdown safer, use bun for holodeck server ([@runspired](https://github.com/runspired))
72
+ * [#9699](https://github.com/warp-drive-data/warp-drive/pull/9699) chore: update to pnpm 10 ([@runspired](https://github.com/runspired))
73
+ * [#9629](https://github.com/warp-drive-data/warp-drive/pull/9629) fix: restore * versions and setup publish to not overwrite them ([@runspired](https://github.com/runspired))
74
+ * [#9620](https://github.com/warp-drive-data/warp-drive/pull/9620) Starwars ([@NullVoxPopuli](https://github.com/NullVoxPopuli))
75
+ * [#9543](https://github.com/warp-drive-data/warp-drive/pull/9543) Improvement: Setting Up The Project ([@leoeuclids](https://github.com/leoeuclids))
76
+
77
+ #### Committers: (3)
78
+
79
+ Chris Thoburn ([@runspired](https://github.com/runspired))
80
+ [@NullVoxPopuli](https://github.com/NullVoxPopuli)
81
+ Leo Euclides ([@leoeuclids](https://github.com/leoeuclids))
82
+
83
+ ## v0.0.0-alpha.71 (2024-06-15)
84
+
85
+ #### :memo: Documentation
86
+
87
+ * [#9328](https://github.com/warp-drive-data/warp-drive/pull/9328) chore: update READMEs with status and dist tag info ([@runspired](https://github.com/runspired))
88
+
89
+ #### :rocket: Enhancement
90
+
91
+ * [#9471](https://github.com/warp-drive-data/warp-drive/pull/9471) feat: npx warp-drive ([@runspired](https://github.com/runspired))
92
+ * [#9366](https://github.com/warp-drive-data/warp-drive/pull/9366) feat: typed Model ([@runspired](https://github.com/runspired))
93
+ * [#9260](https://github.com/warp-drive-data/warp-drive/pull/9260) feat: ember specific data utils ([@runspired](https://github.com/runspired))
94
+
95
+ #### :bug: Bug Fix
96
+
97
+ * [#9383](https://github.com/warp-drive-data/warp-drive/pull/9383) fix: ensure cache-handler clones full errors ([@runspired](https://github.com/runspired))
98
+
99
+ #### :house: Internal
100
+
101
+ * [#9292](https://github.com/warp-drive-data/warp-drive/pull/9292) feat: add new build-config package ([@runspired](https://github.com/runspired))
102
+
103
+ #### Committers: (1)
104
+
105
+ Chris Thoburn ([@runspired](https://github.com/runspired))
106
+
107
+ For the full project changelog see [https://github.com/warp-drive-data/warp-drive/blob/main/CHANGELOG.md](https://github.com/warp-drive-data/warp-drive/blob/main/CHANGELOG.md)
108
+
109
+ ## v0.0.0-alpha.9 (2024-02-24)
110
+
111
+ #### :rocket: Enhancement
112
+
113
+ * [#8921](https://github.com/warp-drive-data/warp-drive/pull/8921) feat: Improved Fetch Errors ([@runspired](https://github.com/runspired))
114
+
115
+ #### :house: Internal
116
+
117
+ * [#9125](https://github.com/warp-drive-data/warp-drive/pull/9125) Configure ESLint for test packages ([@gitKrystan](https://github.com/gitKrystan))
118
+ * [#8994](https://github.com/warp-drive-data/warp-drive/pull/8994) chore: fix recursive pnpm on node 18.18 ([@runspired](https://github.com/runspired))
119
+ * [#9084](https://github.com/warp-drive-data/warp-drive/pull/9084) Add import types ([@gitKrystan](https://github.com/gitKrystan))
120
+ * [#8989](https://github.com/warp-drive-data/warp-drive/pull/8989) chore(private): concurrent mode ([@runspired](https://github.com/runspired))
121
+ * [#9058](https://github.com/warp-drive-data/warp-drive/pull/9058) Switch from eslint-plugin-prettier to running prettier directly ([@gitKrystan](https://github.com/gitKrystan))
122
+ * [#9057](https://github.com/warp-drive-data/warp-drive/pull/9057) Add eslint-plugin-n to eslint config for node files ([@gitKrystan](https://github.com/gitKrystan))
123
+ * [#9055](https://github.com/warp-drive-data/warp-drive/pull/9055) Fix ESLint for VSCode ([@gitKrystan](https://github.com/gitKrystan))
124
+ * [#9051](https://github.com/warp-drive-data/warp-drive/pull/9051) chore: use references for tsc, add checks to schema-record, bun to run scripts ([@runspired](https://github.com/runspired))
125
+ * [#9032](https://github.com/warp-drive-data/warp-drive/pull/9032) chore(types): split out lint and type commands to be per-package ([@runspired](https://github.com/runspired))
126
+ * [#9050](https://github.com/warp-drive-data/warp-drive/pull/9050) chore: use composite mode for tsc ([@runspired](https://github.com/runspired))
127
+ * [#9049](https://github.com/warp-drive-data/warp-drive/pull/9049) chore: incremental tsc builds ([@runspired](https://github.com/runspired))
128
+ * [#9046](https://github.com/warp-drive-data/warp-drive/pull/9046) chore: reduce number of things turbo builds for build ([@runspired](https://github.com/runspired))
129
+ * [#9025](https://github.com/warp-drive-data/warp-drive/pull/9025) chore: reconfigure request package type location ([@runspired](https://github.com/runspired))
130
+ * [#8972](https://github.com/warp-drive-data/warp-drive/pull/8972) chore: use new test runner for request tests ([@runspired](https://github.com/runspired))
131
+ * [#8931](https://github.com/warp-drive-data/warp-drive/pull/8931) chore: package infra for schema-record ([@runspired](https://github.com/runspired))
132
+ * [#8912](https://github.com/warp-drive-data/warp-drive/pull/8912) chore: docs for holodeck ([@runspired](https://github.com/runspired))
133
+ * [#8906](https://github.com/warp-drive-data/warp-drive/pull/8906) feat: expand mock-server capabilities, add to main tests ([@runspired](https://github.com/runspired))
134
+
135
+ #### Committers: (2)
136
+
137
+ Chris Thoburn ([@runspired](https://github.com/runspired))
138
+ Krystan HuffMenne ([@gitKrystan](https://github.com/gitKrystan))
139
+
@@ -0,0 +1,56 @@
1
+ import { ScaffoldGenerator } from "./mock.js";
2
+ import { Handler, NextFn } from "@warp-drive/core/request";
3
+ import { RequestContext, StructuredDataDocument } from "@warp-drive/core/types/request";
4
+ import { Store } from "@warp-drive/legacy/store";
5
+ //#region src/index.d.ts
6
+ /**
7
+ * @public
8
+ */
9
+ declare function setConfig({ host }: {
10
+ host: string;
11
+ }): void;
12
+ /**
13
+ * @public
14
+ */
15
+ declare function setTestId(context: object, str: string | null): void;
16
+ /**
17
+ * @public
18
+ */
19
+ declare function setIsRecording(value: boolean): void;
20
+ /**
21
+ * @public
22
+ */
23
+ declare function getIsRecording(): boolean;
24
+ /**
25
+ * A request handler that intercepts requests and routes them through
26
+ * the Holodeck mock server.
27
+ *
28
+ * This handler modifies the request URL to include test identifiers
29
+ * and manages request counts for accurate mocking.
30
+ *
31
+ * Requires that the test context be configured with a testId using `setTestId`.
32
+ *
33
+ * @param owner - the test context object used to retrieve the test ID.
34
+ */
35
+ declare class MockServerHandler implements Handler {
36
+ owner: object;
37
+ constructor(owner: object);
38
+ request<T>(context: RequestContext, next: NextFn<T>): Promise<StructuredDataDocument<T>>;
39
+ }
40
+ /**
41
+ * Creates an adapterFor function that wraps the provided adapterFor function
42
+ * to override the adapter's _fetchRequest method to route requests through
43
+ * the Holodeck mock server.
44
+ *
45
+ * @param owner - The test context object used to retrieve the test ID.
46
+ */
47
+ declare function installAdapterFor(owner: object, store: Store): void;
48
+ /**
49
+ * Mock a request by sending the scaffold to the mock server.
50
+ *
51
+ * @public
52
+ */
53
+ declare function mock(owner: object, generate: ScaffoldGenerator, isRecording?: boolean): Promise<void>;
54
+ //#endregion
55
+ export { MockServerHandler, getIsRecording, installAdapterFor, mock, setConfig, setIsRecording, setTestId };
56
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;;iBAuIgB,YAAY;EAAU;;;;;iBAQtB,UAAU,iBAAiB;;;;iBA2C3B,eAAe;;;;iBAOf;;;;;;;;;;;;cAeH,6BAA6B;EACxC;EACA,YAAY;EAGZ,QAAc,GAAG,SAAS,gBAAgB,MAAM,OAAO,KAAK,QAAQ,uBAAuB;;;;;;;;;iBAkG7E,kBAAkB,eAAe,OAAO;;;;;;iBAuClC,KAAK,eAAe,UAAU,mBAAmB,wBAAwB"}
package/dist/index.js CHANGED
@@ -1,237 +1,181 @@
1
- import { SHOULD_RECORD } from '@warp-drive/core/build-config/env';
1
+ import { getGlobalConfig, macroCondition } from "@embroider/macros";
2
2
 
3
+ //#region src/index.ts
3
4
  /**
4
- * @module
5
- * @mergeModuleWith <project>
6
- */
7
- const TEST_IDS = new WeakMap();
8
- let HOST = '/';
9
-
5
+ * @module
6
+ * @mergeModuleWith <project>
7
+ */
8
+ const TEST_IDS = /* @__PURE__ */ new WeakMap();
9
+ let HOST = "/";
10
10
  /**
11
- * @public
12
- */
13
-
14
- function setConfig({
15
- host
16
- }) {
17
- HOST = host.endsWith('/') ? host : `${host}/`;
11
+ * @public
12
+ */
13
+ function setConfig({ host }) {
14
+ HOST = host.endsWith("/") ? host : `${host}/`;
18
15
  }
19
-
20
16
  /**
21
- * @public
22
- */
23
-
17
+ * @public
18
+ */
24
19
  function setTestId(context, str) {
25
- if (str && TEST_IDS.has(context)) {
26
- throw new Error(`MockServerHandler is already configured with a testId.`);
27
- }
28
- if (str) {
29
- TEST_IDS.set(context, {
30
- id: str,
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
- }
55
- });
56
- } else {
57
- TEST_IDS.delete(context);
58
- }
20
+ if (str && TEST_IDS.has(context)) throw new Error(`MockServerHandler is already configured with a testId.`);
21
+ if (str) TEST_IDS.set(context, {
22
+ id: str,
23
+ mock: {
24
+ GET: {},
25
+ PUT: {},
26
+ PATCH: {},
27
+ DELETE: {},
28
+ POST: {},
29
+ QUERY: {},
30
+ OPTIONS: {},
31
+ HEAD: {},
32
+ CONNECT: {},
33
+ TRACE: {}
34
+ },
35
+ request: {
36
+ GET: {},
37
+ PUT: {},
38
+ PATCH: {},
39
+ DELETE: {},
40
+ POST: {},
41
+ QUERY: {},
42
+ OPTIONS: {},
43
+ HEAD: {},
44
+ CONNECT: {},
45
+ TRACE: {}
46
+ }
47
+ });
48
+ else TEST_IDS.delete(context);
59
49
  }
60
- const shouldRecord = SHOULD_RECORD ? true : false;
50
+ const shouldRecord = macroCondition(getGlobalConfig().WarpDrive.env.SHOULD_RECORD) ? true : false;
61
51
  let IS_RECORDING = null;
62
-
63
52
  /**
64
- * @public
65
- */
53
+ * @public
54
+ */
66
55
  function setIsRecording(value) {
67
- IS_RECORDING = value === null ? value : Boolean(value);
56
+ IS_RECORDING = value === null ? value : Boolean(value);
68
57
  }
69
-
70
58
  /**
71
- * @public
72
- */
59
+ * @public
60
+ */
73
61
  function getIsRecording() {
74
- return IS_RECORDING === null ? shouldRecord : IS_RECORDING;
62
+ return IS_RECORDING === null ? shouldRecord : IS_RECORDING;
75
63
  }
76
-
77
64
  /**
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
- */
88
- class MockServerHandler {
89
- constructor(owner) {
90
- this.owner = owner;
91
- }
92
- async request(context, next) {
93
- const {
94
- request,
95
- queryForTest
96
- } = setupHolodeckFetch(this.owner, Object.assign({}, context.request));
97
- try {
98
- const future = next(request);
99
- context.setStream(future.getStream());
100
- return await future;
101
- } catch (e) {
102
- if (e instanceof Error && !(e instanceof DOMException)) {
103
- e.message = e.message.replace(queryForTest, '');
104
- }
105
- throw e;
106
- }
107
- }
108
- }
65
+ * A request handler that intercepts requests and routes them through
66
+ * the Holodeck mock server.
67
+ *
68
+ * This handler modifies the request URL to include test identifiers
69
+ * and manages request counts for accurate mocking.
70
+ *
71
+ * Requires that the test context be configured with a testId using `setTestId`.
72
+ *
73
+ * @param owner - the test context object used to retrieve the test ID.
74
+ */
75
+ var MockServerHandler = class {
76
+ constructor(owner) {
77
+ this.owner = owner;
78
+ }
79
+ async request(context, next) {
80
+ const { request, queryForTest } = setupHolodeckFetch(this.owner, Object.assign({}, context.request));
81
+ try {
82
+ const future = next(request);
83
+ context.setStream(future.getStream());
84
+ return await future;
85
+ } catch (e) {
86
+ if (e instanceof Error && !(e instanceof DOMException)) e.message = e.message.replace(queryForTest, "");
87
+ throw e;
88
+ }
89
+ }
90
+ };
109
91
  function setupHolodeckFetch(owner, request) {
110
- const test = TEST_IDS.get(owner);
111
- if (!test) {
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
- };
92
+ const test = TEST_IDS.get(owner);
93
+ if (!test) throw new Error(`MockServerHandler is not configured with a testId. Use setTestId to set the testId for each test`);
94
+ const url = request.url;
95
+ const firstChar = url.includes("?") ? "&" : "?";
96
+ const method = request.method?.toUpperCase() ?? "GET";
97
+ if (!test.request[method]) {
98
+ console.log(`⚠️ Using custom HTTP method ${method} for response to request ${url}`);
99
+ test.request[method] = {};
100
+ }
101
+ if (!(url in test.request[method])) test.request[method][url] = 0;
102
+ const queryForTest = `${firstChar}__xTestId=${test.id}&__xTestRequestNumber=${test.request[method][url]++}`;
103
+ request.url = url + queryForTest;
104
+ request.method = method;
105
+ request.mode = "cors";
106
+ request.credentials = "omit";
107
+ request.referrerPolicy = "";
108
+ const headers = new Headers(request.headers);
109
+ if (headers.has("Content-Type")) {
110
+ headers.set("Content-Type", "text/plain");
111
+ request.headers = headers;
112
+ }
113
+ return {
114
+ request,
115
+ queryForTest
116
+ };
155
117
  }
156
118
  function upgradeStore(store) {
157
- if (typeof store.adapterFor !== 'function') {
158
- throw new Error('Store is not compatible with Holodeck. Missing adapterFor method.');
159
- }
119
+ if (typeof store.adapterFor !== "function") throw new Error("Store is not compatible with Holodeck. Missing adapterFor method.");
160
120
  }
161
-
162
121
  /**
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
- */
122
+ * Creates an adapterFor function that wraps the provided adapterFor function
123
+ * to override the adapter's _fetchRequest method to route requests through
124
+ * the Holodeck mock server.
125
+ *
126
+ * @param owner - The test context object used to retrieve the test ID.
127
+ */
169
128
  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;
129
+ upgradeStore(store);
130
+ const fn = store.adapterFor;
131
+ function holodeckAdapterFor(modelName, _allowMissing) {
132
+ const adapter = fn.call(this, modelName, _allowMissing);
133
+ if (adapter) {
134
+ if (!adapter.hasOverriddenFetch) {
135
+ adapter.hasOverriddenFetch = true;
136
+ adapter.useFetch = true;
137
+ const originalFetch = adapter._fetchRequest?.bind(adapter);
138
+ adapter._fetchRequest = function(options) {
139
+ if (!originalFetch) throw new Error(`Adapter ${String(modelName)} does not implement _fetchRequest`);
140
+ const { request } = setupHolodeckFetch(owner, options);
141
+ return originalFetch(request);
142
+ };
143
+ }
144
+ }
145
+ return adapter;
146
+ }
147
+ store.adapterFor = holodeckAdapterFor;
193
148
  }
194
-
195
149
  /**
196
- * Mock a request by sending the scaffold to the mock server.
197
- *
198
- * @public
199
- */
150
+ * Mock a request by sending the scaffold to the mock server.
151
+ *
152
+ * @public
153
+ */
200
154
  async function mock(owner, generate, isRecording) {
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]++;
226
- const url = `${HOST}__record?__xTestId=${test.id}&__xTestRequestNumber=${testMockNum}`;
227
- await fetch(url, {
228
- method: 'POST',
229
- body: JSON.stringify(requestToMock),
230
- mode: 'cors',
231
- credentials: 'omit',
232
- referrerPolicy: ''
233
- });
234
- }
155
+ if (getIsRecording() || isRecording) {
156
+ const test = TEST_IDS.get(owner);
157
+ if (!test) throw new Error(`Cannot call "mock" before configuring a testId. Use setTestId to set the testId for each test`);
158
+ const requestToMock = generate();
159
+ const { url: mockUrl, method } = requestToMock;
160
+ if (!mockUrl || !method) throw new Error(`MockError: Cannot mock a request without providing a URL and Method`);
161
+ const mockMethod = method?.toUpperCase() ?? "GET";
162
+ if (!test.mock[mockMethod]) {
163
+ console.log(`⚠️ Using custom HTTP method ${mockMethod} for response to request ${mockUrl}`);
164
+ test.mock[mockMethod] = {};
165
+ }
166
+ if (!(mockUrl in test.mock[mockMethod])) test.mock[mockMethod][mockUrl] = 0;
167
+ const testMockNum = test.mock[mockMethod][mockUrl]++;
168
+ const url = `${HOST}__record?__xTestId=${test.id}&__xTestRequestNumber=${testMockNum}`;
169
+ await fetch(url, {
170
+ method: "POST",
171
+ body: JSON.stringify(requestToMock),
172
+ mode: "cors",
173
+ credentials: "omit",
174
+ referrerPolicy: ""
175
+ });
176
+ }
235
177
  }
236
178
 
179
+ //#endregion
237
180
  export { MockServerHandler, getIsRecording, installAdapterFor, mock, setConfig, setIsRecording, setTestId };
181
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["getGlobalConfig","macroCondition","TEST_IDS","WeakMap","HOST","setConfig","host","endsWith","setTestId","context","str","has","Error","set","id","mock","GET","PUT","PATCH","DELETE","POST","QUERY","OPTIONS","HEAD","CONNECT","TRACE","request","delete","shouldRecord","WarpDrive","env","SHOULD_RECORD","IS_RECORDING","setIsRecording","value","Boolean","getIsRecording","MockServerHandler","constructor","owner","next","queryForTest","setupHolodeckFetch","Object","assign","future","setStream","getStream","e","DOMException","message","replace","test","get","url","firstChar","includes","method","toUpperCase","console","log","mode","credentials","referrerPolicy","headers","Headers","upgradeAdapter","adapter","upgradeStore","store","adapterFor","installAdapterFor","fn","holodeckAdapterFor","modelName","_allowMissing","call","hasOverriddenFetch","useFetch","originalFetch","_fetchRequest","bind","options","String","generate","isRecording","requestToMock","mockUrl","mockMethod","testMockNum","fetch","body","JSON","stringify"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @module\n * @mergeModuleWith <project>\n */\nimport { SHOULD_RECORD } from '@warp-drive/core/build-config/env';\nimport type { Handler, NextFn } from '@warp-drive/core/request';\nimport type { HTTPMethod, RequestContext, RequestInfo, StructuredDataDocument } from '@warp-drive/core/types/request';\nimport type { MinimumAdapterInterface } from '@warp-drive/legacy/compat';\nimport type { Store } from '@warp-drive/legacy/store';\n\nimport type { ScaffoldGenerator } from './mock';\n\nconst TEST_IDS = new WeakMap<\n object,\n {\n id: string;\n /**\n * keeps track of the count of calls to record a mock\n */\n mock: {\n /**\n * For each GET, we keep track of the count\n * for a specific URL\n */\n GET: Record<string, number>;\n /**\n * For each PUT, we keep track of the count\n * for a specific URL\n */\n PUT: Record<string, number>;\n /**\n * For each PATCH, we keep track of the count\n * for a specific URL\n */\n PATCH: Record<string, number>;\n /**\n * For each DELETE, we keep track of the count\n * for a specific URL\n */\n DELETE: Record<string, number>;\n /**\n * For each POST, we keep track of the count\n * for a specific URL\n */\n POST: Record<string, number>;\n /**\n * For each OPTIONS, we keep track of the count\n * for a specific URL\n */\n OPTIONS: Record<string, number>;\n /**\n * For each QUERY, we keep track of the count\n * for a specific URL\n */\n QUERY: Record<string, number>;\n /**\n * for each HEAD, we keep track of the count\n * for a specific URL\n */\n HEAD: Record<string, number>;\n /**\n * for each CONNECT, we keep track of the count\n * for a specific URL\n */\n CONNECT: Record<string, number>;\n /**\n * for each TRACE, we keep track of the count\n * for a specific URL\n */\n TRACE: Record<string, number>;\n };\n /**\n * keeps track of the count of calls to make a request\n */\n request: {\n /**\n * For each GET, we keep track of the count\n * for a specific URL\n */\n GET: Record<string, number>;\n /**\n * For each PUT, we keep track of the count\n * for a specific URL\n */\n PUT: Record<string, number>;\n /**\n * For each PATCH, we keep track of the count\n * for a specific URL\n */\n PATCH: Record<string, number>;\n /**\n * For each DELETE, we keep track of the count\n * for a specific URL\n */\n DELETE: Record<string, number>;\n /**\n * For each POST, we keep track of the count\n * for a specific URL\n */\n POST: Record<string, number>;\n /**\n * For each OPTIONS, we keep track of the count\n * for a specific URL\n */\n OPTIONS: Record<string, number>;\n /**\n * For each QUERY, we keep track of the count\n * for a specific URL\n */\n QUERY: Record<string, number>;\n /**\n * for each HEAD, we keep track of the count\n * for a specific URL\n */\n HEAD: Record<string, number>;\n /**\n * for each CONNECT, we keep track of the count\n * for a specific URL\n */\n CONNECT: Record<string, number>;\n /**\n * for each TRACE, we keep track of the count\n * for a specific URL\n */\n TRACE: Record<string, number>;\n };\n }\n>();\n\nlet HOST = '/';\n\n/**\n * @public\n */\n\nexport function setConfig({ host }: { host: string }): void {\n HOST = host.endsWith('/') ? host : `${host}/`;\n}\n\n/**\n * @public\n */\n\nexport function setTestId(context: object, str: string | null): void {\n if (str && TEST_IDS.has(context)) {\n throw new Error(`MockServerHandler is already configured with a testId.`);\n }\n if (str) {\n TEST_IDS.set(context, {\n id: str,\n mock: {\n GET: {},\n PUT: {},\n PATCH: {},\n DELETE: {},\n POST: {},\n QUERY: {},\n OPTIONS: {},\n HEAD: {},\n CONNECT: {},\n TRACE: {},\n },\n request: {\n GET: {},\n PUT: {},\n PATCH: {},\n DELETE: {},\n POST: {},\n QUERY: {},\n OPTIONS: {},\n HEAD: {},\n CONNECT: {},\n TRACE: {},\n },\n });\n } else {\n TEST_IDS.delete(context);\n }\n}\n\nconst shouldRecord = SHOULD_RECORD ? true : false;\nlet IS_RECORDING: boolean | null = null;\n\n/**\n * @public\n */\nexport function setIsRecording(value: boolean): void {\n IS_RECORDING = value === null ? value : Boolean(value);\n}\n\n/**\n * @public\n */\nexport function getIsRecording(): boolean {\n return IS_RECORDING === null ? shouldRecord : IS_RECORDING;\n}\n\n/**\n * A request handler that intercepts requests and routes them through\n * the Holodeck mock server.\n *\n * This handler modifies the request URL to include test identifiers\n * and manages request counts for accurate mocking.\n *\n * Requires that the test context be configured with a testId using `setTestId`.\n *\n * @param owner - the test context object used to retrieve the test ID.\n */\nexport class MockServerHandler implements Handler {\n declare owner: object;\n constructor(owner: object) {\n this.owner = owner;\n }\n async request<T>(context: RequestContext, next: NextFn<T>): Promise<StructuredDataDocument<T>> {\n const { request, queryForTest } = setupHolodeckFetch(this.owner, Object.assign({}, context.request));\n\n try {\n const future = next(request);\n context.setStream(future.getStream());\n return await future;\n } catch (e) {\n if (e instanceof Error && !(e instanceof DOMException)) {\n e.message = e.message.replace(queryForTest, '');\n }\n throw e;\n }\n }\n}\n\nfunction setupHolodeckFetch(owner: object, request: RequestInfo): { request: RequestInfo; queryForTest: string } {\n const test = TEST_IDS.get(owner);\n if (!test) {\n throw new Error(`MockServerHandler is not configured with a testId. Use setTestId to set the testId for each test`);\n }\n\n const url = request.url!;\n const firstChar = url.includes('?') ? '&' : '?';\n const method = (request.method?.toUpperCase() ?? 'GET') as HTTPMethod;\n\n // enable custom methods\n if (!test.request[method]) {\n // oxlint-disable-next-line no-console\n console.log(`⚠️ Using custom HTTP method ${method} for response to request ${url}`);\n\n test.request[method] = {};\n }\n if (!(url in test.request[method])) {\n test.request[method][url] = 0;\n }\n\n const queryForTest = `${firstChar}__xTestId=${test.id}&__xTestRequestNumber=${test.request[method][url]++}`;\n request.url = url + queryForTest;\n request.method = method;\n\n request.mode = 'cors';\n request.credentials = 'omit';\n request.referrerPolicy = '';\n\n // since holodeck currently runs on a separate port\n // and we don't want to trigger cors pre-flight\n // we convert PUT to POST to keep the request in the\n // \"simple\" cors category.\n // if (request.method === 'PUT') {\n // request.method = 'POST';\n // }\n\n const headers = new Headers(request.headers);\n if (headers.has('Content-Type')) {\n // under the rules of simple-cors, content-type can only be\n // one of three things, none of which are what folks typically\n // set this to. Since holodeck always expects body to be JSON\n // this \"just works\".\n headers.set('Content-Type', 'text/plain');\n request.headers = headers;\n }\n\n return { request, queryForTest };\n}\n\ninterface HasAdapterForFn {\n adapterFor(this: Store, modelName: string): MinimumAdapterInterface;\n adapterFor(this: Store, modelName: string, _allowMissing?: true): MinimumAdapterInterface | undefined;\n}\n\n/*\n _fetchRequest(options: FetchRequestInit): Promise<Response> {\n const fetchFunction = fetch();\n\n return fetchFunction(options.url, options);\n }\n*/\ninterface PrivateAdapter {\n _fetchRequest(options: RequestInfo): Promise<Response>;\n hasOverriddenFetch: boolean;\n useFetch: boolean;\n}\n\nfunction upgradeAdapter(adapter: unknown): asserts adapter is PrivateAdapter {}\nfunction upgradeStore(store: Store): asserts store is Store & { adapterFor: HasAdapterForFn['adapterFor'] } {\n if (typeof store.adapterFor !== 'function') {\n throw new Error('Store is not compatible with Holodeck. Missing adapterFor method.');\n }\n}\n\n/**\n * Creates an adapterFor function that wraps the provided adapterFor function\n * to override the adapter's _fetchRequest method to route requests through\n * the Holodeck mock server.\n *\n * @param owner - The test context object used to retrieve the test ID.\n */\nexport function installAdapterFor(owner: object, store: Store): void {\n upgradeStore(store);\n const fn = store.adapterFor;\n function holodeckAdapterFor(\n this: Store,\n modelName: string,\n _allowMissing?: true\n ): MinimumAdapterInterface | undefined {\n const adapter = fn.call(this, modelName, _allowMissing);\n\n if (adapter) {\n upgradeAdapter(adapter);\n\n if (!adapter.hasOverriddenFetch) {\n adapter.hasOverriddenFetch = true;\n adapter.useFetch = true;\n const originalFetch = adapter._fetchRequest?.bind(adapter);\n\n adapter._fetchRequest = function (options: RequestInfo) {\n if (!originalFetch) {\n throw new Error(`Adapter ${String(modelName)} does not implement _fetchRequest`);\n }\n const { request } = setupHolodeckFetch(owner, options);\n\n return originalFetch(request);\n };\n }\n }\n\n return adapter;\n }\n store.adapterFor = holodeckAdapterFor as HasAdapterForFn['adapterFor'];\n}\n\n/**\n * Mock a request by sending the scaffold to the mock server.\n *\n * @public\n */\nexport async function mock(owner: object, generate: ScaffoldGenerator, isRecording?: boolean): Promise<void> {\n if (getIsRecording() || isRecording) {\n const test = TEST_IDS.get(owner);\n if (!test) {\n throw new Error(`Cannot call \"mock\" before configuring a testId. Use setTestId to set the testId for each test`);\n }\n const requestToMock = generate();\n const { url: mockUrl, method } = requestToMock;\n if (!mockUrl || !method) {\n throw new Error(`MockError: Cannot mock a request without providing a URL and Method`);\n }\n const mockMethod = (method?.toUpperCase() ?? 'GET') as HTTPMethod;\n\n // enable custom methods\n if (!test.mock[mockMethod]) {\n // oxlint-disable-next-line no-console\n console.log(`⚠️ Using custom HTTP method ${mockMethod} for response to request ${mockUrl}`);\n test.mock[mockMethod] = {};\n }\n if (!(mockUrl in test.mock[mockMethod])) {\n test.mock[mockMethod][mockUrl] = 0;\n }\n const testMockNum = test.mock[mockMethod][mockUrl]++;\n const url = `${HOST}__record?__xTestId=${test.id}&__xTestRequestNumber=${testMockNum}`;\n await fetch(url, {\n method: 'POST',\n body: JSON.stringify(requestToMock),\n mode: 'cors',\n credentials: 'omit',\n referrerPolicy: '',\n });\n }\n}\n"],"mappings":"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"}