@ethlete/query 6.0.0-next.6 → 6.0.0-next.60

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.
Files changed (54) hide show
  1. package/CHANGELOG.md +939 -0
  2. package/fesm2022/ethlete-query-devtools-contract.mjs +1 -0
  3. package/fesm2022/ethlete-query-ngrx-toolkit.mjs +411 -0
  4. package/fesm2022/ethlete-query-ngrx-toolkit.mjs.map +1 -0
  5. package/fesm2022/ethlete-query-testing.mjs +548 -169
  6. package/fesm2022/ethlete-query-testing.mjs.map +1 -1
  7. package/fesm2022/ethlete-query.mjs +14680 -7172
  8. package/fesm2022/ethlete-query.mjs.map +1 -1
  9. package/generators/deprecate-legacy-queries/migration.js +85 -0
  10. package/generators/deprecate-legacy-queries/schema.json +26 -0
  11. package/generators/generators.json +30 -0
  12. package/generators/migrate-from-ngrx-toolkit/args-assignability.js +140 -0
  13. package/generators/migrate-from-ngrx-toolkit/consumers.js +521 -0
  14. package/generators/migrate-from-ngrx-toolkit/creators.js +301 -0
  15. package/generators/migrate-from-ngrx-toolkit/facade.js +295 -0
  16. package/generators/migrate-from-ngrx-toolkit/feature.js +513 -0
  17. package/generators/migrate-from-ngrx-toolkit/migration.js +140 -0
  18. package/generators/migrate-from-ngrx-toolkit/report.js +154 -0
  19. package/generators/migrate-from-ngrx-toolkit/schema.json +42 -0
  20. package/generators/migrate-from-ngrx-toolkit/store-removal.js +633 -0
  21. package/generators/migrate-from-ngrx-toolkit/ts-edits.js +128 -0
  22. package/generators/migrate-query-client-features/migration.js +144 -0
  23. package/generators/migrate-query-client-features/report.js +136 -0
  24. package/generators/migrate-query-client-features/schema.json +26 -0
  25. package/generators/migrate-query-opt-in-features/migration.js +195 -0
  26. package/generators/migrate-query-opt-in-features/report.js +143 -0
  27. package/generators/migrate-query-opt-in-features/schema.json +31 -0
  28. package/generators/migrate-to-query-v3/auth-provider-scaffold.js +160 -0
  29. package/generators/migrate-to-query-v3/cleanup-migration.js +247 -125
  30. package/generators/migrate-to-query-v3/http-client-check.js +93 -0
  31. package/generators/migrate-to-query-v3/legacy-prepare-migration.js +129 -40
  32. package/generators/migrate-to-query-v3/legacy-query-creator-migration.js +274 -152
  33. package/generators/migrate-to-query-v3/migration-scope.js +40 -0
  34. package/generators/migrate-to-query-v3/migration.js +19 -10
  35. package/generators/migrate-to-query-v3/module-graph.js +165 -0
  36. package/generators/migrate-to-query-v3/query-client-migration.js +342 -160
  37. package/generators/migrate-to-query-v3/rename-symbols.js +233 -0
  38. package/generators/migrate-to-query-v3/report.js +40 -7
  39. package/generators/migrate-to-query-v3/schema.json +12 -0
  40. package/generators/migrate-to-query-v3/shared.js +97 -3
  41. package/generators/move-devtools-contract-imports/migration.js +155 -0
  42. package/generators/move-devtools-contract-imports/moved-names.js +153 -0
  43. package/generators/move-devtools-contract-imports/schema.json +26 -0
  44. package/generators/prep-for-query-v3/migration.js +465 -65
  45. package/generators/report-legacy-query-apis/migration.js +256 -0
  46. package/generators/report-legacy-query-apis/schema.json +25 -0
  47. package/migrations/query-field-default-types.md +43 -0
  48. package/migrations/search-query-field-string.md +43 -0
  49. package/migrations.json +87 -0
  50. package/package.json +34 -14
  51. package/types/ethlete-query-devtools-contract.d.ts +2 -0
  52. package/types/ethlete-query-ngrx-toolkit.d.ts +131 -0
  53. package/types/ethlete-query-testing.d.ts +295 -160
  54. package/types/ethlete-query.d.ts +9755 -3682
