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

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @ethlete/query
2
2
 
3
+ ## 6.0.0-next.54
4
+
5
+ ### Patch Changes
6
+
7
+ - Stop a legacy `InfinityQuery` from aborting a non-cacheable page (such as a POST) that is still loading when it is reset or destroyed; the page now finishes and is destroyed once it settles.
8
+ - A legacy query container or collection destroyed while its mutation is in flight now lets the request finish instead of cancelling it.
9
+ - An interop mutation whose prepare injector is destroyed while its request is in flight now finishes before it is destroyed, as it did in v2.
10
+ - Destroying the owner of a legacy mutation that waits to settle after its container let go no longer throws NG0205.
11
+ - A mutation whose `withArgs` source returns `null` while its request is in flight is no longer aborted; it finishes and the query parks once it has settled.
12
+
3
13
  ## 6.0.0-next.53
4
14
 
5
15
  ### Major Changes
@@ -1186,7 +1186,7 @@ const setupQueryDependencies = (options) => {
1186
1186
  const clientToken = options.client.token;
1187
1187
  const dependencies = {
1188
1188
  destroyRef: undefined, // Will be set after injector creation
1189
- scopeDestroyRef: hostInjector.get(DestroyRef),
1189
+ scopeDestroyRef: options.queryConfig?.scopeDestroyRef ?? hostInjector.get(DestroyRef),
1190
1190
  client: hostInjector.get(clientToken),
1191
1191
  injector: undefined, // Will be set after injector creation
1192
1192
  ngErrorHandler: hostInjector.get(ErrorHandler),
@@ -1457,7 +1457,8 @@ const createQueryFeature = (config) => {
1457
1457
  * The arguments are read within a computed function, so you can use reactive values.
1458
1458
  *
1459
1459
  * Return `null` to park the query: it is reset (`response()` and `executionState()` become `null`),
1460
- * and polling and auto refresh pause until args are set again.
1460
+ * and polling and auto refresh pause until args are set again. A mutation that is still in flight
1461
+ * finishes first and the query parks once it has settled.
1461
1462
  *
1462
1463
  * Changing arguments will automatically trigger a new execution of the query if it is eligible for auto execution (e.g. a GET request).
1463
1464
  */
@@ -1474,6 +1475,8 @@ const withArgs = (args) => {
1474
1475
  nestedEffect(() => {
1475
1476
  const currArgsNow = context.state.args();
1476
1477
  if (currArgsNow === null) {
1478
+ if (!context.flags.shouldAutoExecuteMethod && context.state.loading() !== null)
1479
+ return;
1477
1480
  untracked(() => {
1478
1481
  if (context.state.subtle.request() !== null || context.state.loading() !== null)
1479
1482
  context.execute.reset();
@@ -2495,7 +2498,10 @@ const trackRefreshedExecutions = (options) => {
2495
2498
  };
2496
2499
  const createQueryObject = (options) => {
2497
2500
  const { state, execute, deps } = options;
2498
- const destroy = () => deps.injector.destroy();
2501
+ const destroy = () => {
2502
+ if (!deps.injector.destroyed)
2503
+ deps.injector.destroy();
2504
+ };
2499
2505
  const setResponse = (response) => state.response.set(response);
2500
2506
  const setLoading = (loading) => state.loading.set(loading);
2501
2507
  const setError = (error) => state.error.set(error);
@@ -10976,7 +10982,7 @@ const withQueryDevtoolsOverridePersistence = (id, recorder) => {
10976
10982
 
10977
10983
  // Generated from package.json by tools/scripts/generate-version.js - do not edit.
10978
10984
  /** The version of `@ethlete/query` this build was cut from. */
10979
- const QUERY_VERSION = '6.0.0-next.53';
10985
+ const QUERY_VERSION = '6.0.0-next.54';
10980
10986
 
10981
10987
  /** How deep a schema is expanded before a nested branch is given up on. */
10982
10988
  const MAX_DEPTH = 12;
@@ -13059,6 +13065,15 @@ const request = (config) => {
13059
13065
  });
13060
13066
  };
13061
13067
 
13068
+ /**
13069
+ * Destroying a legacy query tears down the underlying query's injector, which cancels its request - so
13070
+ * a superseded or released query that was not aborted (a mutation the server may already have accepted)
13071
+ * is torn down only once it has settled. `state$` completes with the query, so this ends either way.
13072
+ */
13073
+ const destroyOnceSettled = (query) => {
13074
+ query.state$.pipe(takeUntilResponse()).subscribe({ complete: () => query.destroy() });
13075
+ };
13076
+
13062
13077
  let nextQueryContainerOwnerId = 0;
13063
13078
  const queryContainerOwnerIds = /* @__PURE__ */ new WeakMap();
13064
13079
  const queryContainerOwnerId = (injector) => {
@@ -13070,14 +13085,6 @@ const queryContainerOwnerId = (injector) => {
13070
13085
  queryContainerOwnerIds.set(injector, id);
13071
13086
  return id;
13072
13087
  };
13073
- /**
13074
- * Destroying a legacy query tears down the underlying query's injector, which cancels its request - so
13075
- * a superseded query that was not aborted (a mutation the server may already have accepted) is torn
13076
- * down only once it has settled. `state$` completes with the query, so this ends either way.
13077
- */
13078
- const destroyOnceSettled = (query) => {
13079
- query.state$.pipe(takeUntilResponse()).subscribe({ complete: () => query.destroy() });
13080
- };
13081
13088
  /**
13082
13089
  * @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
13083
13090
  */
@@ -13153,7 +13160,14 @@ const addQueryContainerHandling = (obs, valueFn, config) => {
13153
13160
  if ((q?.canBeCached && abortOnDestroy === undefined) || abortOnDestroy) {
13154
13161
  q?.abort();
13155
13162
  }
13156
- q?.destroy?.();
13163
+ const legacyQuery = q;
13164
+ if (!legacyQuery?.destroy)
13165
+ return;
13166
+ if (isQueryStateLoading(q?.rawState)) {
13167
+ destroyOnceSettled(legacyQuery);
13168
+ return;
13169
+ }
13170
+ legacyQuery.destroy();
13157
13171
  };
13158
13172
  if (isQuery(query)) {
13159
13173
  handleQuery(query);
@@ -14204,6 +14218,35 @@ const isInjectorUsable = (injector) => {
14204
14218
  return false;
14205
14219
  }
14206
14220
  };
14221
+ /**
14222
+ * The scope ends when its owner dies with no request in flight, when that request settles, or when the
14223
+ * owner's nearest environment injector dies - a destroyed injector does not destroy the environment
14224
+ * injectors made with `createEnvironmentInjector` below it.
14225
+ */
14226
+ const createSettleScope = (owner) => {
14227
+ const anchor = owner.get(EnvironmentInjector);
14228
+ const scope = createEnvironmentInjector([], anchor);
14229
+ const end = () => {
14230
+ if (!scope.destroyed)
14231
+ scope.destroy();
14232
+ };
14233
+ const bind = (query, queryInjector) => {
14234
+ const stopOnAnchorDestroy = anchor.get(DestroyRef).onDestroy(end);
14235
+ const stopOnOwnerDestroy = owner.get(DestroyRef).onDestroy(() => {
14236
+ if (isQueryStateLoading(untracked(() => query.rawState))) {
14237
+ query.state$.pipe(takeUntilResponse()).subscribe({ complete: end });
14238
+ return;
14239
+ }
14240
+ end();
14241
+ });
14242
+ queryInjector.get(DestroyRef).onDestroy(() => {
14243
+ stopOnAnchorDestroy();
14244
+ stopOnOwnerDestroy();
14245
+ end();
14246
+ });
14247
+ };
14248
+ return { destroyRef: scope.get(DestroyRef), bind };
14249
+ };
14207
14250
  /**
14208
14251
  * Angular's NG0203, thrown by `inject()` outside an injection context. Matched on the code rather than
14209
14252
  * the message: production builds strip the message, and NG0205 ("Injector has already been destroyed")
@@ -14278,14 +14321,17 @@ class LegacyQueryCreator {
14278
14321
  }
14279
14322
  return runInInjectionContext(injector, () => {
14280
14323
  return untracked(() => {
14324
+ const settleScope = this.canBeCached ? null : createSettleScope(injector);
14281
14325
  const newQuery = this.options.creator({
14282
14326
  onlyManualExecution: true,
14283
14327
  injector,
14328
+ ...(settleScope ? { scopeDestroyRef: settleScope.destroyRef } : {}),
14284
14329
  silenceMissingWithArgsFeatureError: true,
14285
14330
  // `LegacyQuery.execute()` forwards v2's `skipCache` as `allowCache` for every method, cacheable or not.
14286
14331
  silenceUncacheableAllowCacheError: true,
14287
14332
  });
14288
14333
  const legacyQuery = new LegacyQuery(newQuery, queryArgs, this.options.entity, false, this.canBeCached);
14334
+ settleScope?.bind(legacyQuery, newQuery.subtle.injector);
14289
14335
  if (args?.config?.destroyOnResponse) {
14290
14336
  // Owned by the query's own injector, not the caller's. Call sites are told to pass an injector
14291
14337
  // that outlives them, so they hand over a root or environment one - and an effect that only
@@ -15490,9 +15536,16 @@ class InfinityQuery {
15490
15536
  destroyQueries() {
15491
15537
  for (const query of this.queries$.value) {
15492
15538
  query.stopPolling();
15493
- query.abort();
15494
- if ('destroy' in query && typeof query.destroy === 'function')
15539
+ if (query.canBeCached)
15540
+ query.abort();
15541
+ if (!('destroy' in query) || typeof query.destroy !== 'function')
15542
+ continue;
15543
+ if (!query.canBeCached && isQueryStateLoading(query.rawState)) {
15544
+ destroyOnceSettled(query);
15545
+ }
15546
+ else {
15495
15547
  query.destroy();
15548
+ }
15496
15549
  }
15497
15550
  }
15498
15551
  prepareArgs(config, page) {