@ethlete/query 0.1.0-next.9 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/esm2020/lib/auth/auth-provider.types.mjs +1 -1
  2. package/esm2020/lib/auth/auth-provider.utils.mjs +1 -1
  3. package/esm2020/lib/auth/basic-auth-provider.mjs +4 -5
  4. package/esm2020/lib/auth/bearer-auth-provider.mjs +117 -60
  5. package/esm2020/lib/auth/custom-header-auth-provider.mjs +4 -5
  6. package/esm2020/lib/directives/infinity-query-trigger.directive.mjs +3 -3
  7. package/esm2020/lib/directives/infinity-query.directive.mjs +7 -9
  8. package/esm2020/lib/directives/query.directive.mjs +23 -19
  9. package/esm2020/lib/infinite-query/infinity-query.mjs +15 -11
  10. package/esm2020/lib/query/query.mjs +111 -86
  11. package/esm2020/lib/query/query.types.mjs +1 -1
  12. package/esm2020/lib/query/query.utils.mjs +51 -4
  13. package/esm2020/lib/query-client/query-client.mjs +25 -13
  14. package/esm2020/lib/query-client/query-client.types.mjs +1 -1
  15. package/esm2020/lib/query-form/query-form.mjs +7 -6
  16. package/esm2020/lib/request/request.mjs +168 -19
  17. package/esm2020/lib/request/request.types.mjs +1 -1
  18. package/esm2020/lib/request/request.util.mjs +66 -91
  19. package/fesm2015/ethlete-query.mjs +673 -398
  20. package/fesm2015/ethlete-query.mjs.map +1 -1
  21. package/fesm2020/ethlete-query.mjs +657 -389
  22. package/fesm2020/ethlete-query.mjs.map +1 -1
  23. package/lib/auth/auth-provider.types.d.ts +30 -22
  24. package/lib/auth/auth-provider.utils.d.ts +2 -1
  25. package/lib/auth/basic-auth-provider.d.ts +0 -2
  26. package/lib/auth/bearer-auth-provider.d.ts +14 -14
  27. package/lib/auth/custom-header-auth-provider.d.ts +0 -2
  28. package/lib/directives/infinity-query.directive.d.ts +1 -2
  29. package/lib/directives/query.directive.d.ts +6 -5
  30. package/lib/infinite-query/infinity-query.types.d.ts +4 -4
  31. package/lib/query/query.d.ts +6 -6
  32. package/lib/query/query.types.d.ts +78 -43
  33. package/lib/query/query.utils.d.ts +21 -5
  34. package/lib/query-client/query-client.d.ts +13 -11
  35. package/lib/query-client/query-client.types.d.ts +22 -10
  36. package/lib/query-form/query-form.types.d.ts +2 -2
  37. package/lib/request/request.d.ts +3 -9
  38. package/lib/request/request.types.d.ts +63 -15
  39. package/lib/request/request.util.d.ts +74 -7
  40. package/package.json +2 -5
@@ -1,40 +1,60 @@
1
- import { Subject, timer, filter, takeWhile, BehaviorSubject, interval, takeUntil, tap, switchMap, of, fromEvent, startWith, concat, take, debounceTime, combineLatest, map, distinctUntilChanged } from 'rxjs';
1
+ import { getCookie, setCookie, deleteCookie, DestroyService, equal, clone } from '@ethlete/core';
2
+ import { Observable, filter, takeWhile, switchMap, of, BehaviorSubject, Subject, takeUntil, interval, tap, timer, fromEvent, startWith, concat, take, debounceTime, combineLatest, map, distinctUntilChanged } from 'rxjs';
2
3
  import * as i0 from '@angular/core';
3
4
  import { Directive, Input, InjectionToken, inject, ChangeDetectorRef, ViewContainerRef, TemplateRef, ErrorHandler, ElementRef } from '@angular/core';
4
- import * as i1 from '@ethlete/core';
5
- import { DestroyDirective } from '@ethlete/core';
6
5
  import { FormGroup } from '@angular/forms';
7
6
  import { Router, ActivatedRoute } from '@angular/router';
8
7
 
9
8
  class CustomHeaderAuthProvider {
10
- constructor(_config) {
11
- this._config = _config;
12
- this.queryClient = null;
13
- }
14
9
  get header() {
15
10
  return { [this._config.name]: this._config.value };
16
11
  }
12
+ constructor(_config) {
13
+ this._config = _config;
14
+ }
17
15
  cleanUp() {
18
16
  // noop
19
17
  }
20
18
  }
21
19
 
22
20
  class BasicAuthProvider {
23
- constructor(_config) {
24
- this._config = _config;
25
- this.queryClient = null;
26
- }
27
21
  get header() {
28
22
  return { Authorization: `Basic ${this._basicAuthString}` };
29
23
  }
30
24
  get _basicAuthString() {
31
25
  return btoa(`${this._config.username}:${this._config.password}`);
32
26
  }
27
+ constructor(_config) {
28
+ this._config = _config;
29
+ }
33
30
  cleanUp() {
34
31
  // noop
35
32
  }
36
33
  }
37
34
 
