@ethlete/query 6.0.0-next.51 → 6.0.0-next.53

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 (37) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/fesm2022/ethlete-query-ngrx-toolkit.mjs +393 -0
  3. package/fesm2022/ethlete-query-ngrx-toolkit.mjs.map +1 -0
  4. package/fesm2022/ethlete-query-testing.mjs +67 -25
  5. package/fesm2022/ethlete-query-testing.mjs.map +1 -1
  6. package/fesm2022/ethlete-query.mjs +7895 -7226
  7. package/fesm2022/ethlete-query.mjs.map +1 -1
  8. package/generators/generators.json +5 -0
  9. package/generators/migrate-from-ngrx-toolkit/args-assignability.js +140 -0
  10. package/generators/migrate-from-ngrx-toolkit/consumers.js +521 -0
  11. package/generators/migrate-from-ngrx-toolkit/creators.js +301 -0
  12. package/generators/migrate-from-ngrx-toolkit/facade.js +295 -0
  13. package/generators/migrate-from-ngrx-toolkit/feature.js +513 -0
  14. package/generators/migrate-from-ngrx-toolkit/migration.js +140 -0
  15. package/generators/migrate-from-ngrx-toolkit/report.js +154 -0
  16. package/generators/migrate-from-ngrx-toolkit/schema.json +42 -0
  17. package/generators/migrate-from-ngrx-toolkit/store-removal.js +633 -0
  18. package/generators/migrate-from-ngrx-toolkit/ts-edits.js +128 -0
  19. package/generators/migrate-query-opt-in-features/migration.js +10 -9
  20. package/generators/migrate-to-query-v3/auth-provider-scaffold.js +29 -2
  21. package/generators/migrate-to-query-v3/cleanup-migration.js +163 -53
  22. package/generators/migrate-to-query-v3/legacy-query-creator-migration.js +39 -2
  23. package/generators/migrate-to-query-v3/migration-scope.js +2 -0
  24. package/generators/migrate-to-query-v3/migration.js +5 -4
  25. package/generators/migrate-to-query-v3/module-graph.js +41 -29
  26. package/generators/migrate-to-query-v3/query-client-migration.js +209 -126
  27. package/generators/migrate-to-query-v3/rename-symbols.js +38 -11
  28. package/generators/migrate-to-query-v3/report.js +40 -7
  29. package/generators/migrate-to-query-v3/shared.js +46 -1
  30. package/generators/prep-for-query-v3/migration.js +400 -57
  31. package/migrations/query-field-default-types.md +43 -0
  32. package/migrations/search-query-field-string.md +43 -0
  33. package/migrations.json +16 -0
  34. package/package.json +5 -1
  35. package/types/ethlete-query-ngrx-toolkit.d.ts +143 -0
  36. package/types/ethlete-query-testing.d.ts +28 -11
  37. package/types/ethlete-query.d.ts +789 -584
