@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,195 @@
1
+ import { BehaviorSubject } from 'rxjs';
2
+ import { AuthProvider } from '../auth';
3
+ import { EntityStore } from '../entity';
4
+ import {
5
+ BaseArguments,
6
+ GqlQueryConfig,
7
+ GqlQueryConfigWithoutMethod,
8
+ QueryConfigWithoutMethod,
9
+ RestQueryConfig,
10
+ RouteType,
11
+ } from '../query';
12
+ import { QueryCreator } from '../query-creator';
13
+ import { QueryStore } from '../query-store';
14
+ import { RequestHeaders, RequestHeadersMethodMap } from '../request';
15
+ import { QueryClientConfig } from './query-client.types';
16
+
17
+ export class QueryClient {
18
+ /**
19
+ * @internal
20
+ */
21
+ readonly _store: QueryStore;
22
+
23
+ get config() {
24
+ return this._clientConfig;
25
+ }
26
+
27
+ get authProvider() {
28
+ return this._authProvider$.getValue() ?? this._clientConfig.parent?._authProvider$.getValue() ?? null;
29
+ }
30
+ get authProvider$() {
31
+ return this._authProvider$.asObservable() ?? this._clientConfig.parent?._authProvider$.asObservable() ?? null;
32
+ }
33
+ private readonly _authProvider$ = new BehaviorSubject<AuthProvider | null>(null);
34
+
35
+ constructor(private _clientConfig: QueryClientConfig) {
36
+ this._store = new QueryStore({
37
+ enableChangeLogging: _clientConfig.logging?.queryStateChanges,
38
+ enableGarbageCollectorLogging: _clientConfig.logging?.queryStateGarbageCollector,
39
+ autoRefreshQueriesOnWindowFocus: _clientConfig.request?.autoRefreshQueriesOnWindowFocus ?? true,
40
+ enableSmartPolling: _clientConfig.request?.enableSmartPolling ?? true,
41
+ });
42
+ }
43
+
44
+ get = <
45
+ Route extends RouteType<Arguments>,
46
+ Response,
47
+ Id,
48
+ Data = Response,
49
+ Arguments extends BaseArguments | undefined = undefined,
50
+ Store extends EntityStore<unknown> = EntityStore<unknown>,
51
+ >(
52
+ queryConfig: QueryConfigWithoutMethod<Route, Response, Arguments, Store, Data, Id>,
53
+ ) =>
54
+ this.fetch<Route, Response, Arguments, Id, Store, Data>({
55
+ ...queryConfig,
56
+ method: 'GET',
57
+ });
58
+
59
+ post = <
60
+ Route extends RouteType<Arguments>,
61
+ Response,
62
+ Id,
63
+ Data = Response,
64
+ Arguments extends BaseArguments | undefined = undefined,
65
+ Store extends EntityStore<unknown> = EntityStore<unknown>,
66
+ >(
67
+ queryConfig: QueryConfigWithoutMethod<Route, Response, Arguments, Store, Data, Id>,
68
+ ) =>
69
+ this.fetch<Route, Response, Arguments, Id, Store, Data>({
70
+ ...queryConfig,
71
+ method: 'POST',
72
+ });
73
+
74
+ put = <
75
+ Route extends RouteType<Arguments>,
76
+ Response,
77
+ Id,
78
+ Data = Response,
79
+ Arguments extends BaseArguments | undefined = undefined,
80
+ Store extends EntityStore<unknown> = EntityStore<unknown>,
81
+ >(
82
+ queryConfig: QueryConfigWithoutMethod<Route, Response, Arguments, Store, Data, Id>,
83
+ ) =>
84
+ this.fetch<Route, Response, Arguments, Id, Store, Data>({
85
+ ...queryConfig,
86
+ method: 'PUT',
87
+ });
88
+
89
+ patch = <
90
+ Route extends RouteType<Arguments>,
91
+ Response,
92
+ Id,
93
+ Data = Response,
94
+ Arguments extends BaseArguments | undefined = undefined,
95
+ Store extends EntityStore<unknown> = EntityStore<unknown>,
96
+ >(
97
+ queryConfig: QueryConfigWithoutMethod<Route, Response, Arguments, Store, Data, Id>,
98
+ ) =>
99
+ this.fetch<Route, Response, Arguments, Id, Store, Data>({
100
+ ...queryConfig,
101
+ method: 'PATCH',
102
+ });
103
+
104
+ delete = <
105
+ Route extends RouteType<Arguments>,
106
+ Response,
107
+ Id,
108
+ Data = Response,
109
+ Arguments extends BaseArguments | undefined = undefined,
110
+ Store extends EntityStore<unknown> = EntityStore<unknown>,
111
+ >(
112
+ queryConfig: QueryConfigWithoutMethod<Route, Response, Arguments, Store, Data, Id>,
113
+ ) =>
114
+ this.fetch<Route, Response, Arguments, Id, Store, Data>({
115
+ ...queryConfig,
116
+ method: 'DELETE',
117
+ });
118
+
119
+ gqlQuery = <
120
+ Route extends RouteType<Arguments>,
121
+ Response,
122
+ Id,
123
+ Data = Response,
124
+ Arguments extends BaseArguments | undefined = undefined,
125
+ Store extends EntityStore<unknown> = EntityStore<unknown>,
126
+ >(
127
+ queryConfig: GqlQueryConfigWithoutMethod<Route, Response, Arguments, Store, Data, Id>,
128
+ ) =>
129
+ this.fetch<Route, Response, Arguments, Id, Store, Data>({
130
+ ...queryConfig,
131
+ method: 'GQL_QUERY',
132
+ });
133
+
134
+ gqlMutate = <
135
+ Route extends RouteType<Arguments>,
136
+ Response,
137
+ Id,
138
+ Data = Response,
139
+ Arguments extends BaseArguments | undefined = undefined,
140
+ Store extends EntityStore<unknown> = EntityStore<unknown>,
141
+ >(
142
+ queryConfig: GqlQueryConfigWithoutMethod<Route, Response, Arguments, Store, Data, Id>,
143
+ ) =>
144
+ this.fetch<Route, Response, Arguments, Id, Store, Data>({
145
+ ...queryConfig,
146
+ method: 'GQL_MUTATE',
147
+ });
148
+
149
+ fetch = <
150
+ Route extends RouteType<Arguments>,
151
+ Response,
152
+ Arguments extends BaseArguments | undefined,
153
+ Id,
154
+ Store extends EntityStore<unknown> = EntityStore<unknown>,
155
+ Data = Response,
156
+ >(
157
+ queryConfig:
158
+ | RestQueryConfig<Route, Response, Arguments, Store, Data, Id>
159
+ | GqlQueryConfig<Route, Response, Arguments, Store, Data, Id>,
160
+ ) => new QueryCreator<Arguments, Response, Route, Store, Data, Id>(queryConfig, this, this._store);
161
+
162
+ setAuthProvider = (authProvider: AuthProvider) => {
163
+ if (this.authProvider) {
164
+ this.authProvider?.cleanUp();
165
+ }
166
+
167
+ this._authProvider$.next(authProvider);
168
+ };
169
+
170
+ setDefaultHeaders = (config: {
171
+ headers: RequestHeaders | RequestHeadersMethodMap | null;
172
+ refreshQueriesInUse?: boolean;
173
+ }) => {
174
+ if (!this._clientConfig.request) {
175
+ this._clientConfig.request = {
176
+ headers: config.headers ?? undefined,
177
+ };
178
+ } else {
179
+ this._clientConfig.request.headers = config.headers ?? undefined;
180
+ }
181
+
182
+ if (config.refreshQueriesInUse) {
183
+ this._store.refreshQueriesInUse({ purgeUnused: true, ignoreCacheValidity: true });
184
+ }
185
+ };
186
+
187
+ clearAuthProvider = () => {
188
+ this.authProvider?.cleanUp();
189
+ this._authProvider$.next(null);
190
+ };
191
+
192
+ _updateBaseRoute = (route: string) => {
193
+ this._clientConfig.baseRoute = route;
194
+ };
195
+ }
@@ -0,0 +1,101 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { GqlTransferOption } from '../query/query.types';
3
+ import {
4
+ BuildQueryStringConfig,
5
+ CacheAdapterFn,
6
+ RequestHeaders,
7
+ RequestHeadersMethodMap,
8
+ RequestRetryFn,
9
+ } from '../request';
10
+ import { QueryClient } from './query-client';
11
+
12
+ export interface QueryClientConfig {
13
+ /**
14
+ * The api base route. Should **not** end with a trailing slash.
15
+ * @example 'https://api.example.com'
16
+ */
17
+ baseRoute: string;
18
+
19
+ /**
20
+ * Logging configuration for debugging.
21
+ */
22
+ logging?: {
23
+ /**
24
+ * Log all query state changes.
25
+ */
26
+ queryStateChanges?: boolean;
27
+
28
+ /**
29
+ * Log query state garbage collector runs.
30
+ */
31
+ queryStateGarbageCollector?: boolean;
32
+
33
+ /**
34
+ * Log if subscriptions are made to queries that are in the `prepared` state.
35
+ * This usually indicates that the `.execute()` method was forgotten.
36
+ */
37
+ preparedQuerySubscriptions?: boolean;
38
+ };
39
+
40
+ /**
41
+ * Request options.
42
+ */
43
+ request?: {
44
+ /**
45
+ * Adapter function used for extracting the time until the response is invalid.
46
+ * The default uses the `cache-control` (`max-age` & `s-maxage`), `age` and `expires` headers.
47
+ * Should return a number in seconds.
48
+ */
49
+ cacheAdapter?: CacheAdapterFn;
50
+
51
+ /**
52
+ * Default headers to be sent with every request.
53
+ * Will be overridden by headers passed to the query.
54
+ * Do not include the `Authorization` header here. Use an `AuthProvider` instead.
55
+ */
56
+ headers?: RequestHeaders | RequestHeadersMethodMap;
57
+
58
+ /**
59
+ * Whether to automatically refresh expired queries when the window regains focus.
60
+ * @default true
61
+ */
62
+ autoRefreshQueriesOnWindowFocus?: boolean;
63
+
64
+ /**
65
+ * Whether to automatically stop polling queries when the window loses focus.
66
+ * Polling will resume when the window regains focus.
67
+ * @default true
68
+ */
69
+ enableSmartPolling?: boolean;
70
+
71
+ /**
72
+ * A retry function to be used for all queries.
73
+ * @default shouldRetryRequest()
74
+ */
75
+ retryFn?: RequestRetryFn;
76
+
77
+ /**
78
+ * Configuration options for all graphql queries.
79
+ */
80
+ gql?: {
81
+ /**
82
+ * Determines if the query should be sent via GET or POST.
83
+ * - `GET`: The query will be sent via query parameters.
84
+ * - `POST`: The query will be sent via the body.
85
+ * @default 'POST'
86
+ */
87
+ transferVia?: GqlTransferOption;
88
+ };
89
+
90
+ /**
91
+ * Configuration options for all query strings.
92
+ */
93
+ queryParams?: BuildQueryStringConfig;
94
+ };
95
+
96
+ /**
97
+ * Parent query client.
98
+ * Used for sharing the same auth provider.
99
+ */
100
+ parent?: QueryClient;
101
+ }
@@ -0,0 +1,61 @@
1
+ import { BaseArguments } from '../query/query.types';
2
+ import { buildQueryCacheKey, shouldCacheQuery } from './query-client.utils';
3
+
4
+ describe('buildQueryCacheKey', () => {
5
+ it('should return a string with a shortened query and variables', () => {
6
+ const route = '/posts';
7
+ const args: BaseArguments = {
8
+ variables: {
9
+ id: 123,
10
+ },
11
+ };
12
+
13
+ const expectedCacheKey = '1769813287';
14
+
15
+ const cacheKey = buildQueryCacheKey(route, args);
16
+
17
+ expect(cacheKey).toBe(expectedCacheKey);
18
+ });
19
+ });
20
+
21
+ describe('shouldCacheQuery', () => {
22
+ it('should return true for GET requests', () => {
23
+ const method = 'GET';
24
+
25
+ const shouldCache = shouldCacheQuery(method);
26
+
27
+ expect(shouldCache).toBe(true);
28
+ });
29
+
30
+ it('should return true for OPTIONS requests', () => {
31
+ const method = 'OPTIONS';
32
+
33
+ const shouldCache = shouldCacheQuery(method);
34
+
35
+ expect(shouldCache).toBe(true);
36
+ });
37
+
38
+ it('should return true for HEAD requests', () => {
39
+ const method = 'HEAD';
40
+
41
+ const shouldCache = shouldCacheQuery(method);
42
+
43
+ expect(shouldCache).toBe(true);
44
+ });
45
+
46
+ it('should return true for GQL_QUERY requests', () => {
47
+ const method = 'GQL_QUERY';
48
+
49
+ const shouldCache = shouldCacheQuery(method);
50
+
51
+ expect(shouldCache).toBe(true);
52
+ });
53
+
54
+ it('should return false for other HTTP methods', () => {
55
+ const method = 'POST';
56
+
57
+ const shouldCache = shouldCacheQuery(method);
58
+
59
+ expect(shouldCache).toBe(false);
60
+ });
61
+ });
@@ -0,0 +1,28 @@
1
+ import { BaseArguments } from '../query/query.types';
2
+ import { Method } from '../request';
3
+
4
+ export const shouldCacheQuery = (method: Method) => {
5
+ return method === 'GET' || method === 'OPTIONS' || method === 'HEAD' || method === 'GQL_QUERY';
6
+ };
7
+
8
+ export const buildQueryCacheKey = (route: string, args: BaseArguments | undefined) => {
9
+ const variables = JSON.stringify(args?.variables || {})
10
+ // replace all curly braces with empty string
11
+ .replace(/{|}/g, '')
12
+ // replace new lines and whitespaces with empty string
13
+ .replace(/\s/g, '');
14
+
15
+ const seed = `${route}...${variables}`;
16
+
17
+ let hash = 0;
18
+
19
+ for (const char of seed) {
20
+ hash = (Math.imul(31, hash) + char.charCodeAt(0)) << 0;
21
+ }
22
+
23
+ // Force positive number hash.
24
+ // 2147483647 = equivalent of Integer.MAX_VALUE.
25
+ hash += 2147483647 + 1;
26
+
27
+ return hash.toString();
28
+ };
@@ -0,0 +1,103 @@
1
+ import { assertInInjectionContext, signal } from '@angular/core';
2
+ import { toObservable } from '@angular/core/rxjs-interop';
3
+ import { BehaviorSubject } from 'rxjs';
4
+ import { EntityStore } from '../entity';
5
+ import { Query, computeQueryQueryParams } from '../query';
6
+ import { QueryClient, buildQueryCacheKey, shouldCacheQuery } from '../query-client';
7
+ import { QueryStore } from '../query-store';
8
+ import {
9
+ AnyRoute,
10
+ BaseArguments,
11
+ GqlQueryConfig,
12
+ RestQueryConfig,
13
+ RouteType,
14
+ WithConfig,
15
+ WithHeaders,
16
+ WithMock,
17
+ } from '../query/query.types';
18
+ import { buildRoute } from '../request';
19
+ import { QueryContainerConfig, addQueryContainerHandling } from '../utils';
20
+ import { QueryPrepareFn } from './query-creator.types';
21
+
22
+ export class QueryCreator<
23
+ Arguments extends BaseArguments | undefined,
24
+ Response,
25
+ Route extends RouteType<Arguments>,
26
+ Store extends EntityStore<unknown>,
27
+ Data,
28
+ Id,
29
+ > {
30
+ constructor(
31
+ private _queryConfig:
32
+ | RestQueryConfig<Route, Response, Arguments, Store, Data, Id>
33
+ | GqlQueryConfig<Route, Response, Arguments, Store, Data, Id>,
34
+ private _client: QueryClient,
35
+ private _store: QueryStore,
36
+ ) {}
37
+
38
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
39
+ // @ts-ignore
40
+ prepare: QueryPrepareFn<Arguments, Response, Route, Store, Data, Id> = (
41
+ args?: Arguments & WithHeaders & WithConfig & WithMock<Response>,
42
+ ) => {
43
+ const route = buildRoute({
44
+ base: this._client.config.baseRoute,
45
+ route: this._queryConfig.route as AnyRoute,
46
+ pathParams: args?.pathParams,
47
+ queryParams: computeQueryQueryParams({ config: this._queryConfig, client: this._client, args }),
48
+ queryParamConfig: this._client.config.request?.queryParams,
49
+ }) as Route;
50
+
51
+ const cacheKey = (args?.config?.queryStoreCacheKey ?? '') + buildQueryCacheKey(route as string, args);
52
+
53
+ if (shouldCacheQuery(this._queryConfig.method) && !args?.config?.skipQueryStore) {
54
+ const existingQuery = this._store.get<Query<Response, Arguments, Route, Store, Data, Id>>(cacheKey);
55
+
56
+ if (existingQuery) {
57
+ return existingQuery;
58
+ }
59
+ }
60
+
61
+ const query = new Query<Response, Arguments, Route, Store, Data, Id>(
62
+ this._client,
63
+ this._queryConfig,
64
+ route,
65
+ args ?? ({} as Arguments),
66
+ cacheKey,
67
+ );
68
+
69
+ if (shouldCacheQuery(this._queryConfig.method) && !args?.config?.skipQueryStore) {
70
+ this._store.add(cacheKey, query);
71
+ }
72
+
73
+ this._store._dispatchQueryCreated(query);
74
+
75
+ return query;
76
+ };
77
+
78
+ createSubject = (initialValue?: ReturnType<typeof this.prepare> | null, config?: QueryContainerConfig) => {
79
+ assertInInjectionContext(this.createSubject);
80
+
81
+ const subject = new BehaviorSubject<ReturnType<typeof this.prepare> | null>(initialValue ?? null);
82
+
83
+ addQueryContainerHandling(subject, () => subject.getValue(), config);
84
+
85
+ return subject;
86
+ };
87
+
88
+ createSignal = (initialValue?: ReturnType<typeof this.prepare> | null, config?: QueryContainerConfig) => {
89
+ assertInInjectionContext(this.createSignal);
90
+
91
+ const _signal = signal<ReturnType<typeof this.prepare> | null>(initialValue ?? null);
92
+
93
+ addQueryContainerHandling(toObservable(_signal), () => _signal(), config);
94
+
95
+ return _signal;
96
+ };
97
+
98
+ /**
99
+ * @deprecated Use `myQuery.createSubject()` or `myQuery.createSignal()` instead. Will be removed in v6.
100
+ */
101
+ behaviorSubject = (initialValue?: ReturnType<typeof this.prepare> | null) =>
102
+ new BehaviorSubject<ReturnType<typeof this.prepare> | null>(initialValue ?? null);
103
+ }
@@ -0,0 +1,74 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { EntityStore } from '../entity';
3
+ import { Query } from '../query/query';
4
+ import {
5
+ AnyQuery,
6
+ BaseArguments,
7
+ EmptyObject,
8
+ RouteType,
9
+ WithConfig,
10
+ WithHeaders,
11
+ WithMock,
12
+ } from '../query/query.types';
13
+ import { QueryCreator } from './query-creator';
14
+
15
+ export type QueryPrepareFn<
16
+ Arguments extends BaseArguments | undefined,
17
+ Response,
18
+ Route extends RouteType<Arguments>,
19
+ Store extends EntityStore<unknown>,
20
+ Data,
21
+ Id,
22
+ > = Arguments extends BaseArguments
23
+ ? (
24
+ args: Arguments & WithHeaders & WithConfig & WithMock<Response>,
25
+ ) => Query<Response, Arguments, Route, Store, Data, Id>
26
+ : (
27
+ args?: (Arguments extends EmptyObject ? Arguments : EmptyObject) & WithHeaders & WithConfig & WithMock<Response>,
28
+ ) => Query<Response, Arguments, Route, Store, Data, Id>;
29
+
30
+ export type AnyQueryCreator = QueryCreator<any, any, any, any, any, any>;
31
+
32
+ export type QueryArgsOf<T extends AnyQueryCreator | AnyQuery | null> =
33
+ T extends QueryCreator<infer Args, any, any, any, any, any>
34
+ ? Args
35
+ : T extends Query<any, infer Args, any, any, any, any>
36
+ ? Args
37
+ : never;
38
+
39
+ export type QueryResponseOf<T extends AnyQueryCreator | AnyQuery | null> =
40
+ T extends QueryCreator<any, infer Response, any, any, any, any>
41
+ ? Response
42
+ : T extends Query<infer Response, any, any, any, any, any>
43
+ ? Response
44
+ : never;
45
+
46
+ export type QueryRouteOf<T extends AnyQueryCreator | AnyQuery | null> =
47
+ T extends QueryCreator<any, any, infer Route, any, any, any>
48
+ ? Route
49
+ : T extends Query<any, any, infer Route, any, any, any>
50
+ ? Route
51
+ : never;
52
+
53
+ export type QueryStoreOf<T extends AnyQueryCreator | AnyQuery | null> =
54
+ T extends QueryCreator<any, any, any, infer Store, any, any>
55
+ ? Store
56
+ : T extends Query<any, any, any, infer Store, any, any>
57
+ ? Store
58
+ : never;
59
+
60
+ export type QueryDataOf<T extends AnyQueryCreator | AnyQuery | null> =
61
+ T extends QueryCreator<any, any, any, any, infer Data, any>
62
+ ? Data
63
+ : T extends Query<any, any, any, any, infer Data, any>
64
+ ? Data
65
+ : never;
66
+
67
+ export type QueryStoreIdOf<T extends AnyQueryCreator | AnyQuery | null> =
68
+ T extends QueryCreator<any, any, any, any, any, infer StoreId>
69
+ ? StoreId
70
+ : T extends Query<any, any, any, any, any, infer StoreId>
71
+ ? StoreId
72
+ : never;
73
+
74
+ export type ConstructQuery<T extends AnyQueryCreator> = ReturnType<T['prepare']>;