@@ -1,178 +1,557 @@
1
- import { DestroyRef, ErrorHandler, Injector } from '@angular/core';
2
- import { TestBed } from '@angular/core/testing';
3
- import { createBearerAuthProvider, withAuthenticationQuery, withRefreshQuery, createQueryClient, createDeleteQuery, createPatchQuery, createPutQuery, createPostQuery, createGetQuery } from '@ethlete/query';
4
- import { provideHttpClient, HttpClient } from '@angular/common/http';
5
- import { provideHttpClientTesting, HttpTestingController } from '@angular/common/http/testing';
6
- import { provideRouter } from '@angular/router';
7
-
8
- /* eslint-disable @typescript-eslint/no-explicit-any */
1
+ import { DestroyRef, ErrorHandler, Injector } from "@angular/core";
2
+ import { TestBed } from "@angular/core/testing";
3
+ import { createBearerAuthProvider, createDeleteQuery, createGetQuery, createPatchQuery, createPostQuery, createPutQuery, createQueryClient, isQueryErrorResponse, withAuthenticationQuery, withRefreshQuery } from "@ethlete/query";
4
+ import { HttpClient, HttpErrorResponse, provideHttpClient } from "@angular/common/http";
5
+ import { HttpTestingController, provideHttpClientTesting } from "@angular/common/http/testing";
6
+ import { provideRouter } from "@angular/router";
9
7
  const expectAndFlush = (httpTesting, url, response, statusOpts) => {
10
- const req = httpTesting.expectOne(url);
11
- if (statusOpts) {
12
- req.flush(response, statusOpts);
13
- }
14
- else {
15
- req.flush(response);
16
- }
8
+ const req = httpTesting.expectOne(url);
9
+ if (statusOpts) req.flush(response, statusOpts);
10
+ else req.flush(response);
17
11
  };
18
12
  const expectFlushAndWait = (httpTesting, url, response, statusOpts) => {
19
- expectAndFlush(httpTesting, url, response, statusOpts);
20
- TestBed.tick();
13
+ expectAndFlush(httpTesting, url, response, statusOpts);
14
+ TestBed.tick();
21
15
  };