@@ -0,0 +1,143 @@
1
+ import * as i0 from '@angular/core';
2
+ import { PipeTransform, Injector } from '@angular/core';
3
+ import { Observable, Subject } from 'rxjs';
4
+ import { AnyQueryCreator, QueryArgsOf, QueryHeaders, ResponseType } from '@ethlete/query';
5
+
6
+ /** The call state of a toolkit handle, as `@tomtomb/ngrx-toolkit` names it. */
7
+ declare const CallState: {
8
+ readonly INIT: "INIT";
9
+ readonly LOADING: "LOADING";
10
+ readonly SUCCESS: "SUCCESS";
11
+ readonly ERROR: "ERROR";
12
+ };
13
+ type CallState = (typeof CallState)[keyof typeof CallState];
14
+ /** The error shape of `@tomtomb/ngrx-toolkit`: the HTTP status, the error message and the response body. */
15
+ type ToolkitError<T = unknown> = {
16
+ status: number | string;
17
+ message: string;
18
+ data: T | null;
19
+ };
20
+ type ToolkitActionOptions = {
21
+ headers?: QueryHeaders;
22
+ extras?: {
23
+ skipCache?: boolean;
24
+ };
25
+ };
26
+ /**
27
+ * The toolkit-shaped args of a query creator: `queryParams` holds the path params, `params` the query string and
28
+ * `body` the body, the way `@tomtomb/ngrx-toolkit` action groups named them.
29
+ */
30
+ type ActionCallArgs<TCreator extends AnyQueryCreator> = {
31
+ [K in keyof QueryArgsOf<TCreator> as K extends 'pathParams' ? 'queryParams' : K extends 'queryParams' ? 'params' : K extends 'body' ? 'body' : never]: QueryArgsOf<TCreator>[K];
32
+ } & {
33
+ actionOptions?: ToolkitActionOptions;
34
+ };
35
+ type ToolkitPollingOptions = {
36
+ intervalDuration: number;
37
+ killSwitch: Subject<boolean>;
38
+ };
39
+ /** The handle `toolkitCall` returns, shaped like the `MappedEntityState` of `@tomtomb/ngrx-toolkit`. */
40
+ type MappedEntityState<TCreator extends AnyQueryCreator> = {
41
+ response$: Observable<ResponseType<QueryArgsOf<TCreator>> | null>;
42
+ cachedResponse$: Observable<ResponseType<QueryArgsOf<TCreator>>>;
43
+ error$: Observable<ToolkitError | null>;
44
+ args$: Observable<ActionCallArgs<TCreator> | null>;
45
+ isInit$: Observable<boolean | null>;
46
+ isLoading$: Observable<boolean | null>;
47
+ isSuccess$: Observable<boolean | null>;
48
+ isError$: Observable<boolean | null>;
49
+ timestamp$: Observable<number | null>;
50
+ callState$: Observable<CallState | null>;
51
+ refresh: () => void;
52
+ remove: () => void;
53
+ startPolling: (options: ToolkitPollingOptions) => void;
54
+ stopPolling: () => void;
55
+ };
56
+ type AnyMappedEntityState = MappedEntityState<any>;
57
+
58
+ type ExtractFrom<T> = T extends Observable<infer U> ? U : never;
59
+ /** What `suspense` and `suspenseMulti` render for a handle: every stream's current value, plus the handle's methods. */
60
+ type ToolkitSuspenseState<T extends AnyMappedEntityState | null | undefined> = T extends AnyMappedEntityState ? {
61
+ args: ExtractFrom<T['args$']>;
62
+ cachedResponse: ExtractFrom<T['cachedResponse$']> | null;
63
+ callState: ExtractFrom<T['callState$']>;
64
+ error: ExtractFrom<T['error$']>;
65
+ isError: ExtractFrom<T['isError$']>;
66
+ isInit: ExtractFrom<T['isInit$']>;
67
+ isLoading: ExtractFrom<T['isLoading$']>;
68
+ isSuccess: ExtractFrom<T['isSuccess$']>;
69
+ response: ExtractFrom<T['response$']>;
70
+ timestamp: ExtractFrom<T['timestamp$']>;
71
+ refresh: () => void;
72
+ remove: () => void;
73
+ startPolling: (options: ToolkitPollingOptions) => void;
74
+ stopPolling: () => void;
75
+ } : T;
76
+ /** Subscribes to a toolkit handle and renders its current state, like the `suspense` pipe of `@tomtomb/ngrx-toolkit`. */
77
+ declare class SuspensePipe implements PipeTransform {
78
+ transform: <T extends AnyMappedEntityState>(value: T | null | undefined) => ToolkitSuspenseState<T>;
79
+ static ɵfac: i0.ɵɵFactoryDeclaration<SuspensePipe, never>;
80
+ static ɵpipe: i0.ɵɵPipeDeclaration<SuspensePipe, "suspense", true>;
81
+ }
82
+
83
+ type AnyHandleRecord = Record<string, AnyMappedEntityState | null | undefined>;
84
+ type ToolkitSuspenseMultiState<T extends AnyHandleRecord> = {
85
+ [K in keyof T]: ToolkitSuspenseState<T[K]>;
86
+ };
87
+ /**
88
+ * Subscribes to a record of toolkit handles and renders each one's current state, like the `suspenseMulti` pipe of
89
+ * `@tomtomb/ngrx-toolkit`. Nothing renders until every non-null handle has emitted.
90
+ */
91
+ declare class SuspenseMultiPipe implements PipeTransform {
92
+ transform: <T extends AnyHandleRecord>(value: T | null | undefined) => ToolkitSuspenseMultiState<T>;
93
+ static ɵfac: i0.ɵɵFactoryDeclaration<SuspenseMultiPipe, never>;
94
+ static ɵpipe: i0.ɵɵPipeDeclaration<SuspenseMultiPipe, "suspenseMulti", true>;
95
+ }
96
+
97
+ /** Exports the `suspense` and `suspenseMulti` pipes, like the `NgRxToolkitModule` of `@tomtomb/ngrx-toolkit`. */
98
+ declare class NgRxToolkitModule {
99
+ static ɵfac: i0.ɵɵFactoryDeclaration<NgRxToolkitModule, never>;
100
+ static ɵmod: i0.ɵɵNgModuleDeclaration<NgRxToolkitModule, never, [typeof SuspensePipe, typeof SuspenseMultiPipe], [typeof SuspensePipe, typeof SuspenseMultiPipe]>;
101
+ static ɵinj: i0.ɵɵInjectorDeclaration<NgRxToolkitModule>;
102
+ }
103
+
104
+ type ToolkitCallOptions = {
105
+ /**
106
+ * The injector the handle's query lives in. Required outside an injection context, which is where a
107
+ * `providedIn: 'root'` facade calls from; pass the facade's `inject(Injector)`.
108
+ */
109
+ injector?: Injector;
110
+ };
111
+ /**
112
+ * Returns the handle for a creator and args without executing it - the `FacadeBase.select(group, actionId)` of
113
+ * `@tomtomb/ngrx-toolkit`. Args that build the same request (in any key order, other keys ignored) give the same
114
+ * handle `toolkitCall` returns, so `toolkitSelect(getTeam, args, { injector }).refresh()` re-runs a request another
115
+ * component started.
116
+ */
117
+ declare const toolkitSelect: <TCreator extends AnyQueryCreator>(creator: TCreator, args: ActionCallArgs<TCreator>, options?: ToolkitCallOptions) => MappedEntityState<TCreator>;
118
+ /**
119
+ * Executes a query creator with toolkit-shaped args and returns its handle - the `FacadeBase.call(group, args)` of
120
+ * `@tomtomb/ngrx-toolkit`. Every call sends the request again; equal args return the same handle, which lives as
121
+ * long as the injector.
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * @Injectable({ providedIn: 'root' })
126
+ * export class TeamFacade {
127
+ * private injector = inject(Injector);
128
+ *
129
+ * getTeam(args: ActionCallArgs<typeof getTeam>) {
130
+ * return toolkitCall(getTeam, args, { injector: this.injector });
131
+ * }
132
+ * }
133
+ * ```
134
+ */
135
+ declare const toolkitCall: <TCreator extends AnyQueryCreator>(creator: TCreator, args: ActionCallArgs<TCreator>, options?: ToolkitCallOptions) => MappedEntityState<TCreator>;
136
+
137
+ /** Emits `true` while any of the given loading streams is `true`. Nullish entries are skipped. */
138
+ declare const joinLoading: (observables: (Observable<boolean | null> | null | undefined)[]) => Observable<boolean>;
139
+ /** Emits every non-null error of the given error streams, or `null` when there is none. Nullish entries are skipped. */
140
+ declare const joinErrors: (observables: (Observable<ToolkitError | null> | null | undefined)[]) => Observable<ToolkitError[] | null>;
141
+
142
+ export { CallState, NgRxToolkitModule, SuspenseMultiPipe, SuspensePipe, joinErrors, joinLoading, toolkitCall, toolkitSelect };
143
+ export type { ActionCallArgs, AnyMappedEntityState, MappedEntityState, ToolkitActionOptions, ToolkitCallOptions, ToolkitError, ToolkitPollingOptions, ToolkitSuspenseMultiState, ToolkitSuspenseState };
@@ -124,7 +124,7 @@ type AuthTestSetup<TLoginArgs extends {
124
124
  ], TFeatures, TBearerData>;
