@ethlete/query-devtools 1.0.0-next.4 → 1.0.0-next.6

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,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0-next.6
4
+
5
+ ### Patch Changes
6
+
7
+ - [#3067](https://github.com/ethlete-io/ethdk/pull/3067) [`e271c58`](https://github.com/ethlete-io/ethdk/commit/e271c58ffc395075a5c4496abf34b34568855276) Thanks [@github-actions](https://github.com/apps/github-actions)! - Query devtools: the value explorer shows the headers a `headers` provider hands the request, instead of an `fn(headerProvider)` row.
8
+
9
+ ## 1.0.0-next.5
10
+
11
+ ### Patch Changes
12
+
13
+ - [`4044a00`](https://github.com/ethlete-io/ethdk/commit/4044a0007de55d25b5a697c6e4190e21d68d022f) Thanks [@TomTomB](https://github.com/TomTomB)! - - Notifications: a toast keeps one elevation, paints above overlays, and keeps out of every reserved viewport edge.
14
+ - Query devtools: a side dock reserves the edge it covers.
15
+ - `SurfaceContextTracker` drops `topType` / `topElevation`.
16
+
17
+ - [`3122607`](https://github.com/ethlete-io/ethdk/commit/3122607d1727844a2d987032f45ad631f678c2ca) Thanks [@TomTomB](https://github.com/TomTomB)! - Overlays and floating chrome now keep out of space a surface above the page reserved with `reserveOverlayViewportSpace()` - so a dialog, menu or toast is no longer stacked under the docked query devtools panel.
18
+
3
19
  ## 1.0.0-next.4
4
20
 
5
21
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { signal, ViewEncapsulation, Component, input, output, computed, viewChild, effect, numberAttribute, linkedSignal, InjectionToken, inject, untracked, ElementRef, NgZone, DestroyRef, booleanAttribute } from '@angular/core';
3
- import { copyToClipboard, injectStyleManager, injectErrorTheme, ProvideColorDirective, clamp, injectRenderer, injectFileDownload, injectViewportSize, injectIsDocumentVisible, injectBreakpointObserver, signalElementDimensions, randomId, createObjectUrlHandle, DragHandleDirective, ResizeHandlesComponent } from '@ethlete/core';
3
+ import { copyToClipboard, injectStyleManager, injectErrorTheme, ProvideColorDirective, clamp, injectRenderer, injectFileDownload, injectViewportSize, injectIsDocumentVisible, injectBreakpointObserver, signalElementDimensions, randomId, reserveOverlayViewportSpace, createObjectUrlHandle, DragHandleDirective, ResizeHandlesComponent } from '@ethlete/core';
4
4
  import { queryDevtoolsAbout, hasQueryDevtoolsOverridesAtPath, detectPaginationShape, isDateShapedLeaf, generateQueryDevtoolsStringPreset, generateQueryDevtoolsNumberPreset, collectLeafPaths, armAllQueryDevtoolsMocks, clearQueryDevtoolsArmedMocks, clearQueryDevtoolsFaults, queryDevtoolsResponseHistory, queryDevtoolsMocks, queryDevtoolsArmedMocks, queryDevtoolsSettings, setQueryDevtoolsOverridesScope, setQueryDevtoolsArmedMocksScope, setQueryDevtoolsFaultsScope, setQueryDevtoolsSettings, QUERY_DEVTOOLS_TOKEN_TTL_LIMIT, setQueryDevtoolsTokenTtl, clearQueryDevtoolsTokenTtl, serializeQueryDevtoolsOverrideTransfer, parseQueryDevtoolsOverrideTransfer, armQueryDevtoolsOverrideTransfer, countUnresolvedQueryDevtoolsOverrides, createQueryDevtoolsOverrides, applyQueryDevtoolsOverrides, queryDevtoolsSchemaNames, queryDevtoolsSchemaRoutes, queryDevtoolsSchemaState, measureQueryDevtoolsPayload, seedQueryDevtoolsSchemaBody, loadQueryDevtoolsSchema, queryDevtoolsMockId, saveQueryDevtoolsMock, armQueryDevtoolsMock, deleteQueryDevtoolsMock, collectQueryDevtoolsSchemaComponents, seedQueryDevtoolsSchemaRoute, QUERY_DEVTOOLS_FAULT_STATUSES, setQueryDevtoolsFault, clearQueryDevtoolsTombstones, readQueryDevtoolsStore, createQueryKeyLockManager, queryDevtoolsEntries, queryDevtoolsTokenTtls, queryDevtoolsFaults, EMPTY_QUERY_DEVTOOLS_FAULT, isQueryDevtoolsFaultArmed, queryDevtoolsFaultsRestored, queryDevtoolsArmedMocksRestored, restoredQueryDevtoolsOverrides, queryDevtoolsRestoredOverridesScope, clearRestoredQueryDevtoolsOverrides, clearQueryDevtoolsStore, writeQueryDevtoolsStore, registerEthleteVersion, queryDevtoolsOverridePersistence, setQueryDevtoolsOverridePersistence, clearQueryDevtoolsOverrideStore, clearQueryDevtoolsMockStore, createQueryErrorResponse, applyQueryDevtoolsTokenTtl, canOverrideQueryDevtoolsTokenTtl, sumQueryDevtoolsStats } from '@ethlete/query';
5
5
  import { tap, Subject, switchMap, timer, interval, animationFrames, NEVER, takeUntil, finalize as finalize$1, EMPTY, startWith, merge, map, fromEvent, filter, take } from 'rxjs';
6
6
  import { takeUntilDestroyed, toSignal, toObservable } from '@angular/core/rxjs-interop';
@@ -290,14 +290,20 @@ const isHeadersValue = (value) => !!value &&
290
290
  'getAll' in value &&
291
291
  typeof value.getAll === 'function' &&
292
292
  'lazyInit' in value;
293
+ const HEADERS_KEY = 'headers';
293
294
  /**
294
295
  * How to render `value` if it is one of the built-ins the explorer knows, or `null` for anything it
295
- * should keep treating as a plain object or array.
296
+ * should keep treating as a plain object or array. `key` is the name the value sits under; a
297
+ * `headers` function is called so the row shows the headers it hands the request.
296
298
  */
297
- const exoticOf = (value) => {
298
- // A header provider is the case in practice; `String(fn)` would dump its whole source into the row.
299
- if (typeof value === 'function')
299
+ const exoticOf = (value, key) => {
300
+ if (typeof value === 'function') {
301
+ const provided = key === HEADERS_KEY && value.length === 0 ? providedHeaders(value) : null;
302
+ if (provided)
303
+ return { typeName: 'HttpHeaders', entries: headerEntries(provided) };
304
+ // `String(fn)` would dump the whole source of the function into the row.
300
305
  return { typeName: 'fn', display: value.name || 'anonymous' };
306
+ }
301
307
  if (!value || typeof value !== 'object')
302
308
  return null;
303
309
  if (value instanceof Date) {
@@ -337,6 +343,20 @@ const exoticOf = (value) => {
337
343
  }
338
344
  return null;
339
345
  };
346
+ /**
347
+ * The headers a `headers` provider hands the request, or `null` if it hands anything else. A secure
348
+ * query resolves its access token inside such a provider and throws while no token is available yet -
349
+ * both cases keep the plain `fn` row.
350
+ */
351
+ const providedHeaders = (provider) => {
352
+ try {
353
+ const value = provider();
354
+ return isHeadersValue(value) ? value : null;
355
+ }
356
+ catch {
357
+ return null;
358
+ }
359
+ };
340
360
  /**
341
361
  * The headers an `HttpHeaders` actually holds, as `name: value` pairs. A header set more than once
342
362
  * joins on `, ` the way the wire format does.
@@ -717,10 +737,10 @@ const kindOf = (value) => {
717
737
  return 'array';
718
738
  return typeof value;
719
739
  };
720
- const entriesOf = (value) => {
740
+ const entriesOf = (value, key) => {
721
741
  if (Array.isArray(value))
722
742
  return value.map((v, i) => ({ k: String(i), v }));
723
- const exotic = exoticOf(value);
743
+ const exotic = exoticOf(value, key);
724
744
  if (exotic)
725
745
  return exotic.entries ?? [];
726
746
  if (value && typeof value === 'object') {
@@ -786,7 +806,7 @@ class QueryDevtoolsJsonComponent {
786
806
  ...(ngDevMode ? [{ debugName: "annotations" }] : /* istanbul ignore next */ []));
787
807
  this.kind = computed(() => kindOf(this.value()), /* @ts-ignore */
788
808
  ...(ngDevMode ? [{ debugName: "kind" }] : /* istanbul ignore next */ []));
789
- this.exotic = computed(() => exoticOf(this.value()), /* @ts-ignore */
809
+ this.exotic = computed(() => exoticOf(this.value(), this.nodeKey()), /* @ts-ignore */
790
810
  ...(ngDevMode ? [{ debugName: "exotic" }] : /* istanbul ignore next */ []));
791
811
  /** One annotation covers every element of an array, so the lookup path forgets which index this is. */
792
812
  this.shapePath = computed(() => this.path()
@@ -798,12 +818,20 @@ class QueryDevtoolsJsonComponent {
798
818
  ...(ngDevMode ? [{ debugName: "annotation" }] : /* istanbul ignore next */ []));
799
819
  this.annotationTitle = computed(() => `Declared in the API description as ${this.annotation()}`, /* @ts-ignore */
800
820
  ...(ngDevMode ? [{ debugName: "annotationTitle" }] : /* istanbul ignore next */ []));
801
- /** A `Date` or a `File` is object-typed but has nothing to expand into, so it renders as a leaf. */
802
- this.isContainer = computed(() => (this.kind() === 'array' || this.kind() === 'object') && !this.exotic()?.display, /* @ts-ignore */
821
+ /**
822
+ * A `Date` is object-typed but has nothing to expand into, so it renders as a leaf. A resolved
823
+ * header provider is the other way round: a function that does expand.
824
+ */
825
+ this.isContainer = computed(() => {
826
+ const exotic = this.exotic();
827
+ if (exotic)
828
+ return !!exotic.entries;
829
+ return this.kind() === 'array' || this.kind() === 'object';
830
+ }, /* @ts-ignore */
803
831
  ...(ngDevMode ? [{ debugName: "isContainer" }] : /* istanbul ignore next */ []));
804
832
  /** The entries this node covers: the container's own, or just the window a chunk stands for. */
805
833
  this.ownEntries = computed(() => {
806
- const entries = entriesOf(this.value());
834
+ const entries = entriesOf(this.value(), this.nodeKey());
807
835
  const chunk = this.chunk();
808
836
  return chunk ? entries.slice(chunk.start, chunk.end) : entries;
809
837
  }, /* @ts-ignore */
@@ -844,7 +872,7 @@ class QueryDevtoolsJsonComponent {
844
872
  /** Only slices that actually contain a match unfold while searching, so a filter stays cheap. */
845
873
  this.chunkHasHit = computed(() => {
846
874
  const term = this.search();
847
- return !!term && this.ownEntries().some(({ k, v }) => k.toLowerCase().includes(term) || matchesDeep(v, term));
875
+ return (!!term && this.ownEntries().some((entry) => entry.k.toLowerCase().includes(term) || matchesDeep(entry, term)));
848
876
  }, /* @ts-ignore */
849
877
  ...(ngDevMode ? [{ debugName: "chunkHasHit" }] : /* istanbul ignore next */ []));
850
878
  this.effectiveExpanded = computed(() => {
@@ -895,7 +923,7 @@ class QueryDevtoolsJsonComponent {
895
923
  return count ? `{…} ${count}` : '{}';
896
924
  }, /* @ts-ignore */
897
925
  ...(ngDevMode ? [{ debugName: "preview" }] : /* istanbul ignore next */ []));
898
- this.display = computed(() => displayOf(this.value()), /* @ts-ignore */
926
+ this.display = computed(() => displayOf(this.value(), this.nodeKey()), /* @ts-ignore */
899
927
  ...(ngDevMode ? [{ debugName: "display" }] : /* istanbul ignore next */ []));
900
928
  this.keyHit = computed(() => {
901
929
  const term = this.search();
@@ -1239,8 +1267,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
1239
1267
  }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], nodeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeKey", required: false }] }], depth: [{ type: i0.Input, args: [{ isSignal: true, alias: "depth", required: false }] }], search: [{ type: i0.Input, args: [{ isSignal: true, alias: "search", required: false }] }], chunk: [{ type: i0.Input, args: [{ isSignal: true, alias: "chunk", required: false }] }], path: [{ type: i0.Input, args: [{ isSignal: true, alias: "path", required: false }] }], expandedPaths: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedPaths", required: false }] }], collapsedPaths: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsedPaths", required: false }] }], toggleFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggleFn", required: false }] }], jsonPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "jsonPath", required: false }] }], parentKind: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentKind", required: false }] }], overrides: [{ type: i0.Input, args: [{ isSignal: true, alias: "overrides", required: false }] }], annotations: [{ type: i0.Input, args: [{ isSignal: true, alias: "annotations", required: false }] }] } });