22
-
23
16
  const setupAuthTest = (config) => {
24
- const { querySetup, loginPath = '/auth/login', refreshPath = '/auth/refresh', autoRetryOn401 = false, extractLoginTokens = (response) => ({
25
- accessToken: response.accessToken,
26
- refreshToken: response.refreshToken,
27
- }), extractRefreshTokens = (response) => ({
28
- accessToken: response.accessToken,
29
- refreshToken: response.refreshToken,
30
- }), features, bearerDecryptFn, multiTabSync = false, refreshStrategy, minRefreshInterval, refreshIfExpired, expiresInPropertyName, } = config;
31
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
- const login = querySetup.createPost(loginPath, {});
33
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
- const refresh = querySetup.createPost(refreshPath, {});
35
- const [, injectAuth] = createBearerAuthProvider({
36
- name: 'test-auth',
37
- queryClientRef: querySetup.queryClientRef,
38
- queries: [
39
- withAuthenticationQuery('login', {
40
- queryCreator: login,
41
- extractTokens: extractLoginTokens,
42
- }),
43
- withRefreshQuery('refresh', {
44
- queryCreator: refresh,
45
- extractTokens: extractRefreshTokens,
46
- autoRetryOn401,
47
- refreshStrategy,
48
- minRefreshInterval,
49
- refreshIfExpired,
50
- expiresInPropertyName,
51
- }),
52
- ],
53
- features: (features ?? []),
54
- bearerDecryptFn,
55
- multiTabSync,
56
- });
57
- const auth = TestBed.runInInjectionContext(() => {
58
- const provider = injectAuth();
59
- if (!provider) {
60
- throw new Error('Failed to create auth provider in test setup');
61
- }
62
- return provider;
63
- });
64
- const loginHelper = (credentials, response) => {
65
- TestBed.runInInjectionContext(() => {
66
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
67
- auth.queries.login.execute({ body: credentials });
68
- });
69
- const fullUrl = `${querySetup.baseUrl}${loginPath}`;
70
- expectFlushAndWait(querySetup.httpTesting, fullUrl, response);
71
- };
72
- const refreshHelper = (token, response) => {
73
- TestBed.runInInjectionContext(() => {
74
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
75
- auth.queries.refresh.execute({ body: { token } });
76
- });
77
- const fullUrl = `${querySetup.baseUrl}${refreshPath}`;
78
- expectFlushAndWait(querySetup.httpTesting, fullUrl, response);
79
- };
80
- const makeSecureRequest = (route) => {
81
- TestBed.runInInjectionContext(() => {
82
- querySetup.queryClient.repository.request({
83
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
- route: route,
85
- method: 'GET',
86
- isSecure: true,
87
- consumerDestroyRef: TestBed.inject(DestroyRef),
88
- });
89
- });
90
- };
91
- return {
92
- auth,
93
- login: loginHelper,
94
- refresh: refreshHelper,
95
- makeSecureRequest,
96
- };
17
+ const { querySetup, loginPath = "/auth/login", refreshPath = "/auth/refresh", autoRetryOn401 = true, extractLoginTokens = (response) => ({
18
+ accessToken: response.accessToken,
19
+ refreshToken: response.refreshToken
20
+ }), extractRefreshTokens = (response) => ({
21
+ accessToken: response.accessToken,
22
+ refreshToken: response.refreshToken
23
+ }), buildRefreshArgs, features, bearerDecryptFn, refreshStrategy, minRefreshInterval, refreshIfExpired, expiresInPropertyName, onRefreshFailure } = config;
24
+ const login = querySetup.createPost(loginPath, {});
25
+ const refresh = querySetup.createPost(refreshPath, {});
26
+ const { inject: injectAuth } = createBearerAuthProvider({
27
+ name: "test-auth",
28
+ queryClientRef: querySetup.queryClientRef,
29
+ queries: [withAuthenticationQuery("login", {
30
+ queryCreator: login,
31
+ extractTokens: extractLoginTokens
32
+ }), withRefreshQuery("refresh", {
33
+ queryCreator: refresh,
34
+ extractTokens: extractRefreshTokens,
35
+ buildArgs: buildRefreshArgs,
36
+ autoRetryOn401,
37
+ refreshStrategy,
38
+ minRefreshInterval,
39
+ refreshIfExpired,
40
+ expiresInPropertyName,
41
+ onRefreshFailure
42
+ })],
43
+ features: features ?? [],
44
+ bearerDecryptFn
45
+ });
46
+ const auth = TestBed.runInInjectionContext(() => {
47
+ const provider = injectAuth();
48
+ if (!provider) throw new Error("Failed to create auth provider in test setup");
49
+ return provider;
50
+ });
51
+ const loginHelper = (credentials, response) => {
52
+ TestBed.runInInjectionContext(() => {
53
+ auth.queries.login.execute({ body: credentials });
54
+ });
55
+ const fullUrl = `${querySetup.baseUrl}${loginPath}`;
56
+ expectFlushAndWait(querySetup.httpTesting, fullUrl, response);
57
+ };
58
+ const refreshHelper = (token, response) => {
59
+ TestBed.runInInjectionContext(() => {
60
+ auth.queries.refresh.execute(buildRefreshArgs?.(token) ?? { body: { token } });
61
+ });
62
+ const fullUrl = `${querySetup.baseUrl}${refreshPath}`;
63
+ expectFlushAndWait(querySetup.httpTesting, fullUrl, response);
64
+ };
65
+ const makeSecureRequest = (route) => {
66
+ TestBed.runInInjectionContext(() => {
67
+ querySetup.queryClient.repository.request({
68
+ route,
69
+ method: "GET",
70
+ isSecure: true,
71
+ consumerDestroyRef: TestBed.inject(DestroyRef)
72
+ });
73
+ });
74
+ };
75
+ return {
76
+ auth,
77
+ login: loginHelper,
78
+ refresh: refreshHelper,
79
+ makeSecureRequest
80
+ };
97
81
  };
