@ethlete/query 5.15.1 → 5.16.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 (184) hide show
  1. package/.eslintrc.json +40 -0
  2. package/CHANGELOG.md +8 -0
  3. package/jest.config.ts +23 -0
  4. package/ng-package.json +8 -0
  5. package/package.json +6 -23
  6. package/project.json +37 -0
  7. package/{index.d.ts → src/index.ts} +2 -0
  8. package/src/lib/auth/auth-provider.types.ts +160 -0
  9. package/src/lib/auth/auth-provider.utils.ts +39 -0
  10. package/src/lib/auth/basic-auth-provider.ts +17 -0
  11. package/src/lib/auth/bearer-auth-provider.ts +243 -0
  12. package/src/lib/auth/custom-header-auth-provider.ts +13 -0
  13. package/{lib/class-validator/class-validator.types.d.ts → src/lib/class-validator/class-validator.types.ts} +4 -3
  14. package/src/lib/class-validator/class-validator.utils.ts +5 -0
  15. package/src/lib/devtools/components/query-devtools/query-devtools.component.html +402 -0
  16. package/src/lib/devtools/components/query-devtools/query-devtools.component.scss +360 -0
  17. package/src/lib/devtools/components/query-devtools/query-devtools.component.ts +427 -0
  18. package/src/lib/devtools/pipes/query-short-name.pipe.ts +24 -0
  19. package/src/lib/devtools/utils/query-client.utils.ts +25 -0
  20. package/src/lib/directives/infinity-query-trigger.directive.ts +58 -0
  21. package/src/lib/directives/infinity-query.directive.ts +208 -0
  22. package/src/lib/directives/query.directive.ts +201 -0
  23. package/src/lib/entity/entity-store.ts +180 -0
  24. package/src/lib/entity/entity.types.ts +34 -0
  25. package/src/lib/entity/entity.utils.ts +51 -0
  26. package/src/lib/gql/gql.ts +22 -0
  27. package/src/lib/infinite-query/infinity-query.ts +245 -0
  28. package/src/lib/infinite-query/infinity-query.types.ts +149 -0
  29. package/src/lib/infinite-query/infinity-query.utils.ts +14 -0
  30. package/src/lib/logger/logger.ts +30 -0
  31. package/src/lib/query/query.ts +631 -0
  32. package/src/lib/query/query.types.ts +532 -0
  33. package/src/lib/query/query.utils.spec.ts +92 -0
  34. package/src/lib/query/query.utils.ts +450 -0
  35. package/src/lib/query-client/query-client.spec.ts +77 -0
  36. package/src/lib/query-client/query-client.ts +195 -0
  37. package/src/lib/query-client/query-client.types.ts +101 -0
  38. package/src/lib/query-client/query-client.utils.spec.ts +61 -0
  39. package/src/lib/query-client/query-client.utils.ts +28 -0
  40. package/src/lib/query-creator/query-creator.ts +103 -0
  41. package/src/lib/query-creator/query-creator.types.ts +74 -0
  42. package/src/lib/query-form/query-form.ts +526 -0
  43. package/src/lib/query-form/query-form.types.ts +169 -0
  44. package/src/lib/query-form/query-form.utils.ts +116 -0
  45. package/src/lib/query-store/query-store.ts +192 -0
  46. package/src/lib/request/request.ts +253 -0
  47. package/src/lib/request/request.types.ts +164 -0
  48. package/src/lib/request/request.util.spec.ts +177 -0
  49. package/src/lib/request/request.util.ts +399 -0
  50. package/src/lib/stories/angular-example-page.mdx +85 -0
  51. package/src/lib/stories/authentication-page.mdx +35 -0
  52. package/src/lib/stories/changelog-page.mdx +9 -0
  53. package/src/lib/stories/client-page.mdx +100 -0
  54. package/src/lib/stories/queries-page.mdx +117 -0
  55. package/src/lib/symfony/symfony.types.ts +21 -0
  56. package/src/lib/symfony/symfony.utils.ts +43 -0
  57. package/src/lib/utils/data.utils.ts +280 -0
  58. package/src/lib/utils/deep-freeze.util.ts +21 -0
  59. package/{lib/utils/def.util.d.ts → src/lib/utils/def.util.ts} +1 -1
  60. package/src/test-setup.ts +1 -0
  61. package/tsconfig.json +29 -0
  62. package/tsconfig.lib.json +12 -0
  63. package/tsconfig.lib.prod.json +10 -0
  64. package/tsconfig.spec.json +11 -0
  65. package/esm2022/ethlete-query.mjs +0 -5
  66. package/esm2022/index.mjs +0 -20
  67. package/esm2022/lib/auth/auth-provider.types.mjs +0 -2
  68. package/esm2022/lib/auth/auth-provider.utils.mjs +0 -25
  69. package/esm2022/lib/auth/basic-auth-provider.mjs +0 -15
  70. package/esm2022/lib/auth/bearer-auth-provider.mjs +0 -168
  71. package/esm2022/lib/auth/custom-header-auth-provider.mjs +0 -12
  72. package/esm2022/lib/auth/index.mjs +0 -6
  73. package/esm2022/lib/class-validator/class-validator.types.mjs +0 -2
  74. package/esm2022/lib/class-validator/class-validator.utils.mjs +0 -4
  75. package/esm2022/lib/class-validator/index.mjs +0 -3
  76. package/esm2022/lib/devtools/components/query-devtools/index.mjs +0 -2
  77. package/esm2022/lib/devtools/components/query-devtools/query-devtools.component.mjs +0 -297
  78. package/esm2022/lib/devtools/pipes/index.mjs +0 -2
  79. package/esm2022/lib/devtools/pipes/query-short-name.pipe.mjs +0 -24
  80. package/esm2022/lib/devtools/utils/index.mjs +0 -2
  81. package/esm2022/lib/devtools/utils/query-client.utils.mjs +0 -13
  82. package/esm2022/lib/directives/index.mjs +0 -4
  83. package/esm2022/lib/directives/infinity-query-trigger.directive.mjs +0 -56
  84. package/esm2022/lib/directives/infinity-query.directive.mjs +0 -147
  85. package/esm2022/lib/directives/query.directive.mjs +0 -123
  86. package/esm2022/lib/entity/entity-store.mjs +0 -127
  87. package/esm2022/lib/entity/entity.types.mjs +0 -2
  88. package/esm2022/lib/entity/entity.utils.mjs +0 -29
  89. package/esm2022/lib/entity/index.mjs +0 -4
  90. package/esm2022/lib/gql/gql.mjs +0 -23
  91. package/esm2022/lib/gql/index.mjs +0 -2
  92. package/esm2022/lib/infinite-query/index.mjs +0 -4
  93. package/esm2022/lib/infinite-query/infinity-query.mjs +0 -171
  94. package/esm2022/lib/infinite-query/infinity-query.types.mjs +0 -2
  95. package/esm2022/lib/infinite-query/infinity-query.utils.mjs +0 -3
  96. package/esm2022/lib/logger/index.mjs +0 -2
  97. package/esm2022/lib/logger/logger.mjs +0 -16
  98. package/esm2022/lib/query/index.mjs +0 -4
  99. package/esm2022/lib/query/query.mjs +0 -428
  100. package/esm2022/lib/query/query.types.mjs +0 -2
  101. package/esm2022/lib/query/query.utils.mjs +0 -253
  102. package/esm2022/lib/query-client/index.mjs +0 -4
  103. package/esm2022/lib/query-client/query-client.mjs +0 -80
  104. package/esm2022/lib/query-client/query-client.types.mjs +0 -2
  105. package/esm2022/lib/query-client/query-client.utils.mjs +0 -20
  106. package/esm2022/lib/query-creator/index.mjs +0 -3
  107. package/esm2022/lib/query-creator/query-creator.mjs +0 -55
  108. package/esm2022/lib/query-creator/query-creator.types.mjs +0 -2
  109. package/esm2022/lib/query-form/index.mjs +0 -4
  110. package/esm2022/lib/query-form/query-form.mjs +0 -368
  111. package/esm2022/lib/query-form/query-form.types.mjs +0 -2
  112. package/esm2022/lib/query-form/query-form.utils.mjs +0 -97
  113. package/esm2022/lib/query-store/index.mjs +0 -2
  114. package/esm2022/lib/query-store/query-store.mjs +0 -144
  115. package/esm2022/lib/request/index.mjs +0 -4
  116. package/esm2022/lib/request/request.mjs +0 -196
  117. package/esm2022/lib/request/request.types.mjs +0 -2
  118. package/esm2022/lib/request/request.util.mjs +0 -252
  119. package/esm2022/lib/symfony/index.mjs +0 -3
  120. package/esm2022/lib/symfony/symfony.types.mjs +0 -2
  121. package/esm2022/lib/symfony/symfony.utils.mjs +0 -31
  122. package/esm2022/lib/utils/data.utils.mjs +0 -138
  123. package/esm2022/lib/utils/deep-freeze.util.mjs +0 -17
  124. package/esm2022/lib/utils/def.util.mjs +0 -8
  125. package/esm2022/lib/utils/index.mjs +0 -4
  126. package/fesm2022/ethlete-query.mjs +0 -3276
  127. package/fesm2022/ethlete-query.mjs.map +0 -1
  128. package/lib/auth/auth-provider.types.d.ts +0 -133
  129. package/lib/auth/auth-provider.utils.d.ts +0 -9
  130. package/lib/auth/basic-auth-provider.d.ts +0 -10
  131. package/lib/auth/bearer-auth-provider.d.ts +0 -23
  132. package/lib/auth/custom-header-auth-provider.d.ts +0 -9
  133. package/lib/class-validator/class-validator.utils.d.ts +0 -2
  134. package/lib/devtools/components/query-devtools/query-devtools.component.d.ts +0 -151
  135. package/lib/devtools/pipes/query-short-name.pipe.d.ts +0 -8
  136. package/lib/devtools/utils/query-client.utils.d.ts +0 -12
  137. package/lib/directives/infinity-query-trigger.directive.d.ts +0 -15
  138. package/lib/directives/infinity-query.directive.d.ts +0 -49
  139. package/lib/directives/query.directive.d.ts +0 -63
  140. package/lib/entity/entity-store.d.ts +0 -25
  141. package/lib/entity/entity.types.d.ts +0 -25
  142. package/lib/entity/entity.utils.d.ts +0 -12
  143. package/lib/gql/gql.d.ts +0 -6
  144. package/lib/infinite-query/infinity-query.d.ts +0 -29
  145. package/lib/infinite-query/infinity-query.types.d.ts +0 -104
  146. package/lib/infinite-query/infinity-query.utils.d.ts +0 -4
  147. package/lib/logger/logger.d.ts +0 -12
  148. package/lib/query/query.d.ts +0 -60
  149. package/lib/query/query.types.d.ts +0 -388
  150. package/lib/query/query.utils.d.ts +0 -91
  151. package/lib/query-client/query-client.d.ts +0 -29
  152. package/lib/query-client/query-client.types.d.ts +0 -82
  153. package/lib/query-client/query-client.utils.d.ts +0 -4
  154. package/lib/query-creator/query-creator.d.ts +0 -20
  155. package/lib/query-creator/query-creator.types.d.ts +0 -13
  156. package/lib/query-form/query-form.d.ts +0 -89
  157. package/lib/query-form/query-form.types.d.ts +0 -138
  158. package/lib/query-form/query-form.utils.d.ts +0 -13
  159. package/lib/query-store/query-store.d.ts +0 -31
  160. package/lib/request/request.d.ts +0 -3
  161. package/lib/request/request.types.d.ts +0 -126
  162. package/lib/request/request.util.d.ts +0 -92
  163. package/lib/symfony/symfony.types.d.ts +0 -19
  164. package/lib/symfony/symfony.utils.d.ts +0 -6
  165. package/lib/utils/data.utils.d.ts +0 -59
  166. package/lib/utils/deep-freeze.util.d.ts +0 -1
  167. /package/{lib/auth/index.d.ts → src/lib/auth/index.ts} +0 -0
  168. /package/{lib/class-validator/index.d.ts → src/lib/class-validator/index.ts} +0 -0
  169. /package/{lib/devtools/components/query-devtools/index.d.ts → src/lib/devtools/components/query-devtools/index.ts} +0 -0
  170. /package/{lib/devtools/pipes/index.d.ts → src/lib/devtools/pipes/index.ts} +0 -0
  171. /package/{lib/devtools/utils/index.d.ts → src/lib/devtools/utils/index.ts} +0 -0
  172. /package/{lib/directives/index.d.ts → src/lib/directives/index.ts} +0 -0
  173. /package/{lib/entity/index.d.ts → src/lib/entity/index.ts} +0 -0
  174. /package/{lib/gql/index.d.ts → src/lib/gql/index.ts} +0 -0
  175. /package/{lib/infinite-query/index.d.ts → src/lib/infinite-query/index.ts} +0 -0
  176. /package/{lib/logger/index.d.ts → src/lib/logger/index.ts} +0 -0
  177. /package/{lib/query/index.d.ts → src/lib/query/index.ts} +0 -0
  178. /package/{lib/query-client/index.d.ts → src/lib/query-client/index.ts} +0 -0
  179. /package/{lib/query-creator/index.d.ts → src/lib/query-creator/index.ts} +0 -0
  180. /package/{lib/query-form/index.d.ts → src/lib/query-form/index.ts} +0 -0
  181. /package/{lib/query-store/index.d.ts → src/lib/query-store/index.ts} +0 -0
  182. /package/{lib/request/index.d.ts → src/lib/request/index.ts} +0 -0
  183. /package/{lib/symfony/index.d.ts → src/lib/symfony/index.ts} +0 -0
  184. /package/{lib/utils/index.d.ts → src/lib/utils/index.ts} +0 -0