1240
1268
  // Below the component: an interpolated template literal above an inline template breaks the Angular
1241
1269
  // language service for the whole template (see `ethlete/no-template-literal-before-inline-template`).
1242
- const displayOf = (value) => {
1243
- const exotic = exoticOf(value);
1270
+ const displayOf = (value, key) => {
1271
+ const exotic = exoticOf(value, key);
1244
1272
  if (exotic?.display)
1245
1273
  return `${exotic.typeName}(${exotic.display})`;
1246
1274
  switch (kindOf(value)) {
@@ -1268,11 +1296,13 @@ const buildChunks = (entries, window) => {
1268
1296
  return chunks;
1269
1297
  };
1270
1298
  /** Whether a search term appears anywhere in a subtree - used to keep non-matching slices folded. */
1271
- const matchesDeep = (value, term) => {
1272
- if (!value || typeof value !== 'object' || exoticOf(value)?.display) {
1273
- return displayOf(value).toLowerCase().includes(term);
1299
+ const matchesDeep = (entry, term) => {
1300
+ const { k, v } = entry;
1301
+ const exotic = exoticOf(v, k);
1302
+ if (exotic ? !exotic.entries : !v || typeof v !== 'object') {
1303
+ return displayOf(v, k).toLowerCase().includes(term);
1274
1304
  }
1275
- return entriesOf(value).some(({ k, v }) => k.toLowerCase().includes(term) || matchesDeep(v, term));
1305
+ return entriesOf(v, k).some((child) => child.k.toLowerCase().includes(term) || matchesDeep(child, term));
1276
1306
  };
1277
1307
 
1278
1308
  const QUERY_DEVTOOLS_HOST = new InjectionToken('QUERY_DEVTOOLS_HOST');
@@ -3205,7 +3235,7 @@ const summarizeLocks = (options) => {
3205
3235
 
3206
3236
  // Generated from package.json by tools/scripts/generate-version.js - do not edit.
3207
3237
  /** The version of `@ethlete/query-devtools` this build was cut from. */
3208
- const QUERY_DEVTOOLS_VERSION = '1.0.0-next.4';
3238
+ const QUERY_DEVTOOLS_VERSION = '1.0.0-next.6';
3209
3239
 
3210
3240
  /* eslint-disable @typescript-eslint/naming-convention -- the `__export_*` keys are Insomnia's file format */
3211
3241
  const WORKSPACE_ID = 'wrk_ethlete_query_devtools';
@@ -4112,6 +4142,11 @@ const DOCK_PADDING = {
4112
4142
  left: 'paddingInlineStart',
4113
4143
  right: 'paddingInlineEnd',
4114
4144
  };
4145
+ /**
4146
+ * The panel's own z-index, mirrored from `query-devtools.component.css`. An overlay below it keeps out
4147
+ * of the space a docked panel covers; the panel's own overlays paint a level above it and may use it.
4148
+ */
4149
+ const PANEL_LAYER = 2147483010;
4115
4150
  const noop = () => undefined;
4116
4151
  /**
4117
4152
  * A devtools entry id as a person reads it. Ids are the registry's descriptor plus a per-descriptor
@@ -4578,6 +4613,28 @@ class QueryDevtoolsComponent {
4578
4613
  return { padding: DOCK_PADDING[dock], px: this.sideDocked() ? size.width : size.height };
4579
4614
  }, /* @ts-ignore */
4580
4615
  ...(ngDevMode ? [{ debugName: "pageReservation" }] : /* istanbul ignore next */ []));
4616
+ /**
4617
+ * The viewport edge a docked panel covers, reserved for overlays whatever {@link reservesSpace} says:
4618
+ * page content can be scrolled out from under the panel, a dialog or a menu cannot. A dock edge is
4619
+ * logical, so which physical edge a side dock covers flips in RTL - read from the panel, since the
4620
+ * writing direction cannot change under a dock switch.
4621
+ */
4622
+ this.overlayReservation = computed(() => {
4623
+ const dock = this.dock();
4624
+ if (dock === 'float' || this.poppedOut() || !this.open())
4625
+ return null;
4626
+ const panel = this.panelEl()?.nativeElement;
4627
+ const size = this.panelSize().offset;
4628
+ if (!panel || !size)
4629
+ return null;
4630
+ if (dock === 'top')
4631
+ return { top: size.height, layer: PANEL_LAYER };
4632
+ if (dock === 'bottom')
4633
+ return { bottom: size.height, layer: PANEL_LAYER };
4634
+ const coversLeft = (dock === 'left') !== (getComputedStyle(panel).direction === 'rtl');
4635
+ return coversLeft ? { left: size.width, layer: PANEL_LAYER } : { right: size.width, layer: PANEL_LAYER };
4636
+ }, /* @ts-ignore */
4637
+ ...(ngDevMode ? [{ debugName: "overlayReservation" }] : /* istanbul ignore next */ []));
4581
4638
  /** Which section of the query detail is showing. Shared by the Queries tab and both drawers. */
4582
4639
  this.detailTab = signal(this.persisted.detailTab ?? 'overview', /* @ts-ignore */
4583
4640
  ...(ngDevMode ? [{ debugName: "detailTab" }] : /* istanbul ignore next */ []));
@@ -5147,6 +5204,12 @@ class QueryDevtoolsComponent {
5147
5204
  this.renderer.setStyle(root, { boxSizing: 'border-box', [reservation.padding]: `${reservation.px}px` });
5148
5205
  onCleanup(() => this.renderer.removeStyles(root, 'boxSizing', reservation.padding));
5149
5206
  });
5207
+ effect((onCleanup) => {
5208
+ const reservation = this.overlayReservation();
5209
+ if (!reservation)
5210
+ return;
5211
+ onCleanup(reserveOverlayViewportSpace(reservation));
5212
+ });
5150
5213
  // A window that shrinks below the floating panel would otherwise leave it half (or wholly) off
5151
5214
  // screen, with no edge left to grab. Untracked on the way in so clamping cannot re-trigger itself.
5152
5215
  effect(() => {