125
125
  /** Helper to login a user and flush the HTTP request */
126
126
  login: (credentials: TLoginArgs['body'], response: TLoginArgs['response']) => void;
127
- /** Helper to trigger a refresh and flush the HTTP request */
127
+ /** Helper to trigger a refresh and flush the HTTP request. Sends `buildRefreshArgs(token)` when configured, else `{ body: { token } }` */
128
128
  refresh: (token: string, response: TRefreshArgs['response']) => void;
129
129
  /** Helper to make a secure request that requires authentication */
130
130
  makeSecureRequest: (route: string) => void;
@@ -184,9 +184,9 @@ type FakeBroadcastChannelHandle = {
184
184
  * Replaces `globalThis.BroadcastChannel` with an in-memory bus.
185
185
  *
186
186
  * Messages are structured-cloned at post time (so a non-cloneable payload throws exactly like the
187
- * real API does), delivered on a microtask (never synchronously), and never delivered back to the
188
- * channel that posted them - all three matching browser behavior. `await Promise.resolve()` in a
189
- * spec is enough to flush delivery.
187
+ * real API does), delivered as a task (never synchronously, and after every pending microtask), and
188
+ * never delivered back to the channel that posted them - all three matching browser behavior. Delivery
189
+ * does not depend on fake timers; `await flushMultiTabSync()` in a spec flushes it.
190
190
  */