98
-
82
+ const nodeSetImmediate = globalThis.setImmediate;
83
+ const scheduleTask = nodeSetImmediate ? nodeSetImmediate.bind(globalThis) : setTimeout.bind(globalThis);
84
+ const installFakeBroadcastChannel = () => {
85
+ const original = globalThis.BroadcastChannel;
86
+ const instances = /* @__PURE__ */ new Set();
87
+ const posted = [];
88
+ class FakeBroadcastChannel {
89
+ constructor(name) {
90
+ this.name = name;
91
+ this.closed = false;
92
+ this.onmessage = null;
93
+ instances.add(this);
94
+ }
95
+ postMessage(data) {
96
+ if (this.closed) throw new DOMException("Channel is closed", "InvalidStateError");
97
+ const clone = structuredClone(data);
98
+ posted.push({
99
+ channel: this.name,
100
+ data: clone
101
+ });
102
+ for (const instance of instances) {
103
+ if (instance === this || instance.closed || instance.name !== this.name) continue;
104
+ scheduleTask(() => {
105
+ if (instance.closed) return;
106
+ instance.onmessage?.({ data: structuredClone(clone) });
107
+ });
108
+ }
109
+ }
110
+ close() {
111
+ this.closed = true;
112
+ instances.delete(this);
113
+ }
114
+ addEventListener() {
115
+ throw new Error("The fake BroadcastChannel only supports `onmessage`.");
116
+ }
117
+ }
118
+ globalThis.BroadcastChannel = FakeBroadcastChannel;
119
+ return {
120
+ posted,
121
+ restore: () => {
122
+ for (const instance of [...instances]) instance.closed = true;
123
+ instances.clear();
124
+ globalThis.BroadcastChannel = original;
125
+ }
126
+ };
127
+ };
128
+ const installFakeWebLocks = () => {
129
+ const originalDescriptor = Object.getOwnPropertyDescriptor(navigator, "locks");
130
+ const holders = /* @__PURE__ */ new Map();
131
+ const queues = /* @__PURE__ */ new Map();
132
+ const abortError = () => new DOMException("The lock request was aborted", "AbortError");
133
+ const pump = (name) => {
134
+ if (holders.has(name)) return;
135
+ const waiter = queues.get(name)?.shift();
136
+ if (!waiter) return;
137
+ waiter.granted = true;
138
+ holders.set(name, waiter);
139
+ const settle = (settleWaiter) => {
140
+ if (holders.get(name) === waiter) holders.delete(name);
141
+ waiter.releaseSignal();
142
+ settleWaiter();
143
+ pump(name);
144
+ };
145
+ try {
146
+ Promise.resolve(waiter.callback({
147
+ name,
148
+ mode: "exclusive"
149
+ })).then((value) => settle(() => waiter.resolve(value)), (error) => settle(() => waiter.reject(error)));
150
+ } catch (error) {
151
+ settle(() => waiter.reject(error));
152
+ }
153
+ };
154
+ const request = (name, optionsOrCallback, maybeCallback) => {
155
+ const options = typeof optionsOrCallback === "function" ? {} : optionsOrCallback ?? {};
156
+ const callback = typeof optionsOrCallback === "function" ? optionsOrCallback : maybeCallback;
157
+ return new Promise((resolve, reject) => {
158
+ if (options.signal?.aborted) {
159
+ reject(abortError());
160
+ return;
161
+ }
162
+ const waiter = {
163
+ callback,
164
+ resolve,
165
+ reject,
166
+ granted: false,
167
+ releaseSignal: () => void 0
168
+ };
169
+ const signal = options.signal;
170
+ if (signal) {
171
+ const onAbort = () => {
172
+ waiter.releaseSignal();
173
+ if (waiter.granted) return;
174
+ const queue = queues.get(name);
175
+ if (queue) queues.set(name, queue.filter((entry) => entry !== waiter));
176
+ reject(abortError());
177
+ };
178
+ signal.addEventListener("abort", onAbort);
179
+ waiter.releaseSignal = () => {
180
+ waiter.releaseSignal = () => void 0;
181
+ signal.removeEventListener("abort", onAbort);
182
+ };
183
+ }
184
+ if (options.steal) {
185
+ queueMicrotask(() => {
186
+ const holder = holders.get(name);
187
+ if (holder) {
188
+ holders.delete(name);
189
+ holder.reject(abortError());
190
+ }
191
+ queues.set(name, [waiter, ...queues.get(name) ?? []]);
192
+ pump(name);
193
+ });
194
+ return;
195
+ }
196
+ if (options.ifAvailable) {
197
+ queueMicrotask(() => {
198
+ if (holders.has(name) || (queues.get(name)?.length ?? 0) > 0) {
199
+ waiter.releaseSignal();
200
+ Promise.resolve(callback(null)).then(resolve, reject);
201
+ return;
202
+ }
203
+ queues.set(name, [waiter]);
204
+ pump(name);
205
+ });
206
+ return;
207
+ }
208
+ queues.set(name, [...queues.get(name) ?? [], waiter]);
209
+ queueMicrotask(() => pump(name));
210
+ });
211
+ };
212
+ const query = () => Promise.resolve({
213
+ held: [...holders.keys()].map((name) => ({
214
+ name,
215
+ mode: "exclusive"
216
+ })),
217
+ pending: [...queues.entries()].flatMap(([name, waiters]) => waiters.map(() => ({
218
+ name,
219
+ mode: "exclusive"
220
+ })))
221
+ });
222
+ Object.defineProperty(navigator, "locks", {
223
+ value: {
224
+ request,
225
+ query
226
+ },
227
+ configurable: true,
228
+ writable: true
229
+ });
230
+ return {
231
+ heldNames: () => [...holders.keys()],
232
+ pendingNames: () => [...queues.entries()].filter(([, queue]) => queue.length > 0).map(([name]) => name),
233
+ restore: () => {
234
+ for (const waiter of [...holders.values(), ...[...queues.values()].flat()]) waiter.releaseSignal();
235
+ holders.clear();
236
+ queues.clear();
237
+ if (originalDescriptor) Object.defineProperty(navigator, "locks", originalDescriptor);
238
+ else delete navigator.locks;
239
+ }
240
+ };
241
+ };
242
+ const flushMultiTabSync = () => new Promise((resolve) => scheduleTask(resolve));
243
+ const quotaError = () => new DOMException("The quota has been exceeded.", "QuotaExceededError");
244
+ const createFakeQueryPersistenceStore = () => {
245
+ const store = /* @__PURE__ */ new Map();
246
+ const calls = {
247
+ loadIndex: 0,
248
+ read: 0,
249
+ write: 0,
250
+ remove: 0,
251
+ clear: 0
252
+ };
253
+ const deferredReads = [];
254
+ const deferredLoadIndexes = [];
255
+ let failingWrites = 0;
256
+ let writeError = null;
257
+ let failingRemoves = 0;
258
+ let removeError = null;
259
+ let loadIndexError = null;
260
+ let isDeferringReads = false;
261
+ let isDeferringLoadIndex = false;
262
+ const clone = (value) => structuredClone(value);
263
+ const loadIndex = async () => {
264
+ calls.loadIndex++;
265
+ if (loadIndexError !== null) {
266
+ const error = loadIndexError;
267
+ loadIndexError = null;
268
+ throw error;
269
+ }
270
+ const snapshot = Array.from(store.values()).map(({ body: _body, ...meta }) => clone(meta));
271
+ if (isDeferringLoadIndex) await new Promise((resolve) => deferredLoadIndexes.push(resolve));
272
+ return snapshot;
273
+ };
274
+ const read = async (key) => {
275
+ calls.read++;
276
+ const entry = store.get(key);
277
+ const snapshot = entry ? {
278
+ body: clone(entry.body),
279
+ version: entry.version
280
+ } : null;
281
+ if (isDeferringReads) await new Promise((resolve) => deferredReads.push(resolve));
282
+ return snapshot;
283
+ };
284
+ const write = async (entries) => {
285
+ calls.write++;
286
+ if (failingWrites > 0) {
287
+ failingWrites--;
288
+ throw writeError ?? quotaError();
289
+ }
290
+ for (const entry of entries) store.set(entry.key, clone(entry));
291
+ };
292
+ const remove = async (keys) => {
293
+ calls.remove++;
294
+ if (failingRemoves > 0) {
295
+ failingRemoves--;
296
+ throw removeError ?? /* @__PURE__ */ new Error("The removal failed.");
297
+ }
298
+ for (const key of keys) store.delete(key);
299
+ };
300
+ const clear = async () => {
301
+ calls.clear++;
302
+ store.clear();
303
+ };
304
+ return {
305
+ adapter: {
306
+ loadIndex,
307
+ read,
308
+ write,
309
+ remove,
310
+ clear,
311
+ isSupported: true
312
+ },
313
+ seed: (entries) => {
314
+ for (const entry of entries) store.set(entry.key, clone(entry));
315
+ },
316
+ entries: () => Array.from(store.values()).map(clone),
317
+ entry: (key) => {
318
+ const entry = store.get(key);
319
+ return entry ? clone(entry) : void 0;
320
+ },
321
+ calls: () => ({ ...calls }),
322
+ failNextWrites: (count, error) => {
323
+ failingWrites = count;
324
+ writeError = error ?? null;
325
+ },
326
+ failNextRemoves: (count, error) => {
327
+ failingRemoves = count;
328
+ removeError = error ?? null;
329
+ },
330
+ failNextLoadIndex: (error) => {
331
+ loadIndexError = error ?? quotaError();
332
+ },
333
+ deferReads: () => {
334
+ isDeferringReads = true;
335
+ },
336
+ flushReads: async () => {
337
+ isDeferringReads = false;
338
+ const pending = deferredReads.splice(0, deferredReads.length);
339
+ for (const resolve of pending) resolve();
340
+ await Promise.resolve();
341
+ },
342
+ deferLoadIndex: () => {
343
+ isDeferringLoadIndex = true;
344
+ },
345
+ flushLoadIndex: async () => {
346
+ isDeferringLoadIndex = false;
347
+ const pending = deferredLoadIndexes.splice(0, deferredLoadIndexes.length);
348
+ for (const resolve of pending) resolve();
349
+ await Promise.resolve();
350
+ }
351
+ };
352
+ };
353
+ let originalError = null;
354
+ const filteredError = (...args) => {
355
+ const message = args[0];
356
+ if (message && typeof message === "object" && "name" in message && message.name === "HttpErrorResponse") return;
357
+ if (typeof message === "string" && message.includes("Failed to decrypt bearer token")) return;
358
+ if (typeof message === "string" && message.includes("Failed to extract tokens from")) return;
359
+ originalError?.(...args);
360
+ };
361
+ const installConsoleFilters = () => {
362
+ if (console.error !== filteredError) originalError = console.error;
363
+ console.error = filteredError;
364
+ };
365
+ const restoreConsole = () => {
366
+ if (originalError && console.error === filteredError) console.error = originalError;
367
+ originalError = null;
368
+ };
369
+ const isFailedRequestError = (error) => error instanceof HttpErrorResponse || isQueryErrorResponse(error);
370
+ const requestErrorHandler = { handleError: (error) => {
371
+ if (isFailedRequestError(error)) return;
372
+ throw error;
373
+ } };
99
374
  const setupQueryTest = (config) => {
100
- const baseUrl = config?.baseUrl ?? 'https://api.test.com';
101
- const name = config?.name ?? 'test';
102
- const mockErrorHandler = config?.mockErrorHandler !== false;
103
- // Suppress console.warn for auth-related warnings during tests
104
- const originalWarn = console.warn;
105
- console.warn = (...args) => {
106
- const message = args[0];
107
- if (typeof message === 'string' && message.includes('auto-refresh')) {
108
- return; // Suppress auto-refresh warnings
109
- }
110
- originalWarn(...args);
111
- };
112
- // Suppress console.error for expected error scenarios during tests
113
- const originalError = console.error;
114
- console.error = (...args) => {
115
- const message = args[0];
116
- // Suppress HttpErrorResponse logs
117
- if (message && typeof message === 'object' && 'name' in message && message.name === 'HttpErrorResponse') {
118
- return;
119
- }
120
- // Suppress bearer token decryption errors
121
- if (typeof message === 'string' && message.includes('Failed to decrypt bearer token')) {
122
- return;
123
- }
124
- // Suppress token extraction errors
125
- if (typeof message === 'string' && message.includes('Failed to extract tokens from')) {
126
- return;
127
- }
128
- originalError(...args);
129
- };
130
- const providers = [
131
- provideHttpClient(),
132
- provideHttpClientTesting(),
133
- provideRouter([]),
134
- ];
135
- if (mockErrorHandler) {
136
- providers.push({
137
- provide: ErrorHandler,
138
- useValue: { handleError: () => undefined },
139
- });
140
- }
141
- TestBed.configureTestingModule({ providers });
142
- const queryClientRef = createQueryClient({ baseUrl, name });
143
- const setup = TestBed.runInInjectionContext(() => {
144
- const [, inject] = queryClientRef;
145
- const queryClient = inject();
146
- if (!queryClient) {
147
- throw new Error('Failed to create query client in test setup');
148
- }
149
- return {
150
- queryClient,
151
- queryClientRef,
152
- httpClient: TestBed.inject(HttpClient),
153
- httpTesting: TestBed.inject(HttpTestingController),
154
- injector: TestBed.inject(Injector),
155
- baseUrl,
156
- createGet: createGetQuery(queryClientRef),
157
- createPost: createPostQuery(queryClientRef),
158
- createPut: createPutQuery(queryClientRef),
159
- createPatch: createPatchQuery(queryClientRef),
160
- createDelete: createDeleteQuery(queryClientRef),
161
- };
162
- });
163
- // Restore console.warn and console.error when tests are done
164
- const restoreConsole = () => {
165
- console.warn = originalWarn;
166
- console.error = originalError;
167
- };
168
- // Store the restore function for cleanup
169
- setup._restoreConsole = restoreConsole;
170
- return setup;
375
+ const baseUrl = config?.baseUrl ?? "https://api.test.com";
376
+ const name = config?.name ?? "test";
377
+ const mockErrorHandler = config?.mockErrorHandler ?? true;
378
+ installConsoleFilters();
379
+ const providers = [
380
+ provideHttpClient(),
381
+ provideHttpClientTesting(),
382
+ provideRouter([])
383
+ ];
384
+ if (mockErrorHandler) providers.push({
385
+ provide: ErrorHandler,
386
+ useValue: mockErrorHandler === "all" ? { handleError: () => void 0 } : requestErrorHandler
387
+ });
388
+ TestBed.configureTestingModule({ providers });
389
+ const queryClientRef = createQueryClient({
390
+ baseUrl,
391
+ name
392
+ });
393
+ return TestBed.runInInjectionContext(() => {
394
+ const { inject } = queryClientRef;
395
+ const queryClient = inject();
396
+ TestBed.inject(DestroyRef).onDestroy(restoreConsole);
397
+ if (!queryClient) throw new Error("Failed to create query client in test setup");
398
+ return {
399
+ queryClient,
400
+ queryClientRef,
401
+ httpClient: TestBed.inject(HttpClient),
402
+ httpTesting: TestBed.inject(HttpTestingController),
403
+ injector: TestBed.inject(Injector),
404
+ baseUrl,
405
+ createGet: createGetQuery(queryClientRef),
406
+ createPost: createPostQuery(queryClientRef),
407
+ createPut: createPutQuery(queryClientRef),
408
+ createPatch: createPatchQuery(queryClientRef),
409
+ createDelete: createDeleteQuery(queryClientRef),
410
+ restoreConsole
411
+ };
412
+ });
171
413
  };