@@ -0,0 +1,100 @@
1
+ import { Meta } from '@storybook/blocks';
2
+
3
+ <Meta title="HttpClient/Client" />
4
+
5
+ # HttpClient
6
+
7
+ For API requests, the Eth SDK offers you the option of using a powerful query client. The client includes standard authentication, e.g. with Bearer Token.
8
+
9
+ ## Clients
10
+
11
+ ```ts
12
+ import { QueryClient } from '@ethlete/query';
13
+
14
+ export const client = new QueryClient({
15
+ baseRoute: 'https://api.test.com',
16
+ });
17
+ ```
18
+
19
+ ### Options
20
+
21
+ ##### Logging
22
+
23
+ ```ts
24
+ export const client = new QueryClient({
25
+ baseRoute: 'https://api.test.com',
26
+ logging: {
27
+ /**
28
+ * Log all query state changes.
29
+ */
30
+ queryStateChanges?: boolean;
31
+ /**
32
+ * Log query state garbage collector runs.
33
+ */
34
+ queryStateGarbageCollector?: boolean;
35
+ /**
36
+ * Log if subscriptions are made to queries that are in the `prepared` state.
37
+ * This usually indicates that the `.execute()` method was forgotten.
38
+ */
39
+ preparedQuerySubscriptions?: boolean;
40
+ },
41
+ });
42
+ ```
43
+
44
+ ---
45
+
46
+ ##### Request
47
+
48
+ ```ts
49
+ import { QueryClient } from '@ethlete/query';
50
+
51
+ export const client = new QueryClient({
52
+ baseRoute: 'https://api.test.com',
53
+ request?: {
54
+ /**
55
+ * Adapter function used for extracting the time until the response is invalid.
56
+ * The default uses the `cache-control` (`max-age` & `s-maxage`), `age` and `expires` headers.
57
+ * Should return a number in seconds.
58
+ */
59
+ cacheAdapter?: CacheAdapterFn;
60
+ /**
61
+ * Default headers to be sent with every request.
62
+ * Will be overridden by headers passed to the query.
63
+ * Do not include the `Authorization` header here. Use an `AuthProvider` instead.
64
+ */
65
+ headers?: RequestHeaders | RequestHeadersMethodMap;
66
+ /**
67
+ * Whether to automatically refresh expired queries when the window regains focus.
68
+ * @default true
69
+ */
70
+ autoRefreshQueriesOnWindowFocus?: boolean;
71
+ /**
72
+ * Whether to automatically stop polling queries when the window loses focus.
73
+ * Polling will resume when the window regains focus.
74
+ * @default true
75
+ */
76
+ enableSmartPolling?: boolean;
77
+ /**
78
+ * A retry function to be used for all queries.
79
+ * @default shouldRetryRequest()
80
+ */
81
+ retryFn?: RequestRetryFn;
82
+ /**
83
+ * Configuration options for all graphql queries.
84
+ */
85
+ gql?: {
86
+ /**
87
+ * Determines if the query should be sent via GET or POST.
88
+ * - `GET`: The query will be sent via query parameters.
89
+ * - `POST`: The query will be sent via the body.
90
+ * @default 'POST'
91
+ */
92
+ transferVia?: GqlTransferOption;
93
+ };
94
+ /**
95
+ * Configuration options for all query strings.
96
+ */
97
+ queryParams?: BuildQueryStringConfig;
98
+ }
99
+ });
100
+ ```
@@ -0,0 +1,117 @@
1
+ import { Meta } from '@storybook/blocks';
2
+
3
+ <Meta title="HttpClient/Queries" />
4
+
5
+ # Queries
6
+
7
+ We use the client to create so-called queries, which we can then use in our components. The client executes the request and offers many convenient features, e.g. to intercept loading or error states or to activate polling on the request.
8
+
9
+ ## Example
10
+
11
+ ```ts
12
+ import { def } from '@ethlete/query';
13
+ import { client } from '../fut-api.client';
14
+
15
+ // Interfaces
16
+ interface PostLoginArgs {
17
+ email: string;
18
+ password: string;
19
+ }
20
+
21
+ interface PostRefreshTokenArgs {
22
+ refreshToken: string;
23
+ }
24
+
25
+ interface AuthorizationTokenView {
26
+ token: string;
27
+ refreshToken: string;
28
+ }
29
+
30
+ // Queries
31
+ export const postLogin = client.post({
32
+ route: '/auth/login',
33
+ secure: false,
34
+ types: {
35
+ args: def<PostLoginArgs>(),
36
+ response: def<AuthorizationTokenView>(),
37
+ },
38
+ });
39
+
40
+ export const postRefreshToken = client.post({
41
+ route: '/auth/refresh-token',
42
+ secure: false,
43
+ types: {
44
+ args: def<PostRefreshTokenArgs>(),
45
+ response: def<AuthorizationTokenView>(),
46
+ },
47
+ });
48
+ ```
49
+
50
+ ## Options
51
+
52
+ ```ts
53
+
54
+ {
55
+ /**
56
+ * The http method to use for the query.
57
+ */
58
+ method: Method;
59
+ /**
60
+ * Determines if the auth provider should be used for this query.
61
+ * The query **will throw** if the query client's auth provider is unset.
62
+ */
63
+ secure?: boolean;
64
+ /**
65
+ * Determines if the query should emit progress events.
66
+ */
67
+ reportProgress?: boolean;
68
+ /**
69
+ * Determines the query's response type.
70
+ * @default 'json'
71
+ */
72
+ responseType?: 'arraybuffer' | 'blob' | 'json' | 'text';
73
+ /**
74
+ * Whether this request should be sent with outgoing credentials (cookies).
75
+ * @default false
76
+ */
77
+ withCredentials?: boolean;
78
+ /**
79
+ * Configuration for handling auto refresh triggers.
80
+ *
81
+ * **Note:** This is only available for queries that can be refreshed. (`GET`, `HEAD`, `OPTIONS`, `GQL_QUERY`)
82
+ */
83
+ autoRefreshOn?: QueryAutoRefreshConfig;
84
+ /**
85
+ * Whether to automatically stop polling for this query when the window loses focus.
86
+ * Polling will resume when the window regains focus.
87
+ *
88
+ * This can only be disabled if `enableSmartPolling` is enabled on the query client.
89
+ * @default true
90
+ */
91
+ enableSmartPolling?: boolean;
92
+ /**
93
+ * Object containing the query's type information.
94
+ */
95
+ types?: {
96
+ /**
97
+ * The type of the successful response.
98
+ */
99
+ response?: Response;
100
+ /**
101
+ * Arguments for executing the query.
102
+ *
103
+ * - `pathParams`: The path parameters for the query. (in front of the ? in the url)
104
+ * - `queryParams`: The query parameters for the query. (after the ? in the url)
105
+ * - `body`: The body for the query. Unavailable for GET, HEAD and OPTIONS requests.
106
+ * - `headers`: The headers for the query.
107
+ * - `variables`: The variables for the query. (graphql only)
108
+ */
109
+ args?: Arguments;
110
+ };
111
+ /**
112
+ * Object containing the query's entity store information.
113
+ */
114
+ entity?: QueryEntityConfig<Store, Data, Response, Arguments, Id>;
115
+ };
116
+
117
+ ```
@@ -0,0 +1,21 @@
1
+ import { HttpStatusCode } from '../request';
2
+
3
+ export interface SymfonyErrorTrace {
4
+ args: string[];
5
+ class: string;
6
+ file: string;
7
+ function: string;
8
+ line: number;
9
+ namespace: string;
10
+ short_class: string;
11
+ type: string;
12
+ }
13
+
14
+ export interface SymfonyError {
15
+ class: string;
16
+ detail: string;
17
+ status: HttpStatusCode;
18
+ title: string;
19
+ trace: SymfonyErrorTrace[];
20
+ type: string;
21
+ }
@@ -0,0 +1,43 @@
1
+ import { FormViolationListView, FormViolationView } from '@ethlete/types';
2
+ import { SymfonyError } from './symfony.types';
3
+
4
+ export const isSymfonyError = (error: unknown): error is SymfonyError => {
5
+ if (!error || typeof error !== 'object') {
6
+ return false;
7
+ }
8
+
9
+ const symfonyError = error as SymfonyError;
10
+
11
+ return (
12
+ symfonyError.class !== undefined &&
13
+ symfonyError.detail !== undefined &&
14
+ symfonyError.status !== undefined &&
15
+ symfonyError.title !== undefined &&
16
+ symfonyError.trace !== undefined &&
17
+ symfonyError.type !== undefined
18
+ );
19
+ };
20
+
21
+ export const isSymfonyPagerfantaOutOfRangeError = (error: unknown): error is SymfonyError => {
22
+ if (!isSymfonyError(error)) {
23
+ return false;
24
+ }
25
+
26
+ return error.class.startsWith('Pagerfanta') && error.class.endsWith('OutOfRangeCurrentPageException');
27
+ };
28
+
29
+ export const isSymfonyFormViolationListError = (error: unknown): error is FormViolationListView => {
30
+ return typeof error === 'object' && error !== null && 'violations' in error;
31
+ };
32
+
33
+ export const isSymfonyListError = (error: unknown): error is FormViolationView[] => {
34
+ return (
35
+ !!error &&
36
+ typeof error === 'object' &&
37
+ Array.isArray(error) &&
38
+ !!error.length &&
39
+ 'message' in error[0] &&
40
+ 'propertyPath' in error[0] &&
41
+ 'invalidValue' in error[0]
42
+ );
43
+ };
@@ -0,0 +1,280 @@
1
+ import { CreateComputedOptions, Injector, Signal, assertInInjectionContext, computed, inject } from '@angular/core';
2
+ import { ToObservableOptions, ToSignalOptions, toObservable, toSignal } from '@angular/core/rxjs-interop';
3
+ import { computedTillTruthy, createDestroy } from '@ethlete/core';
4
+ import { Observable, Subscribable, of, pairwise, startWith, switchMap, takeUntil, tap } from 'rxjs';
5
+ import {
6
+ AnyQuery,
7
+ AnyQueryCollection,
8
+ QueryOf,
9
+ QueryState,
10
+ QueryStateType,
11
+ extractQuery,
12
+ filterQueryStates,
13
+ isQuery,
14
+ isQueryStateFailure,
15
+ isQueryStateLoading,
16
+ isQueryStateSuccess,
17
+ } from '../query';
18
+ import { QueryDataOf } from '../query-creator';
19
+
20
+ export interface QueryContainerConfig {
21
+ /**
22
+ * If `true`, the previous query will be aborted when a new query is pushed into the container.
23
+ * @default true // Only if the request can be cached (GET, OPTIONS, HEAD and GQL_QUERY). Otherwise false.
24
+ */
25
+ abortPrevious?: boolean;
26
+
27
+ /**
28
+ * If `true`, the query will be aborted when the container is destroyed.
29
+ * @default true // Only if the request can be cached (GET, OPTIONS, HEAD and GQL_QUERY). Otherwise false.
30
+ */
31
+ abortOnDestroy?: boolean;
32
+
33
+ /**
34
+ * If `true`, the previous polling will be stopped when a new query is pushed into the container.
35
+ * @default true // Only if the query has no other dependents and the request can be cached (GET, OPTIONS, HEAD and GQL_QUERY). Otherwise false.
36
+ */
37
+ stopPreviousPolling?: boolean;
38
+ }
39
+
40
+ export interface QueryFilterConfig {
41
+ /**
42
+ * If `true`, the response will be cached until the next response is received or the query fails.
43
+ * @default false
44
+ */
45
+ cacheResponse?: boolean;
46
+ }
47
+
48
+ export const addQueryContainerHandling = (
49
+ obs: Observable<AnyQuery | AnyQuery[] | null>,
50
+ valueFn: () => AnyQuery | AnyQuery[] | null | undefined,
51
+ config?: QueryContainerConfig,
52
+ ) => {
53
+ assertInInjectionContext(addQueryContainerHandling);
54
+
55
+ const { abortPrevious, abortOnDestroy, stopPreviousPolling } = config ?? {};
56
+
57
+ const injector = inject(Injector);
58
+ const destroy$ = createDestroy();
59
+
60
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
+ const tNode = (injector as any)._tNode;
62
+ const componentId = tNode?.index ?? -1;
63
+
64
+ obs
65
+ .pipe(
66
+ takeUntil(destroy$),
67
+ startWith(null),
68
+ pairwise(),
69
+ tap(([prevQuery, currQuery]) => {
70
+ const cleanQuery = (q: AnyQuery | null | undefined) => {
71
+ if (!q?._hasDependents() && ((abortPrevious === undefined && q?.canBeCached) || abortPrevious)) {
72
+ q?.abort();
73
+ }
74
+
75
+ if (
76
+ !q?._hasDependents() &&
77
+ ((stopPreviousPolling === undefined && q?.canBeCached) || stopPreviousPolling) &&
78
+ q?.isPolling
79
+ ) {
80
+ q?.stopPolling();
81
+ }
82
+ };
83
+
84
+ if ((isQuery(prevQuery) || prevQuery === null) && (isQuery(currQuery) || currQuery === null)) {
85
+ prevQuery?._removeDependent(componentId);
86
+ currQuery?._addDependent(componentId);
87
+
88
+ cleanQuery(prevQuery);
89
+ } else if (
90
+ (Array.isArray(prevQuery) || prevQuery === null) &&
91
+ (Array.isArray(currQuery) || currQuery === null)
92
+ ) {
93
+ if (prevQuery) {
94
+ for (let i = 0; i < prevQuery.length; i++) {
95
+ prevQuery[i]?._removeDependent(componentId);
96
+ }
97
+ }
98
+
99
+ if (currQuery) {
100
+ for (let i = 0; i < currQuery.length; i++) {
101
+ currQuery[i]?._addDependent(componentId);
102
+ }
103
+ }
104
+
105
+ if (prevQuery) {
106
+ for (let i = 0; i < prevQuery.length; i++) {
107
+ cleanQuery(prevQuery[i]);
108
+ }
109
+ }
110
+ } else if (
111
+ (isQuery(prevQuery) && Array.isArray(currQuery)) ||
112
+ (Array.isArray(prevQuery) && isQuery(currQuery))
113
+ ) {
114
+ throw new Error('Cannot mix queries and arrays of queries in the same query container.');
115
+ }
116
+ }),
117
+ )
118
+ .subscribe();
119
+
120
+ destroy$.subscribe(() => {
121
+ const query = valueFn();
122
+
123
+ const handleQuery = (q: AnyQuery | null | undefined) => {
124
+ q?._removeDependent(componentId);
125
+
126
+ if (!q?._hasDependents() && ((q?.canBeCached && abortOnDestroy === undefined) || abortOnDestroy)) {
127
+ q?.abort();
128
+ }
129
+ };
130
+
131
+ if (isQuery(query)) {
132
+ handleQuery(query);
133
+ } else if (Array.isArray(query)) {
134
+ for (const q of query) {
135
+ handleQuery(q);
136
+ }
137
+ }
138
+ });
139
+ };
140
+
141
+ export function toQuerySignal<T extends AnyQuery | null>(
142
+ source: Observable<T> | Subscribable<T>,
143
+ ): Signal<T | undefined>;
144
+ export function toQuerySignal<T extends AnyQuery | null>(
145
+ source: Observable<T> | Subscribable<T>,
146
+ options: ToSignalOptions & { initialValue?: undefined; requireSync?: false } & QueryContainerConfig,
147
+ ): Signal<T | undefined>;
148
+ export function toQuerySignal<T extends AnyQuery | null>(
149
+ source: Observable<T> | Subscribable<T>,
150
+ options: ToSignalOptions & { initialValue?: null; requireSync?: false } & QueryContainerConfig,
151
+ ): Signal<T | null>;
152
+ export function toQuerySignal<T extends AnyQuery | null>(
153
+ source: Observable<T> | Subscribable<T>,
154
+ options: ToSignalOptions & { initialValue?: undefined; requireSync: true } & QueryContainerConfig,
155
+ ): Signal<T>;
156
+ export function toQuerySignal<T extends AnyQuery | null, const U extends T>(
157
+ source: Observable<T> | Subscribable<T>,
158
+ options: ToSignalOptions & { initialValue: U; requireSync?: false } & QueryContainerConfig,
159
+ ): Signal<T | U>;
160
+
161
+ export function toQuerySignal<T extends AnyQuery | null, U = undefined>(
162
+ source: Observable<T> | Subscribable<T>,
163
+ options?: ToSignalOptions & { initialValue?: U } & QueryContainerConfig,
164
+ ): Signal<T | U> {
165
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
166
+ const s = toSignal(source, options as any);
167
+
168
+ addQueryContainerHandling(source as Observable<T>, () => s() as T, options);
169
+
170
+ return s as Signal<T | U>;
171
+ }
172
+
173
+ export function queryComputed<T extends AnyQuery | null>(
174
+ computation: () => T,
175
+ options?: CreateComputedOptions<T> & QueryContainerConfig & ToObservableOptions,
176
+ ): Signal<T> {
177
+ const c = computed(computation, options);
178
+ const obs = toObservable(c, options);
179
+
180
+ addQueryContainerHandling(obs, () => c(), options);
181
+
182
+ return c;
183
+ }
184
+
185
+ /**
186
+ * Creates a signal that will only be reactive until the first query is created.
187
+ * All subsequent changes inside the computation will be ignored.
188
+ */
189
+ export function queryComputedTillTruthy<T extends AnyQuery | null>(
190
+ computation: () => T,
191
+ options?: CreateComputedOptions<T> & QueryContainerConfig & ToObservableOptions,
192
+ ): Signal<T | null> {
193
+ const c = computedTillTruthy(computed(computation, options));
194
+ const obs = toObservable(c, options);
195
+
196
+ addQueryContainerHandling(obs, () => c(), options);
197
+
198
+ return c;
199
+ }
200
+
201
+ export function queryArrayComputed<T extends AnyQuery[] | null>(
202
+ computation: () => T,
203
+ options?: CreateComputedOptions<T> & QueryContainerConfig & ToObservableOptions,
204
+ ): Signal<T> {
205
+ const c = computed(computation, options);
206
+ const obs = toObservable(c, options);
207
+
208
+ addQueryContainerHandling(obs, () => c(), options);
209
+
210
+ return c;
211
+ }
212
+
213
+ export function toQuerySubject<T extends AnyQuery | null>(
214
+ source: Signal<T>,
215
+ options?: ToObservableOptions & QueryContainerConfig,
216
+ ): Observable<T> {
217
+ const obs = toObservable(source, options);
218
+
219
+ addQueryContainerHandling(obs, () => source(), options);
220
+
221
+ return obs;
222
+ }
223
+
224
+ export function queryStateSignal<T extends Signal<AnyQuery | AnyQueryCollection | null>>(
225
+ source: T,
226
+ options?: QueryFilterConfig,
227
+ ) {
228
+ const { cacheResponse } = options ?? {};
229
+
230
+ return toSignal(
231
+ toObservable(source).pipe(
232
+ switchMap((q) => extractQuery(q)?.state$ ?? of(null)),
233
+ switchMap((state) => {
234
+ if (cacheResponse) {
235
+ return of(state).pipe(
236
+ filterQueryStates([QueryStateType.Success, QueryStateType.Failure, QueryStateType.Cancelled]),
237
+ );
238
+ }
239
+
240
+ return of(state);
241
+ }),
242
+ ),
243
+ {
244
+ initialValue: null,
245
+ },
246
+ ) as Signal<QueryState<QueryDataOf<QueryOf<ReturnType<T>>>> | null>;
247
+ }
248
+
249
+ export function queryStateResponseSignal<T extends Signal<AnyQuery | AnyQueryCollection | null>>(
250
+ source: T,
251
+ options?: QueryFilterConfig,
252
+ ) {
253
+ const s = queryStateSignal(source, options);
254
+
255
+ return computed(() => {
256
+ const state = s();
257
+
258
+ return isQueryStateSuccess(state) ? state.response : null;
259
+ });
260
+ }
261
+
262
+ export function queryStateErrorSignal<T extends Signal<AnyQuery | AnyQueryCollection | null>>(source: T) {
263
+ const s = queryStateSignal(source);
264
+
265
+ return computed(() => {
266
+ const state = s();
267
+
268
+ return isQueryStateFailure(state) ? state.error : null;
269
+ });
270
+ }
271
+
272
+ export function queryStateLoadingSignal<T extends Signal<AnyQuery | AnyQueryCollection | null>>(source: T) {
273
+ const s = queryStateSignal(source);
274
+
275
+ return computed(() => {
276
+ const state = s();
277
+
278
+ return isQueryStateLoading(state) ? state : null;
279
+ });
280
+ }
@@ -0,0 +1,21 @@
1
+ export const deepFreeze = <T extends Record<string, unknown>>(object: T) => {
2
+ // check if object is actually an object or frozen
3
+ if (!object || typeof object !== 'object' || Object.isFrozen(object)) {
4
+ return object;
5
+ }
6
+
7
+ // Retrieve the property names defined on object
8
+ const propNames = Object.getOwnPropertyNames(object);
9
+
10
+ // Freeze properties before freezing self
11
+
12
+ for (const name of propNames) {
13
+ const value = object[name];
14
+
15
+ if (value && typeof value === 'object') {
16
+ deepFreeze(value as T);
17
+ }
18
+ }
19
+
20
+ return Object.freeze(object);
21
+ };
@@ -4,4 +4,4 @@
4
4
  * @see https://github.com/microsoft/TypeScript/issues/26242