191
191
  declare const installFakeBroadcastChannel: () => FakeBroadcastChannelHandle;
192
192
  type FakeWebLocksHandle = {
@@ -211,9 +211,9 @@ type FakeWebLocksHandle = {
211
211
  */
212
212
  declare const installFakeWebLocks: () => FakeWebLocksHandle;
213
213
  /**
214
- * Flushes the microtask queue, which is where both fakes schedule their work - message delivery and
215
- * lock grants. Awaiting it once is enough for a single hop; a grant that has to wait for another
216
- * tab's release needs one await per hop.
214
+ * Waits for the fakes' pending work - message delivery (a task) and lock grants (a microtask) - even
215
+ * under fake timers. Awaiting it once is enough for a single hop; a grant that has to wait for another
216
+ * tab's release, or a reply to a delivered message, needs one await per hop.
217
217
  */
218
218
  declare const flushMultiTabSync: () => Promise<void>;
219
219
 
@@ -247,7 +247,7 @@ type FakeQueryPersistenceStoreHandle = {
247
247
  /**
248
248
  * Holds every subsequent `read` pending until {@link flushReads} is called, for testing the window
249
249
  * between a cold mount and its hydration (a slow disk, or a mount that happened before startup
250
- * finished).
250
+ * finished). Each held read returns what the store held when it started, as `loadIndex` does.
251
251
  */
252
252
  deferReads: () => void;
253
253
  /** Settles the reads held by {@link deferReads} and stops deferring. */
@@ -295,10 +295,16 @@ type WebSocketTestDouble = {
295
295
  event: string;
296
296
  data: unknown;
297
297
  }[];
298
- /** Whether the client asked the socket to connect, and whether it has since disconnected it. */
298
+ /**
299
+ * Whether the client asked the socket to connect, and whether it has since disconnected it. `active` is
300
+ * socket.io's own flag - `false` after a server disconnect or a rejected handshake, until the next
301
+ * `connect()`; `connectCalls` counts every `connect()`.
302
+ */
299
303
  state: () => {
300
304
  connectRequested: boolean;
301
305
  disconnected: boolean;
306
+ active: boolean;
307
+ connectCalls: number;
302
308
  };
303
309
  /**
304
310
  * Complete the handshake: flush every buffered emit, then turn the client's `isConnected` true. Pass
@@ -307,8 +313,19 @@ type WebSocketTestDouble = {
307
313
  serverConnect: (options?: {
308
314
  recovered?: boolean;
309
315
  }) => void;
310
- /** Drop the connection, so `isConnected` turns false. */
311
- serverDisconnect: () => void;
316
+ /**
317
+ * Drop the connection, so `isConnected` turns false. The default reason, `'transport close'`, is one
318
+ * socket.io recovers from itself; `'io server disconnect'` is the server kicking the socket, after which
319
+ * socket.io only reconnects when `connect()` is called again.
320
+ */
321
+ serverDisconnect: (options?: {
322
+ reason?: 'transport close' | 'io server disconnect';
323
+ }) => void;
324
+ /**
325
+ * Reject the handshake the way a server middleware does: the `auth` payload is recorded in
326
+ * {@link handshakes}, `connect_error` fires and the socket stops reconnecting on its own.
327
+ */
328
+ serverRejectHandshake: (message?: string) => void;
312
329
  /**
313
330
  * Let the ping expire: the socket still reports itself connected, but buffers every emit like socket.io
314
331
  * does until it notices and closes. Follow it with {@link serverDisconnect}.