414
+ const createWebSocketTestDouble = () => {
415
+ const sent = [];
416
+ const delivered = [];
417
+ const buffered = [];
418
+ const listeners = {
419
+ connect: [],
420
+ disconnect: [],
421
+ connect_error: []
422
+ };
423
+ const anyListeners = [];
424
+ const outgoingListeners = [];
425
+ let connection = null;
426
+ let withCredentials = null;
427
+ let auth;
428
+ const handshakes = [];
429
+ let connectRequested = false;
430
+ let disconnected = false;
431
+ let connected = false;
432
+ let active = false;
433
+ let connectCalls = 0;
434
+ let pingExpired = false;
435
+ let recovered = false;
436
+ const deliver = (message) => {
437
+ for (const listener of outgoingListeners) listener(message.event, message.data);
438
+ delivered.push(message);
439
+ };
440
+ const recordHandshake = () => {
441
+ if (auth) auth((data) => handshakes.push(data));
442
+ else handshakes.push(null);
443
+ };
444
+ const disconnect = (reason) => {
445
+ connected = false;
446
+ pingExpired = false;
447
+ for (const listener of listeners.disconnect) listener(reason);
448
+ };
449
+ const socket = {
450
+ connect: () => {
451
+ connectRequested = true;
452
+ active = true;
453
+ connectCalls++;
454
+ },
455
+ disconnect: () => {
456
+ disconnected = true;
457
+ active = false;
458
+ if (connected) disconnect("io client disconnect");
459
+ },
460
+ emit: (event, data) => {
461
+ sent.push({
462
+ event,
463
+ data
464
+ });
465
+ if (connected && !pingExpired) deliver({
466
+ event,
467
+ data
468
+ });
469
+ else buffered.push({
470
+ event,
471
+ data
472
+ });
473
+ },
474
+ on: (event, listener) => void listeners[event].push(listener),
475
+ onAny: (listener) => void anyListeners.push(listener),
476
+ onAnyOutgoing: (listener) => void outgoingListeners.push(listener),
477
+ get recovered() {
478
+ return recovered;
479
+ },
480
+ get active() {
481
+ return active;
482
+ },
483
+ get sendBuffer() {
484
+ return buffered;
485
+ }
486
+ };
487
+ return {
488
+ io: (url, options) => {
489
+ connection = {
490
+ url,
491
+ transports: options.transports
492
+ };
493
+ withCredentials = options.withCredentials;
494
+ auth = options.auth;
495
+ return socket;
496
+ },
497
+ connection: () => connection,
498
+ withCredentials: () => withCredentials,
499
+ handshakes: () => [...handshakes],
500
+ sent: () => [...sent],
501
+ delivered: () => [...delivered],
502
+ state: () => ({
503
+ connectRequested,
504
+ disconnected,
505
+ active,
506
+ connectCalls
507
+ }),
508
+ serverConnect: (options) => {
509
+ recordHandshake();
510
+ active = true;
511
+ recovered = options?.recovered ?? false;
512
+ connected = true;
513
+ pingExpired = false;
514
+ for (const message of buffered.splice(0)) deliver(message);
515
+ for (const listener of listeners.connect) listener();
516
+ },
517
+ serverDisconnect: (options) => {
518
+ const reason = options?.reason ?? "transport close";
519
+ if (reason === "io server disconnect") active = false;
520
+ disconnect(reason);
521
+ },
522
+ serverRejectHandshake: (message) => {
523
+ recordHandshake();
524
+ active = false;
525
+ for (const listener of listeners.connect_error) listener(new Error(message ?? "unauthorized"));
526
+ },
527
+ serverPingExpire: () => void (pingExpired = true),
528
+ serverSend: (message) => {
529
+ const frame = JSON.stringify(message);
530
+ for (const listener of anyListeners) listener("message", frame);
531
+ },
532
+ serverSendRaw: (frame) => {
533
+ for (const listener of anyListeners) listener("message", frame);
534
+ }
535
+ };
536
+ };
537
+ const toBase64Url = (json) => {
538
+ return btoa(encodeURIComponent(json).replace(/%([0-9A-F]{2})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
539
+ };
540
+ let mintCount = 0;
541
+ const mintTestToken = (options = {}) => {
542
+ const { expiresInMs = 9e5, claims = {} } = options;
543
+ const iat = Math.floor(Date.now() / 1e3);
544
+ const exp = Math.floor((Date.now() + expiresInMs) / 1e3);
545
+ return `${toBase64Url(JSON.stringify({
546
+ alg: "none",
547
+ typ: "JWT"
548
+ }))}.${toBase64Url(JSON.stringify({
549
+ iat,
550
+ exp,
551
+ jti: ++mintCount,
552
+ ...claims
553
+ }))}.`;
554
+ };
555
+ export { createFakeQueryPersistenceStore, createWebSocketTestDouble, expectAndFlush, expectFlushAndWait, flushMultiTabSync, installFakeBroadcastChannel, installFakeWebLocks, mintTestToken, setupAuthTest, setupQueryTest };
172
556
 
173
- /**
174
- * Generated bundle index. Do not edit.
175
- */
176
-
177
- export { expectAndFlush, expectFlushAndWait, setupAuthTest, setupQueryTest };
178
- //# sourceMappingURL=ethlete-query-testing.mjs.map
557
+ //# sourceMappingURL=ethlete-query-testing.mjs.map