35
+ const getOpName = /(query|mutation) ?([\w\d-_]+)? ?\(.*?\)? \{/;
36
+ const transformGql = (str) => {
37
+ str = Array.isArray(str) ? str.join('') : str;
38
+ const name = getOpName.exec(str);
39
+ return function (variables) {
40
+ const data = { query: str };
41
+ if (variables)
42
+ data['variables'] = JSON.stringify(variables);
43
+ if (name && name.length) {
44
+ const operationName = name[2];
45
+ if (operationName)
46
+ data['operationName'] = name[2];
47
+ }
48
+ return JSON.stringify(data);
49
+ };
50
+ };
51
+ const gql = (strings, ...values) => {
52
+ const str = strings.reduce((acc, cur, i) => {
53
+ return acc + cur + (values[i] || '');
54
+ }, '');
55
+ return str;
56
+ };
57
+
38
58
  const buildErrorMessage = (code, message) => `[@ethlete\\query:${code}] ${message}`;
39
59
  class QueryError extends Error {
40
60
  constructor(code, message, data) {
@@ -52,7 +72,6 @@ const queryStateCannotTransformError = (data) => new QueryError('006', 'The quer
52
72
  const invalidBodyError = (data) => new QueryError('007', 'The body is not valid or upsupported', data);
53
73
 
54
74
  const isRequestError = (error) => error instanceof Object && 'code' in error && 'message' in error;
55
- const isAbortRequestError = (error) => isRequestError(error) && error.code === -1;
56
75
  const buildRoute = (options) => {
57
76
  if (options.base.endsWith('/')) {
58
77
  throw invalidBaseRouteError(options.base);
@@ -125,9 +144,9 @@ const isParamValid = (primitive) => {
125
144
  return true;
126
145
  };
127
146
  const extractExpiresInSeconds = (headers) => {
128
- const cacheControl = headers.get('cache-control');
129
- const age = headers.get('age');
130
- const expires = headers.get('expires');
147
+ const cacheControl = headers['cache-control'];
148
+ const age = headers['age'];
149
+ const expires = headers['expires'];
131
150
  // In seconds
132
151
  let expiresIn = null;
133
152
  let maxAge = null;
@@ -166,91 +185,21 @@ const buildTimestampFromSeconds = (seconds) => {
166
185
  }
167
186
  return new Date(Date.now() + seconds * 1000).getTime();
168
187
  };
169
- const buildRequestError = async (error, route, requestInit) => {
170
- if (error instanceof DOMException && error.code === error.ABORT_ERR) {
171
- const err = {
172
- code: -1,
173
- message: 'Request aborted',
174
- detail: null,
175
- raw: error,
176
- route,
177
- requestInit,
178
- };
179
- return err;
180
- }
181
- if (error instanceof SyntaxError) {
182
- const err = {
183
- code: -3,
184
- message: 'Syntax error',
185
- detail: null,
186
- raw: error,
187
- route,
188
- requestInit,
189
- };
190
- return err;
191
- }
192
- if (error instanceof Error) {
193
- const err = {
194
- code: -2,
195
- message: `${error.name}: ${error.message}`,
196
- detail: null,
197
- raw: error,
198
- route,
199
- requestInit,
200
- };
201
- return err;
202
- }
203
- if (isFetchResponse(error)) {
204
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
205
- let detail = null;
206
- try {
207
- detail = await error.json();
208
- }
209
- catch {
210
- // noop
211
- }
212
- if (!detail) {
213
- try {
214
- detail = await error.text();
215
- }
216
- catch {
217
- // noop
218
- }
219
- }
220
- const err = {
221
- code: error.status,
222
- message: error.statusText,
223
- detail,
224
- raw: error,
225
- route,
226
- requestInit,
227
- };
228
- return err;
229
- }
230
- const err = {
231
- code: 0,
232
- message: 'Unknown error',
233
- detail: null,
234
- raw: error,
235
- route,
236
- requestInit,
237
- };
238
- return err;
239
- };
240
- const buildBody = (body) => {
241
- if (body === undefined || body === null) {
188
+ const serializeBody = (body) => {
189
+ if (body === null || body === undefined) {
242
190
  return null;
243
191
  }
244
- if (typeof body === 'string') {
192
+ if (body instanceof ArrayBuffer ||
193
+ body instanceof Blob ||
194
+ body instanceof FormData ||
195
+ body instanceof URLSearchParams ||
196
+ typeof body === 'string') {
245
197
  return body;
246
198
  }
247
- if (typeof body === 'object') {
199
+ if (typeof body === 'object' || typeof body === 'boolean' || Array.isArray(body)) {
248
200
  return JSON.stringify(body);
249
201
  }
250
- if (body instanceof FormData) {
251
- return body;
252
- }
253
- throw invalidBodyError(body);
202
+ return body.toString();
254
203
  };
255
204
  const transformMethod = (method) => {
256
205
  if (method === 'GQL_QUERY' || method === 'GQL_MUTATE') {
@@ -258,192 +207,236 @@ const transformMethod = (method) => {
258
207
  }
259
208
  return method;
260
209
  };
261
- const guessContentType = (body) => {
262
- if (body === null || body === undefined) {
210
+ const detectContentTypeHeader = (body) => {
211
+ // An empty body has no content type.
212
+ if (body === null) {
263
213
  return null;
264
214
  }
215
+ // FormData bodies rely on the browser's content type assignment.
216
+ if (body instanceof FormData) {
217
+ return null;
218
+ }
219
+ // Blobs usually have their own content type. If it doesn't, then
220
+ // no type can be inferred.
221
+ if (body instanceof Blob) {
222
+ return body.type || null;
223
+ }
224
+ // Array buffers have unknown contents and thus no type can be inferred.
225
+ if (body instanceof ArrayBuffer) {
226
+ return null;
227
+ }
228
+ // Technically, strings could be a form of JSON data, but it's safe enough
229
+ // to assume they're plain strings.
265
230
  if (typeof body === 'string') {
266
231
  return 'text/plain';
267
232
  }
268
- if (body instanceof FormData) {
269
- return 'multipart/form-data';
270
- }
271
- if (typeof body === 'object') {
233
+ // Arrays, objects, boolean and numbers will be encoded as JSON.
234
+ if (typeof body === 'object' || typeof body === 'number' || typeof body === 'boolean') {
272
235
  return 'application/json';
273
236
  }
237
+ // No type could be inferred.
274
238
  return null;
275
239
  };
276
-
277
- const request = async (options) => {
278
- try {
279
- const response = await fetch(options.route, options.init);
280
- if (!response.ok) {
281
- throw response;
282
- }
283
- const isJsonResponse = response.headers.get('Content-Type')?.includes('application/json');
284
- const isTextResponse = response.headers.get('Content-Type')?.includes('text/plain');
285
- const data = (isJsonResponse ? await response.json() : isTextResponse ? await response.text() : null);
286
- const expiresInSeconds = options.cacheAdapter
287
- ? options.cacheAdapter(response.headers)
288
- : extractExpiresInSeconds(response.headers);
289
- const expiresInTimestamp = buildTimestampFromSeconds(expiresInSeconds);
290
- return { data, expiresInTimestamp };
291
- }
292
- catch (error) {
293
- throw await buildRequestError(error, options.route, options.init);
294
- }
240
+ const hasHeader = (headers, header) => {
241
+ return Object.keys(headers).some((key) => key.toLowerCase() === header.toLowerCase());
295
242
  };
296
-
297
- const isBasicAuthProvider = (authProvider) => authProvider instanceof BasicAuthProvider;
298
- const isBearerAuthProvider = (authProvider) => authProvider instanceof BearerAuthProvider;
299
- const isCustomHeaderAuthProvider = (authProvider) => authProvider instanceof CustomHeaderAuthProvider;
300
- const decryptBearer = (token) => {
301
- try {
302
- const base64Url = token.split('.')[1];
303
- const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
304
- const jsonPayload = decodeURIComponent(atob(base64)
305
- .split('')
306
- .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
307
- .join(''));
308
- return JSON.parse(jsonPayload);
309
- }
310
- catch (error) {
311
- console.error(`Invalid bearer token: ${token}`, error);
312
- return null;
313
- }
243
+ const forEachHeader = (headers, callback) => {
244
+ Object.entries(headers).forEach(([key, value]) => {
245
+ callback(key, value);
246
+ });
314
247
  };
315
-
316
- class BearerAuthProvider {
317
- constructor(_config) {
318
- this._config = _config;
319
- this._refreshToken = null;
320
- this._refreshTimerSubscription = null;
321
- this.queryClient = null;
322
- this.onRefreshInitiation$ = new Subject();
323
- this.onRefreshSuccess$ = new Subject();
324
- this.onRefreshFailure$ = new Subject();
325
- this._token = _config.token;
326
- this._refreshToken = _config.refreshConfig?.token ?? null;
327
- if (this._config.refreshConfig) {
328
- this._setupRefresh(this._config.refreshConfig);
248
+ const parseAllXhrResponseHeaders = (xhr) => {
249
+ const headers = xhr.getAllResponseHeaders();
250
+ const parsedHeaders = {};
251
+ for (const line of headers.split('\n')) {
252
+ const index = line.indexOf(':');
253
+ if (index > 0) {
254
+ const name = line.slice(0, index);
255
+ const key = name.toLowerCase();
256
+ const value = line.slice(index + 1).trim();
257
+ parsedHeaders[key] = value;
329
258
  }
330
259
  }
331
- get header() {
332
- return { Authorization: `Bearer ${this._token}` };
260
+ return parsedHeaders;
261
+ };
262
+ const getResponseUrl = (xhr) => {
263
+ if ('responseURL' in xhr && xhr.responseURL) {
264
+ return xhr.responseURL;
333
265
  }
334
- cleanUp() {
335
- this._refreshTimerSubscription?.unsubscribe();
336
- this._refreshTimerSubscription = null;
266
+ if (/^X-Request-URL:/m.test(xhr.getAllResponseHeaders())) {
267
+ return xhr.getResponseHeader('X-Request-URL');
337
268
  }
338
- _setupRefresh(config) {
339
- const bearer = decryptBearer(this._token);
340
- if (!bearer) {
341
- return;
342
- }
343
- const expiresInPropertyName = config.expiresInPropertyName || 'exp';
344
- const expiresIn = bearer[expiresInPropertyName];
345
- const fiveMinutes = 5 * 60 * 1000;
346
- if (expiresIn === undefined) {
347
- throw new Error(`Bearer token does not contain an '${expiresInPropertyName}' property`);
348
- }
349
- const remainingTime = new Date(expiresIn * 1000).getTime() - fiveMinutes - new Date().getTime();
350
- const strategy = config.strategy ?? "beforeExpiration" /* AuthBearerRefreshStrategy.BeforeExpiration */;
351
- switch (strategy) {
352
- case "beforeExpiration" /* AuthBearerRefreshStrategy.BeforeExpiration */:
353
- this._refreshTimerSubscription = timer(remainingTime).subscribe(() => this._refresh(config));
354
- break;
269
+ return null;
270
+ };
271
+
272
+ const XSSI_PREFIX = /^\)\]\}',?\n/;
273
+ const request = (config) => {
274
+ const headers = config.headers || {};
275
+ const responseType = config.responseType || 'json';
276
+ const body = config.body || null;
277
+ const url = config.urlWithParams.split('?')[0];
278
+ return new Observable((observer) => {
279
+ const xhr = new XMLHttpRequest();
280
+ xhr.open(config.method, config.urlWithParams);
281
+ if (config.withCredentials) {
282
+ xhr.withCredentials = true;
355
283
  }
356
- }
357
- async _refresh(config) {
358
- if (!this._refreshToken) {
359
- throw new Error('No refresh token found');
284
+ forEachHeader(headers, (name, value) => xhr.setRequestHeader(name, value));
285
+ if (!hasHeader(headers, 'Accept')) {
286
+ xhr.setRequestHeader('Accept', 'application/json, text/plain, */*');
360
287
  }
361
- if (!this.queryClient) {
362
- throw new Error('Query client instance is null');
288
+ if (!hasHeader(headers, 'Content-Type')) {
289
+ const detectedType = detectContentTypeHeader(body);
290
+ if (detectedType) {
291
+ xhr.setRequestHeader('Content-Type', detectedType);
292
+ }
363
293
  }
364
- const { method, route, bodyAdapter, responseAdapter } = config;
365
- const fullRoute = buildRoute({
366
- base: this.queryClient.config.baseRoute,
367
- route,
368
- });
369
- const body = bodyAdapter ? bodyAdapter(this._refreshToken) : { refreshToken: this._refreshToken };
370
- const requestInit = { body: buildBody(body), method };
371
- this.onRefreshInitiation$.next();
372
- try {
373
- const result = await request({
374
- route: fullRoute,
375
- init: requestInit,
376
- });
377
- const tokens = responseAdapter ? responseAdapter(result.data) : result.data;
378
- if (!tokens.token) {
379
- throw new Error('Token not found in response');
294
+ xhr.responseType = responseType !== 'json' ? responseType : 'text';
295
+ const reqBody = serializeBody(body);
296
+ let headerResponse = null;
297
+ const partialFromXhr = () => {
298
+ if (headerResponse !== null) {
299
+ return headerResponse;
380
300
  }
381
- if (!tokens.refreshToken) {
382
- throw new Error('Refresh token not found in response');
301
+ const statusText = xhr.statusText || 'OK';
302
+ const headers = parseAllXhrResponseHeaders(xhr);
303
+ const _url = getResponseUrl(xhr) || url;
304
+ headerResponse = { headers, status: xhr.status, statusText, url: _url };
305
+ return headerResponse;
306
+ };
307
+ const onLoad = () => {
308
+ const { headers, statusText, url } = partialFromXhr();
309
+ let { status } = partialFromXhr();
310
+ let body = null;
311
+ if (status !== 204 /* HttpStatusCode.NoContent */) {
312
+ body = typeof xhr.response === 'undefined' ? xhr.responseText : xhr.response;
383
313
  }
384
- this._token = tokens.token;
385
- this._refreshToken = tokens.refreshToken;
386
- this.onRefreshSuccess$.next(tokens);
387
- }
388
- catch (error) {
389
- if (error instanceof Error) {
390
- this.onRefreshFailure$.next(await buildRequestError(error, fullRoute, requestInit));
314
+ if (status === 0) {
315
+ status = body ? 200 /* HttpStatusCode.Ok */ : 0;
316
+ }
317
+ let ok = status >= 200 && status < 300;
318
+ if (responseType === 'json' && typeof body === 'string') {
319
+ const originalBody = body;
320
+ body = body.replace(XSSI_PREFIX, '');
321
+ try {
322
+ body = body !== '' ? JSON.parse(body) : null;
323
+ }
324
+ catch (error) {
325
+ body = originalBody;
326
+ if (ok) {
327
+ ok = false;
328
+ body = { error, text: body };
329
+ }
330
+ }
331
+ }
332
+ if (ok) {
333
+ const successEvent = {
334
+ type: 'success',
335
+ headers,
336
+ response: body,
337
+ };
338
+ const expiresInSeconds = config.cacheAdapter?.(headers) ?? extractExpiresInSeconds(headers);
339
+ const expiresInTimestamp = buildTimestampFromSeconds(expiresInSeconds);
340
+ if (expiresInTimestamp) {
341
+ successEvent.expiresInTimestamp = expiresInTimestamp;
342
+ }
343
+ observer.next(successEvent);
344
+ observer.complete();
391
345
  }
392
346
  else {
393
- this.onRefreshFailure$.next(error);
347
+ observer.next({
348
+ type: 'failure',
349
+ headers,
350
+ error: {
351
+ url,
352
+ detail: body,
353
+ status,
354
+ statusText,
355
+ },
356
+ });
357
+ observer.complete();
358
+ }
359
+ };
360
+ const onError = (error) => {
361
+ const { url, headers } = partialFromXhr();
362
+ observer.next({
363
+ type: 'failure',
364
+ headers,
365
+ error: {
366
+ url,
367
+ detail: error,
368
+ status: xhr.status || 0,
369
+ statusText: xhr.statusText || 'Unknown Error',
370
+ },
371
+ });
372
+ observer.complete();
373
+ };
374
+ const onDownProgress = (event) => {
375
+ const { headers } = partialFromXhr();
376
+ const progress = {
377
+ type: 'download-progress',
378
+ headers,
379
+ progress: {
380
+ current: event.loaded,
381
+ },
382
+ };
383
+ if (event.lengthComputable) {
384
+ const progressPercent = (event.loaded / event.total) * 100;
385
+ progress.progress.percentage = Math.round(progressPercent * 100) / 100;
386
+ progress.progress.total = event.total;
387
+ }
388
+ if (responseType === 'text' && !!xhr.responseText) {
389
+ progress.partialText = xhr.responseText;
390
+ }
391
+ observer.next(progress);
392
+ };
393
+ const onUpProgress = (event) => {
394
+ const { headers } = partialFromXhr();
395
+ const progress = {
396
+ type: 'upload-progress',
397
+ headers,
398
+ progress: {
399
+ current: event.loaded,
400
+ },
401
+ };
402
+ if (event.lengthComputable) {
403
+ const progressPercent = (event.loaded / event.total) * 100;
404
+ progress.progress.percentage = Math.round(progressPercent * 100) / 100;
405
+ progress.progress.total = event.total;
406
+ }
407
+ observer.next(progress);
408
+ };
409
+ xhr.addEventListener('load', onLoad);
410
+ xhr.addEventListener('error', onError);
411
+ xhr.addEventListener('timeout', onError);
412
+ xhr.addEventListener('abort', onError);
413
+ if (config.reportProgress) {
414
+ xhr.addEventListener('progress', onDownProgress);
415
+ if (reqBody !== null && xhr.upload) {
416
+ xhr.upload.addEventListener('progress', onUpProgress);
394
417
  }
395
418
  }
396
- }
397
- }
398
-
399
- const getOpName = /(query|mutation) ?([\w\d-_]+)? ?\(.*?\)? \{/;
400
- const transformGql = (str) => {
401
- str = Array.isArray(str) ? str.join('') : str;
402
- const name = getOpName.exec(str);
403
- return function (variables) {
404
- const data = { query: str };
405
- if (variables)
406
- data['variables'] = JSON.stringify(variables);
407
- if (name && name.length) {
408
- const operationName = name[2];
409
- if (operationName)
410
- data['operationName'] = name[2];
411
- }
412
- return JSON.stringify(data);
413
- };
414
- };
415
- const gql = (strings, ...values) => {
416
- const str = strings.reduce((acc, cur, i) => {
417
- return acc + cur + (values[i] || '');
418
- }, '');
419
- return str;
420
- };
421
-
422
- const deepFreeze = (object) => {
423
- // check if object is actually an object or frozen
424
- if (!object || typeof object !== 'object' || Object.isFrozen(object)) {
425
- return object;
426
- }
427
- // Retrieve the property names defined on object
428
- const propNames = Object.getOwnPropertyNames(object);
429
- // Freeze properties before freezing self
430
- for (const name of propNames) {
431
- const value = object[name];
432
- if (value && typeof value === 'object') {
433
- deepFreeze(value);
434
- }
435
- }
436
- return Object.freeze(object);
419
+ xhr.send(reqBody);
420
+ observer.next({ type: 'start', headers });
421
+ return () => {
422
+ xhr.removeEventListener('error', onError);
423
+ xhr.removeEventListener('abort', onError);
424
+ xhr.removeEventListener('load', onLoad);
425
+ xhr.removeEventListener('timeout', onError);
426
+ if (config.reportProgress) {
427
+ xhr.removeEventListener('progress', onDownProgress);
428
+ if (reqBody !== null && xhr.upload) {
429
+ xhr.upload.removeEventListener('progress', onUpProgress);
430
+ }
431
+ }
432
+ if (xhr.readyState !== xhr.DONE) {
433
+ xhr.abort();
434
+ }
435
+ observer.next({ type: 'cancel', headers });
436
+ };
437
+ });
437
438
  };
438
439
 
439
- /**
440
- * Dummy function for providing types while not breaking inference.
441
- * **This function always returns null.**
442
- * @see https://github.com/microsoft/TypeScript/issues/26242
443
- * @returns null
444
- */
445
- const def = () => null;
446
-
447
440
  function filterSuccess() {
448
441
  return function (source) {
449
442
  return source.pipe(filter((value) => isQueryStateSuccess(value)));
@@ -454,9 +447,34 @@ function filterFailure() {
454
447
  return source.pipe(filter((value) => isQueryStateFailure(value)));
455
448
  };
456
449
  }
457
- function takeUntilResponse() {
450
+ function filterQueryStates(allowedStates) {
458
451
  return function (source) {
459
- return source.pipe(takeWhile((value) => isQueryStateLoading(value), true));
452
+ return source.pipe(filter((value) => value && allowedStates.includes(value.type)));
453
+ };
454
+ }
455
+ function takeUntilResponse(config) {
456
+ return function (source) {
457
+ return source.pipe(takeWhile((value) => {
458
+ if (config?.excludeNull && value === null) {
459
+ return false;
460
+ }
461
+ return isQueryStateLoading(value) || value === null;
462
+ }, true));
463
+ };
464
+ }
465
+ function filterNull() {
466
+ return function (source) {
467
+ return source.pipe(filter((value) => value !== null));
468
+ };
469
+ }
470
+ function switchQueryState() {
471
+ return function (source) {
472
+ return source.pipe(switchMap((value) => value?.state$ ?? of(null)));
473
+ };
474
+ }
475
+ function switchQueryCollectionState() {
476
+ return function (source) {
477
+ return source.pipe(switchMap((value) => value?.query.state$ ?? of(null)));
460
478
  };
461
479
  }
462
480
  const isQueryStateLoading = (state) => state?.type === "LOADING" /* QueryStateType.Loading */;
@@ -484,23 +502,32 @@ const isGqlQueryConfig = (config) => {
484
502
  }
485
503
  return true;
486
504
  };
505
+ const isQuery = (query) => {
506
+ if (!query || typeof query !== 'object' || Array.isArray(query)) {
507
+ return false;
508
+ }
509
+ if (!('state$' in query)) {
510
+ return false;
511
+ }
512
+ return true;
513
+ };
514
+ const createQueryCollection = (
515
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
516
+ queryMap) => new BehaviorSubject(null);
517
+ const extractQuery = (v) => (isQuery(v) ? v : v?.query) ?? null;
518
+ const getDefaultHeaders = (headers, method) => {
519
+ if (!headers) {
520
+ return {};
521
+ }
522
+ if (method in headers) {
523
+ return (headers[method] ?? {});
524
+ }
525
+ return headers;
526
+ };
487
527
 
488
528
  class Query {
489
- constructor(_client, _queryConfig, _route, _args) {
490
- this._client = _client;
491
- this._queryConfig = _queryConfig;
492
- this._route = _route;
493
- this._args = _args;
494
- this._currentId = 0;
495
- this._abortController = new AbortController();
496
- this._pollingSubscription = null;
497
- this._state$ = new BehaviorSubject({
498
- type: "PREPARED" /* QueryStateType.Prepared */,
499
- meta: { id: this._currentId },
500
- });
501
- }
502
529
  get _nextId() {
503
- return this._currentId++;
530
+ return ++this._currentId;
504
531
  }
505
532
  get state$() {
506
533
  return this._state$.asObservable();
@@ -518,6 +545,19 @@ class Query {
518
545
  }
519
546
  return ts < Date.now();
520
547
  }
548
+ constructor(_client, _queryConfig, _routeWithParams, _args) {
549
+ this._client = _client;
550
+ this._queryConfig = _queryConfig;
551
+ this._routeWithParams = _routeWithParams;
552
+ this._args = _args;
553
+ this._currentId = 0;
554
+ this._pollingSubscription = null;
555
+ this._onAbort$ = new Subject();
556
+ this._state$ = new BehaviorSubject({
557
+ type: "PREPARED" /* QueryStateType.Prepared */,
558
+ meta: { id: this._currentId },
559
+ });
560
+ }
521
561
  clone() {
522
562
  return this._client.fetch(this._queryConfig);
523
563
  }
@@ -532,22 +572,20 @@ class Query {
532
572
  if (isQueryStateLoading(this._state$.value)) {
533
573
  this.abort();
534
574
  }
535
- const meta = deepFreeze({ id });
575
+ const meta = { id };
536
576
  this._state$.next({
537
577
  type: "LOADING" /* QueryStateType.Loading */,
538
578
  meta,
539
579
  });
540
- let body = null;
541
- let contentType = null;
580
+ this._updateUseResultInDependencies();
581
+ let body;
542
582
  if (isGqlQueryConfig(this._queryConfig)) {
543
583
  const queryTemplate = this._queryConfig.query;
544
584
  const query = transformGql(queryTemplate);
545
- contentType = 'application/json';
546
585
  body = query(this._args?.variables);
547
586
  }
548
587
  else {
549
- body = buildBody(this._args?.body);
550
- contentType = guessContentType(this._args?.body);
588
+ body = this._args?.body;
551
589
  }
552
590
  let authHeader = null;
553
591
  if (this._queryConfig.secure) {
@@ -561,57 +599,83 @@ class Query {
561
599
  authHeader = this._client.authProvider.header;
562
600
  }
563
601
  }
564
- let headers = mergeHeaders(authHeader, this._args?.headers);
565
- if (contentType && !headers?.['Content-Type'] && !headers?.['content-type']) {
566
- if (headers) {
567
- headers['Content-Type'] = contentType;
568
- }
569
- else {
570
- headers = {
571
- 'Content-Type': contentType,
572
- };
573
- }
574
- }
575
602
  request({
576
- route: this._route,
577
- init: {
578
- method: transformMethod(this._queryConfig.method),
579
- signal: this._abortController.signal,
580
- body,
581
- headers: headers || undefined,
582
- },
603
+ urlWithParams: this._routeWithParams,
604
+ method: transformMethod(this._queryConfig.method),
605
+ body,
606
+ headers: mergeHeaders(getDefaultHeaders(this._client.config.request?.headers, this._queryConfig.method), authHeader, this._args?.headers) || undefined,
583
607
  cacheAdapter: this._client.config.request?.cacheAdapter,
608
+ reportProgress: this._queryConfig.reportProgress,
609
+ responseType: this._queryConfig.responseType,
610
+ withCredentials: this._queryConfig.withCredentials,
584
611
  })
585
- .then((response) => {
586
- const isResponseObject = typeof response === 'object';
587
- const isGql = isGqlQueryConfig(this._queryConfig);
588
- let responseData = null;
589
- if (isGql &&
590
- isResponseObject &&
591
- !!response.data &&
592
- typeof response.data === 'object' &&
593
- 'data' in response.data) {
594
- responseData = response.data['data'];
595
- }
596
- else {
597
- responseData = response.data;
598
- }
599
- const transformedResponse = this._queryConfig.responseTransformer
600
- ? this._queryConfig.responseTransformer(responseData)
601
- : responseData;
602
- this._state$.next({
603
- type: "SUCCESS" /* QueryStateType.Success */,
604
- response: deepFreeze(transformedResponse),
605
- rawResponse: deepFreeze(responseData),
606
- meta: deepFreeze({ ...meta, expiresAt: response.expiresInTimestamp }),
607
- });
608
- })
609
- .catch((error) => this._handleExecuteError(error, meta));
612
+ .pipe(takeUntil(this._onAbort$))
613
+ .subscribe({
614
+ next: (state) => {
615
+ if (state.type === 'start') {
616
+ this._state$.next({
617
+ type: "LOADING" /* QueryStateType.Loading */,
618
+ meta,
619
+ });
620
+ }
621
+ else if (state.type === 'download-progress') {
622
+ this._state$.next({
623
+ type: "LOADING" /* QueryStateType.Loading */,
624
+ progress: state.progress,
625
+ partialText: state.partialText,
626
+ meta,
627
+ });
628
+ }
629
+ else if (state.type === 'upload-progress') {
630
+ this._state$.next({
631
+ type: "LOADING" /* QueryStateType.Loading */,
632
+ progress: state.progress,
633
+ meta,
634
+ });
635
+ }
636
+ else if (state.type === 'success') {
637
+ const isResponseObject = typeof state.response === 'object';
638
+ const isGql = isGqlQueryConfig(this._queryConfig);
639
+ let responseData = null;
640
+ if (isGql &&
641
+ isResponseObject &&
642
+ !!state.response &&
643
+ typeof state.response === 'object' &&
644
+ 'data' in state.response) {
645
+ responseData = state.response['data'];
646
+ }
647
+ else {
648
+ responseData = state.response;
649
+ }
650
+ const transformedResponse = this._queryConfig.responseTransformer
651
+ ? this._queryConfig.responseTransformer(responseData)
652
+ : responseData;
653
+ this._state$.next({
654
+ type: "SUCCESS" /* QueryStateType.Success */,
655
+ rawResponse: state.response,
656
+ response: transformedResponse,
657
+ meta: { ...meta, expiresAt: state.expiresInTimestamp },
658
+ });
659
+ }
660
+ else if (state.type === 'failure') {
661
+ this._state$.next({
662
+ type: "FAILURE" /* QueryStateType.Failure */,
663
+ error: state.error,
664
+ meta,
665
+ });
666
+ }
667
+ else if (state.type === 'cancel') {
668
+ this._state$.next({
669
+ type: "CANCELLED" /* QueryStateType.Cancelled */,
670
+ meta,
671
+ });
672
+ }
673
+ },
674
+ });
610
675
  return this;
611
676
  }
612
677
  abort() {
613
- this._abortController.abort();
614
- this._abortController = new AbortController();
678
+ this._onAbort$.next();
615
679
  return this;
616
680
  }
617
681
  poll(config) {
@@ -628,27 +692,200 @@ class Query {
628
692
  this._pollingSubscription = null;
629
693
  return this;
630
694
  }
631
- _handleExecuteError(error, meta) {
632
- if (isAbortRequestError(error)) {
633
- this._state$.next({
634
- type: "CANCELLED" /* QueryStateType.Cancelled */,
635
- meta,
636
- });
695
+ _updateUseResultInDependencies() {
696
+ if (!this._args?.useResultIn?.length) {
697
+ return;
637
698
  }
638
- else if (isRequestError(error)) {
639
- this._state$.next({
640
- type: "FAILURE" /* QueryStateType.Failure */,
641
- error,
642
- meta,
643
- });
699
+ this.state$
700
+ .pipe(filterSuccess(), takeUntil(this._onAbort$), takeUntilResponse(), tap((state) => {
701
+ if (!this._args?.useResultIn) {
702
+ return;
703
+ }
704
+ for (const query of this._args.useResultIn) {
705
+ query._state$.next({
706
+ type: "SUCCESS" /* QueryStateType.Success */,
707
+ meta: { ...state.meta, id: query._nextId },
708
+ rawResponse: state.rawResponse,
709
+ response: state.response,
710
+ });
711
+ }
712
+ }))
713
+ .subscribe();
714
+ }
715
+ }
716
+
717
+ const isBasicAuthProvider = (authProvider) => authProvider instanceof BasicAuthProvider;
718
+ const isBearerAuthProvider = (authProvider) => authProvider instanceof BearerAuthProvider;
719
+ const isCustomHeaderAuthProvider = (authProvider) => authProvider instanceof CustomHeaderAuthProvider;
720
+ const decryptBearer = (token) => {
721
+ try {
722
+ const base64Url = token.split('.')[1];
723
+ const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
724
+ const jsonPayload = decodeURIComponent(atob(base64)
725
+ .split('')
726
+ .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
727
+ .join(''));
728
+ return JSON.parse(jsonPayload);
729
+ }
730
+ catch (error) {
731
+ console.error(`Invalid bearer token: ${token}`, error);
732
+ return null;
733
+ }
734
+ };
735
+
736
+ class BearerAuthProvider {
737
+ get header() {
738
+ return { Authorization: `Bearer ${this._tokens$.getValue().token}` };
739
+ }
740
+ get tokens$() {
741
+ return this._tokens$.asObservable();
742
+ }
743
+ get tokens() {
744
+ return this._tokens$.getValue();
745
+ }
746
+ get currentRefreshQuery$() {
747
+ return this._currentRefreshQuery$.asObservable();
748
+ }
749
+ get currentRefreshQuery() {
750
+ return this._currentRefreshQuery$.getValue();
751
+ }
752
+ constructor(_config) {
753
+ this._config = _config;
754
+ this._destroy$ = new Subject();
755
+ this._currentRefreshQuery$ = new BehaviorSubject(null);
756
+ this._tokens$ = new BehaviorSubject({
757
+ token: null,
758
+ refreshToken: null,
759
+ });
760
+ const cookieEnabled = _config.refreshConfig?.cookieEnabled ?? true;
761
+ const cookieToken = _config.refreshConfig?.cookieName && cookieEnabled ? getCookie(_config.refreshConfig.cookieName) ?? null : null;
762
+ this._tokens$.next({
763
+ token: _config.token || null,
764
+ refreshToken: _config.refreshConfig?.token || cookieToken || null,
765
+ });
766
+ if (!this.tokens.token && !this.tokens.refreshToken) {
767
+ if (!_config.refreshConfig?.cookieName) {
768
+ console.error('A BearerAuthProvider was created without token or refresh token. You should provide at least a cookieName where the refresh token might be stored.');
769
+ }
770
+ return;
644
771
  }
645
- else {
646
- throw error;
772
+ if (this.tokens.token) {
773
+ this._prepareForRefresh();
774
+ if (this.tokens.refreshToken && this._config.refreshConfig?.cookieName) {
775
+ setCookie(this._config.refreshConfig.cookieName, this.tokens.refreshToken);
776
+ }
777
+ }
778
+ else if (this.tokens.refreshToken) {
779
+ this._refreshQuery();
780
+ }
781
+ }
782
+ cleanUp() {
783
+ this._destroy$.next(true);
784
+ this._destroy$.unsubscribe();
785
+ this._currentRefreshQuery$.next(null);
786
+ if (this._config.refreshConfig?.cookieName) {
787
+ deleteCookie(this._config.refreshConfig.cookieName, '/');
788
+ }
789
+ }
790
+ enableCookie() {
791
+ if (!this._config.refreshConfig?.cookieName) {
792
+ throw new Error('No cookie name was provided');
793
+ }
794
+ if (!this.tokens.refreshToken) {
795
+ throw new Error('No refresh token was provided');
796
+ }
797
+ this._config.refreshConfig.cookieEnabled = true;
798
+ setCookie(this._config.refreshConfig.cookieName, this.tokens.refreshToken);
799
+ }
800
+ disableCookie() {
801
+ if (!this._config.refreshConfig?.cookieName) {
802
+ throw new Error('No cookie name was provided');
803
+ }
804
+ this._config.refreshConfig.cookieEnabled = false;
805
+ deleteCookie(this._config.refreshConfig.cookieName, '/');
806
+ }
807
+ _prepareForRefresh() {
808
+ if (!this.tokens.token || !this._config.refreshConfig) {
809
+ return;
810
+ }
811
+ const bearer = decryptBearer(this.tokens.token);
812
+ if (!bearer) {
813
+ return;
814
+ }
815
+ const expiresInPropertyName = this._config.refreshConfig.expiresInPropertyName || 'exp';
816
+ const expiresIn = bearer[expiresInPropertyName];
817
+ const fiveMinutes = 5 * 60 * 1000;
818
+ const refreshBuffer = this._config.refreshConfig.refreshBuffer ?? fiveMinutes;
819
+ if (expiresIn === undefined) {
820
+ throw new Error(`Bearer token does not contain an '${expiresInPropertyName}' property`);
821
+ }
822
+ const remainingTime = new Date(expiresIn * 1000).getTime() - refreshBuffer - new Date().getTime();
823
+ const strategy = this._config.refreshConfig.strategy ?? "beforeExpiration" /* AuthBearerRefreshStrategy.BeforeExpiration */;
824
+ switch (strategy) {
825
+ case "beforeExpiration" /* AuthBearerRefreshStrategy.BeforeExpiration */:
826
+ timer(remainingTime)
827
+ .pipe(takeUntil(this._destroy$))
828
+ .subscribe(() => this._refreshQuery());
829
+ break;
830
+ }
831
+ }
832
+ _refreshQuery() {
833
+ const currentRefreshToken = this.tokens.refreshToken;
834
+ if (!currentRefreshToken || !this._config.refreshConfig) {
835
+ return;
647
836
  }
837
+ const args = this._config.refreshConfig.requestArgsAdapter?.({
838
+ token: this.tokens.token,
839
+ refreshToken: currentRefreshToken,
840
+ }) ?? {
841
+ body: { refreshToken: currentRefreshToken },
842
+ };
843
+ const query = this._config.refreshConfig.queryCreator.prepare(args).execute({ skipCache: true });
844
+ this._currentRefreshQuery$.next(query);
845
+ this._currentRefreshQuery$
846
+ .pipe(switchQueryState(), tap((state) => {
847
+ if (isQueryStateSuccess(state)) {
848
+ if (this._config.refreshConfig?.responseAdapter) {
849
+ const tokens = this._config.refreshConfig.responseAdapter(state.response);
850
+ this._tokens$.next(tokens);
851
+ }
852
+ else {
853
+ this._tokens$.next({
854
+ token: state.response['token'],
855
+ refreshToken: state.response['refreshToken'],
856
+ });
857
+ }
858
+ const cookieEnabled = this._config.refreshConfig?.cookieEnabled ?? true;
859
+ if (this._config.refreshConfig?.cookieName && this.tokens.refreshToken && cookieEnabled) {
860
+ setCookie(this._config.refreshConfig.cookieName, this.tokens.refreshToken);
861
+ }
862
+ this._prepareForRefresh();
863
+ }
864
+ else if (isQueryStateFailure(state)) {
865
+ this._tokens$.next({ token: null, refreshToken: null });
866
+ if (this._config.refreshConfig?.cookieName) {
867
+ deleteCookie(this._config.refreshConfig.cookieName, '/');
868
+ }
869
+ }
870
+ }), takeUntilResponse(), takeUntil(this._destroy$))
871
+ .subscribe();
648
872
  }
649
873
  }
650
874
 
651
875
  class QueryDirective {
876
+ get query() {
877
+ return this._query;
878
+ }
879
+ set query(v) {
880
+ this._query = v;
881
+ this._subscribeToQuery();
882
+ }
883
+ get cache() {
884
+ return this._cache;
885
+ }
886
+ set cache(v) {
887
+ this._cache = v;
888
+ }
652
889
  constructor(_mainTemplateRef, _viewContainerRef, _errorHandler, _cdr) {
653
890
  this._mainTemplateRef = _mainTemplateRef;
654
891
  this._viewContainerRef = _viewContainerRef;
@@ -662,22 +899,10 @@ class QueryDirective {
662
899
  raw: null,
663
900
  loading: false,
664
901
  error: null,
902
+ progress: null,
665
903
  };
666
904
  this._cache = false;
667
905
  }
668
- get query() {
669
- return this._query;
670
- }
671
- set query(v) {
672
- this._query = v;
673
- this._subscribeToQuery();
674
- }
675
- get cache() {
676
- return this._cache;
677
- }
678
- set cache(v) {
679
- this._cache = v;
680
- }
681
906
  static ngTemplateContextGuard(dir, ctx) {
682
907
  return true;
683
908
  }
@@ -693,15 +918,18 @@ class QueryDirective {
693
918
  if (!this.query) {
694
919
  return;
695
920
  }
696
- const sub = this.query.state$.pipe(tap((state) => this._updateView(state))).subscribe();
921
+ const query = isQuery(this.query) ? this.query : this.query.query;
922
+ const sub = query.state$.pipe(tap((state) => this._updateView(state))).subscribe();
697
923
  this._subscription = sub;
698
924
  }
699
925
  _updateView(state) {
700
926
  if (isQueryStateLoading(state)) {
701
927
  this._viewContext.loading = true;
928
+ this._viewContext.progress = state.progress ?? null;
702
929
  }
703
930
  else {
704
931
  this._viewContext.loading = false;
932
+ this._viewContext.progress = null;
705
933
  }
706
934
  if (isQueryStateSuccess(state)) {
707
935
  this._viewContext.query = state.response;
@@ -729,9 +957,9 @@ class QueryDirective {
729
957
  }
730
958
  }
731
959
  }
732
- QueryDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: QueryDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }, { token: i0.ErrorHandler }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
733
- QueryDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: QueryDirective, isStandalone: true, selector: "[query]", inputs: { query: "query", cache: ["queryCache", "cache"] }, ngImport: i0 });
734
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: QueryDirective, decorators: [{
960
+ QueryDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: QueryDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }, { token: i0.ErrorHandler }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
961
+ QueryDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.1.4", type: QueryDirective, isStandalone: true, selector: "[query]", inputs: { query: "query", cache: ["queryCache", "cache"] }, ngImport: i0 });
962
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: QueryDirective, decorators: [{
735
963
  type: Directive,
736
964
  args: [{
737
965
  // eslint-disable-next-line @angular-eslint/directive-selector
@@ -747,16 +975,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImpor
747
975
 
748
976
  /* eslint-disable @typescript-eslint/no-explicit-any */
749
977
  class InfinityQuery {
750
- constructor(_config) {
751
- this._config = _config;
752
- this._currentPage$ = new BehaviorSubject(null);
753
- this._currentCalculatedPage$ = new BehaviorSubject(null);
754
- this._totalPages$ = new BehaviorSubject(null);
755
- this._itemsPerPage$ = new BehaviorSubject(this._config.limitParam?.value ?? 10);
756
- this._currentQuery$ = new BehaviorSubject(null);
757
- this._data$ = new BehaviorSubject([]);
758
- this._subscriptions = [];
759
- }
760
978
  get currentPage$() {
761
979
  return this._currentPage$.asObservable();
762
980
  }
@@ -793,6 +1011,16 @@ class InfinityQuery {
793
1011
  get data() {
794
1012
  return this._data$.getValue();
795
1013
  }
1014
+ constructor(_config) {
1015
+ this._config = _config;
1016
+ this._currentPage$ = new BehaviorSubject(null);
1017
+ this._currentCalculatedPage$ = new BehaviorSubject(null);
1018
+ this._totalPages$ = new BehaviorSubject(null);
1019
+ this._itemsPerPage$ = new BehaviorSubject(this._config.limitParam?.value ?? 10);
1020
+ this._currentQuery$ = new BehaviorSubject(null);
1021
+ this._data$ = new BehaviorSubject([]);
1022
+ this._subscriptions = [];
1023
+ }
796
1024
  nextPage() {
797
1025
  const newPage = (this._currentPage$.value ?? 0) + 1;
798
1026
  const calculatedPage = this._config?.pageParam?.valueCalculator?.({
@@ -800,6 +1028,10 @@ class InfinityQuery {
800
1028
  totalPages: this.totalPages,
801
1029
  itemsPerPage: this.itemsPerPage,
802
1030
  }) ?? newPage;
1031
+ if (this.totalPages !== null && calculatedPage > this.totalPages) {
1032
+ console.error('Cannot load more pages, already at the end. Make sure to not render the infinity query trigger using *ngIf canLoadMore');
1033
+ return;
1034
+ }
803
1035
  const args = this._prepareArgs(this._config, calculatedPage);
804
1036
  const query = this._config.queryCreator.prepare(args).execute();
805
1037
  this._handleNewQuery(query);
@@ -925,7 +1157,7 @@ class InfinityQueryDirective {
925
1157
  itemsPerPage: null,
926
1158
  };
927
1159
  this._infinityQueryInstance = null;
928
- this._destroy$ = inject(DestroyDirective).destroy$;
1160
+ this._destroy$ = inject(DestroyService, { host: true }).destroy$;
929
1161
  this._cdr = inject(ChangeDetectorRef);
930
1162
  this._viewContainerRef = inject(ViewContainerRef);
931
1163
  this._mainTemplateRef = inject((TemplateRef));
@@ -1040,17 +1272,16 @@ class InfinityQueryDirective {
1040
1272
  this._data$.next([]);
1041
1273
  }
1042
1274
  }
1043
- InfinityQueryDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: InfinityQueryDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1044
- InfinityQueryDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: InfinityQueryDirective, isStandalone: true, selector: "[infinityQuery]", inputs: { infinityQuery: "infinityQuery" }, providers: [{ provide: INFINITY_QUERY_TOKEN, useExisting: InfinityQueryDirective }], exportAs: ["infinityQuery"], hostDirectives: [{ directive: i1.DestroyDirective }], ngImport: i0 });
1045
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: InfinityQueryDirective, decorators: [{
1275
+ InfinityQueryDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: InfinityQueryDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1276
+ InfinityQueryDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.1.4", type: InfinityQueryDirective, isStandalone: true, selector: "[infinityQuery]", inputs: { infinityQuery: "infinityQuery" }, providers: [{ provide: INFINITY_QUERY_TOKEN, useExisting: InfinityQueryDirective }, DestroyService], exportAs: ["infinityQuery"], ngImport: i0 });
1277
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: InfinityQueryDirective, decorators: [{
1046
1278
  type: Directive,
1047
1279
  args: [{
1048
1280
  // eslint-disable-next-line @angular-eslint/directive-selector
1049
1281
  selector: '[infinityQuery]',
1050
1282
  exportAs: 'infinityQuery',
1051
1283
  standalone: true,
1052
- providers: [{ provide: INFINITY_QUERY_TOKEN, useExisting: InfinityQueryDirective }],
1053
- hostDirectives: [DestroyDirective],
1284
+ providers: [{ provide: INFINITY_QUERY_TOKEN, useExisting: InfinityQueryDirective }, DestroyService],
1054
1285
  }]
1055
1286
  }], propDecorators: { infinityQuery: [{
1056
1287
  type: Input
@@ -1093,9 +1324,9 @@ class InfinityQueryTriggerDirective {
1093
1324
  this._observer.observe(this._elementRef.nativeElement);
1094
1325
  }
1095
1326
  }
1096
- InfinityQueryTriggerDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: InfinityQueryTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1097
- InfinityQueryTriggerDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: InfinityQueryTriggerDirective, isStandalone: true, selector: "[infinityQueryTrigger], infinity-query-trigger", inputs: { scrollContainerSelector: "scrollContainerSelector" }, ngImport: i0 });
1098
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: InfinityQueryTriggerDirective, decorators: [{
1327
+ InfinityQueryTriggerDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: InfinityQueryTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1328
+ InfinityQueryTriggerDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.1.4", type: InfinityQueryTriggerDirective, isStandalone: true, selector: "[infinityQueryTrigger], infinity-query-trigger", inputs: { scrollContainerSelector: "scrollContainerSelector" }, ngImport: i0 });
1329
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: InfinityQueryTriggerDirective, decorators: [{
1099
1330
  type: Directive,
1100
1331
  args: [{
1101
1332
  // eslint-disable-next-line @angular-eslint/directive-selector
@@ -1187,9 +1418,18 @@ config, args) => {
1187
1418
  };
1188
1419
 
1189
1420
  class QueryClient {
1421
+ get config() {
1422
+ return this._clientConfig;
1423
+ }
1424
+ get authProvider() {
1425
+ return this._authProvider$.getValue() ?? this._clientConfig.parent?._authProvider$.getValue() ?? null;
1426
+ }
1427
+ get authProvider$() {
1428
+ return this._authProvider$.asObservable() ?? this._clientConfig.parent?._authProvider$.asObservable() ?? null;
1429
+ }
1190
1430
  constructor(_clientConfig) {
1191
1431
  this._clientConfig = _clientConfig;
1192
- this._authProvider = null;
1432
+ this._authProvider$ = new BehaviorSubject(null);
1193
1433
  this.get = (queryConfig) => this.fetch({
1194
1434
  ...queryConfig,
1195
1435
  method: 'GET',
@@ -1246,38 +1486,41 @@ class QueryClient {
1246
1486
  };
1247
1487
  };
1248
1488
  this.setAuthProvider = (authProvider) => {
1249
- if (this._authProvider) {
1489
+ if (this.authProvider) {
1250
1490
  throw new Error('The auth provider is already set. Please call clearAuthProvider() first.');
1251
1491
  }
1252
- this._authProvider = authProvider;
1253
- authProvider.queryClient = this;
1492
+ this._authProvider$.next(authProvider);
1493
+ };
1494
+ this.setDefaultHeaders = (headers) => {
1495
+ if (!this._clientConfig.request) {
1496
+ this._clientConfig.request = {
1497
+ headers: headers ?? undefined,
1498
+ };
1499
+ }
1500
+ else {
1501
+ this._clientConfig.request.headers = headers ?? undefined;
1502
+ }
1254
1503
  };
1255
1504
  this.clearAuthProvider = () => {
1256
- this._authProvider?.cleanUp();
1257
- this._authProvider = null;
1505
+ this.authProvider?.cleanUp();
1506
+ this._authProvider$.next(null);
1258
1507
  };
1259
1508
  this._store = new QueryStore({
1260
1509
  enableChangeLogging: _clientConfig.logging?.queryStateChanges,
1261
1510
  enableGarbageCollectorLogging: _clientConfig.logging?.queryStateGarbageCollector,
1262
1511
  });
1263
1512
  }
1264
- get config() {
1265
- return this._clientConfig;
1266
- }
1267
- get authProvider() {
1268
- return this._authProvider;
1269
- }
1270
1513
  }
1271
1514
 
1272
1515
  class QueryField {
1516
+ get control() {
1517
+ return this.data.control;
1518
+ }
1273
1519
  constructor(data) {
1274
1520
  this.data = data;
1275
1521
  this.changes$ = this._setupChangesObservable();
1276
1522
  this._currentDebounceTimeout = null;
1277
1523
  }
1278
- get control() {
1279
- return this.data.control;
1280
- }
1281
1524
  setValue(value) {
1282
1525
  if (this._currentDebounceTimeout !== null) {
1283
1526
  clearTimeout(this._currentDebounceTimeout);
@@ -1311,7 +1554,7 @@ class QueryForm {
1311
1554
  this.form = this._setupFormGroup();
1312
1555
  }
1313
1556
  observe(options) {
1314
- return combineLatest(Object.values(this._fields).map((field) => field.changes$)).pipe(map(() => this.form.getRawValue()), distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)), tap((values) => {
1557
+ return combineLatest(Object.values(this._fields).map((field) => field.changes$)).pipe(map(() => this.form.getRawValue()), distinctUntilChanged((a, b) => equal(a, b)), tap((values) => {
1315
1558
  if (options?.syncViaUrlQueryParams !== false) {
1316
1559
  this._syncViaUrlQueryParams(values);
1317
1560
  }
@@ -1372,7 +1615,7 @@ class QueryForm {
1372
1615
  if (!this._router || !this._activatedRoute) {
1373
1616
  return;
1374
1617
  }
1375
- const queryParams = structuredClone(this._activatedRoute.snapshot.queryParams);
1618
+ const queryParams = { ...clone(this._activatedRoute.snapshot.queryParams) };
1376
1619
  for (const [key, value] of Object.entries(values)) {
1377
1620
  if (this._isDefaultValue(key, value) || this._fields[key].data.appendToUrl === false) {
1378
1621
  delete queryParams[key];
@@ -1483,9 +1726,34 @@ const transformToSortQueryParam = (value) => {
1483
1726
  return null;
1484
1727
  };
1485
1728
 
1729
+ const deepFreeze = (object) => {
1730
+ // check if object is actually an object or frozen
1731
+ if (!object || typeof object !== 'object' || Object.isFrozen(object)) {
1732
+ return object;
1733
+ }
1734
+ // Retrieve the property names defined on object
1735
+ const propNames = Object.getOwnPropertyNames(object);
1736
+ // Freeze properties before freezing self
1737
+ for (const name of propNames) {
1738
+ const value = object[name];
1739
+ if (value && typeof value === 'object') {
1740
+ deepFreeze(value);
1741
+ }
1742
+ }
1743
+ return Object.freeze(object);
1744
+ };
1745
+
1746
+ /**
1747
+ * Dummy function for providing types while not breaking inference.
1748
+ * **This function always returns null.**
1749
+ * @see https://github.com/microsoft/TypeScript/issues/26242
1750
+ * @returns null
1751
+ */
1752
+ const def = () => null;
1753
+
1486
1754
  /**
1487
1755
  * Generated bundle index. Do not edit.
1488
1756
  */
1489
1757
 
1490
- export { BasicAuthProvider, BearerAuthProvider, CustomHeaderAuthProvider, INFINITY_QUERY_TOKEN, InfinityQuery, InfinityQueryDirective, InfinityQueryTriggerDirective, Query, QueryClient, QueryDirective, QueryError, QueryField, QueryForm, QueryStore, buildBody, buildErrorMessage, buildGqlCacheKey, buildQueryArrayString, buildQueryString, buildRequestError, buildRoute, buildTimestampFromSeconds, createInfinityQueryConfig, decryptBearer, deepFreeze, def, extractExpiresInSeconds, filterFailure, filterInvalidParams, filterSuccess, gql, guessContentType, invalidBaseRouteError, invalidBodyError, invalidRouteError, isAbortRequestError, isBasicAuthProvider, isBearerAuthProvider, isCustomHeaderAuthProvider, isFetchResponse, isGqlQueryConfig, isParamValid, isQueryStateCancelled, isQueryStateFailure, isQueryStateLoading, isQueryStatePrepared, isQueryStateSuccess, isRequestError, mergeHeaders, pathParamsMissingInRouteFunctionError, queryStateAlreadyHasKeyError, queryStateCannotTransformError, queryStateDoesNotContainKeyError, request, shouldCacheQuery, skipPaginationPageParamCalculator, takeUntilResponse, transformGql, transformMethod, transformToBoolean, transformToBooleanArray, transformToDate, transformToDateArray, transformToNumber, transformToNumberArray, transformToSort, transformToSortQueryParam, transformToString, transformToStringArray };
1758
+ export { BasicAuthProvider, BearerAuthProvider, CustomHeaderAuthProvider, INFINITY_QUERY_TOKEN, InfinityQuery, InfinityQueryDirective, InfinityQueryTriggerDirective, Query, QueryClient, QueryDirective, QueryError, QueryField, QueryForm, QueryStore, buildErrorMessage, buildGqlCacheKey, buildQueryArrayString, buildQueryString, buildRoute, buildTimestampFromSeconds, createInfinityQueryConfig, createQueryCollection, decryptBearer, deepFreeze, def, detectContentTypeHeader, extractExpiresInSeconds, extractQuery, filterFailure, filterInvalidParams, filterNull, filterQueryStates, filterSuccess, forEachHeader, getDefaultHeaders, getResponseUrl, gql, hasHeader, invalidBaseRouteError, invalidBodyError, invalidRouteError, isBasicAuthProvider, isBearerAuthProvider, isCustomHeaderAuthProvider, isFetchResponse, isGqlQueryConfig, isParamValid, isQuery, isQueryStateCancelled, isQueryStateFailure, isQueryStateLoading, isQueryStatePrepared, isQueryStateSuccess, isRequestError, mergeHeaders, parseAllXhrResponseHeaders, pathParamsMissingInRouteFunctionError, queryStateAlreadyHasKeyError, queryStateCannotTransformError, queryStateDoesNotContainKeyError, request, serializeBody, shouldCacheQuery, skipPaginationPageParamCalculator, switchQueryCollectionState, switchQueryState, takeUntilResponse, transformGql, transformMethod, transformToBoolean, transformToBooleanArray, transformToDate, transformToDateArray, transformToNumber, transformToNumberArray, transformToSort, transformToSortQueryParam, transformToString, transformToStringArray };
1491
1759
  //# sourceMappingURL=ethlete-query.mjs.map