5
5
  * @returns null
6
6
  */
7
- export declare const def: <Data>() => Data;
7
+ export const def = <Data>() => null as unknown as Data;
@@ -0,0 +1 @@
1
+ import 'jest-preset-angular/setup-jest';
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2022",
4
+ "useDefineForClassFields": false,
5
+ "forceConsistentCasingInFileNames": true,
6
+ "strict": true,
7
+ "noImplicitOverride": true,
8
+ "noPropertyAccessFromIndexSignature": true,
9
+ "noImplicitReturns": true,
10
+ "noFallthroughCasesInSwitch": true
11
+ },
12
+ "files": [],
13
+ "include": [],
14
+ "references": [
15
+ {
16
+ "path": "./tsconfig.lib.json"
17
+ },
18
+ {
19
+ "path": "./tsconfig.spec.json"
20
+ }
21
+ ],
22
+ "extends": "../../tsconfig.base.json",
23
+ "angularCompilerOptions": {
24
+ "enableI18nLegacyMessageIdFormat": false,
25
+ "strictInjectionParameters": true,
26
+ "strictInputAccessModifiers": true,
27
+ "strictTemplates": true
28
+ }
29
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../dist/out-tsc",
5
+ "declaration": true,
6
+ "declarationMap": true,
7
+ "inlineSources": true,
8
+ "types": ["mdx"]
9
+ },
10
+ "exclude": ["src/test-setup.ts", "src/**/*.spec.ts", "jest.config.ts", "src/**/*.test.ts"],
11
+ "include": ["src/**/*.ts"]
12
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "./tsconfig.lib.json",
3
+ "compilerOptions": {
4
+ "declarationMap": false,
5
+ "stripInternal": true
6
+ },
7
+ "angularCompilerOptions": {
8
+ "compilationMode": "partial"
9
+ }
10
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../dist/out-tsc",
5
+ "module": "commonjs",
6
+ "types": ["jest", "node"],
7
+ "target": "es2016"
8
+ },
9
+ "files": ["src/test-setup.ts"],
10
+ "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"]
11
+ }
@@ -1,5 +0,0 @@
1
- /**
2
- * Generated bundle index. Do not edit.
3
- */
4
- export * from './index';
5
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXRobGV0ZS1xdWVyeS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2xpYnMvcXVlcnkvc3JjL2V0aGxldGUtcXVlcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxjQUFjLFNBQVMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogR2VuZXJhdGVkIGJ1bmRsZSBpbmRleC4gRG8gbm90IGVkaXQuXG4gKi9cblxuZXhwb3J0ICogZnJvbSAnLi9pbmRleCc7XG4iXX0=
package/esm2022/index.mjs DELETED
@@ -1,20 +0,0 @@
1
- export * from './lib/auth';
2
- export * from './lib/class-validator';
3
- export * from './lib/devtools/components/query-devtools';
4
- export * from './lib/devtools/pipes';
5
- export * from './lib/devtools/utils';
6
- export * from './lib/directives';
7
- export * from './lib/entity';
8
- export * from './lib/gql';
9
- export * from './lib/infinite-query';
10
- export * from './lib/logger';
11
- export * from './lib/query';
12
- export * from './lib/query-client';
13
- export * from './lib/query-creator';
14
- export * from './lib/query-form';
15
- export * from './lib/query-store';
16
- export * from './lib/request';
17
- export * from './lib/symfony';
18
- export * from './lib/utils';
19
- // Burst cache
20
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9saWJzL3F1ZXJ5L3NyYy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLFlBQVksQ0FBQztBQUMzQixjQUFjLHVCQUF1QixDQUFDO0FBQ3RDLGNBQWMsMENBQTBDLENBQUM7QUFDekQsY0FBYyxzQkFBc0IsQ0FBQztBQUNyQyxjQUFjLHNCQUFzQixDQUFDO0FBQ3JDLGNBQWMsa0JBQWtCLENBQUM7QUFDakMsY0FBYyxjQUFjLENBQUM7QUFDN0IsY0FBYyxXQUFXLENBQUM7QUFDMUIsY0FBYyxzQkFBc0IsQ0FBQztBQUNyQyxjQUFjLGNBQWMsQ0FBQztBQUM3QixjQUFjLGFBQWEsQ0FBQztBQUM1QixjQUFjLG9CQUFvQixDQUFDO0FBQ25DLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyxrQkFBa0IsQ0FBQztBQUNqQyxjQUFjLG1CQUFtQixDQUFDO0FBQ2xDLGNBQWMsZUFBZSxDQUFDO0FBQzlCLGNBQWMsZUFBZSxDQUFDO0FBQzlCLGNBQWMsYUFBYSxDQUFDO0FBRTVCLGNBQWMiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgKiBmcm9tICcuL2xpYi9hdXRoJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NsYXNzLXZhbGlkYXRvcic7XG5leHBvcnQgKiBmcm9tICcuL2xpYi9kZXZ0b29scy9jb21wb25lbnRzL3F1ZXJ5LWRldnRvb2xzJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2RldnRvb2xzL3BpcGVzJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2RldnRvb2xzL3V0aWxzJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2RpcmVjdGl2ZXMnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvZW50aXR5JztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2dxbCc7XG5leHBvcnQgKiBmcm9tICcuL2xpYi9pbmZpbml0ZS1xdWVyeSc7XG5leHBvcnQgKiBmcm9tICcuL2xpYi9sb2dnZXInO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvcXVlcnknO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvcXVlcnktY2xpZW50JztcbmV4cG9ydCAqIGZyb20gJy4vbGliL3F1ZXJ5LWNyZWF0b3InO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvcXVlcnktZm9ybSc7XG5leHBvcnQgKiBmcm9tICcuL2xpYi9xdWVyeS1zdG9yZSc7XG5leHBvcnQgKiBmcm9tICcuL2xpYi9yZXF1ZXN0JztcbmV4cG9ydCAqIGZyb20gJy4vbGliL3N5bWZvbnknO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvdXRpbHMnO1xuXG4vLyBCdXJzdCBjYWNoZVxuIl19