@ethlete/query-devtools 1.0.0-next.11 → 1.0.0-next.12

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,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0-next.12
4
+
5
+ ### Patch Changes
6
+
7
+ - Outside a development build the devtools session vault now defaults to `none`, so a plain login no longer leaves tokens in `sessionStorage`, and a vault left in either store is removed on load.
8
+ - Client `headers` and per-request `args.headers` accept a plain record as well as `HttpHeaders`, for secure queries too, so a client needs no `@angular/*` import to set a header.
9
+
3
10
  ## 1.0.0-next.11
4
11
 
5
12
  ### Minor Changes
@@ -2,7 +2,7 @@ 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
3
  import { takeUntilDestroyed, toSignal, toObservable } from '@angular/core/rxjs-interop';
4
4
  import { copyToClipboard, injectStyleManager, injectErrorTheme, ProvideColorDirective, clamp, injectRenderer, injectFileDownload, injectViewportSize, injectIsDocumentVisible, injectBreakpointObserver, signalElementDimensions, randomId, reserveOverlayViewportSpace, createObjectUrlHandle, DragHandleDirective, ResizeHandlesComponent } from '@ethlete/core';
5
- import { queryDevtoolsAbout, hasQueryDevtoolsOverridesAtPath, detectPaginationShape, isDateShapedLeaf, generateQueryDevtoolsStringPreset, generateQueryDevtoolsNumberPreset, collectLeafPaths, armAllQueryDevtoolsMocks, clearQueryDevtoolsArmedMocks, clearQueryDevtoolsFaults, queryDevtoolsResponseHistory, queryDevtoolsMocks, queryDevtoolsAuthSessions, queryDevtoolsArmedMocks, clearQueryDevtoolsAuthSessions, queryDevtoolsApiEnvs, queryDevtoolsApiEnvValues, setQueryDevtoolsApiEnv, queryDevtoolsSettings, queryDevtoolsAllowsLocalAuthSessions, setQueryDevtoolsOverridesScope, setQueryDevtoolsArmedMocksScope, setQueryDevtoolsFaultsScope, setQueryDevtoolsSettings, QUERY_DEVTOOLS_TOKEN_TTL_LIMIT, queryDevtoolsApiEnvIsProduction, queryDevtoolsAuthSessionsFor, queryDevtoolsAuthAccountsFor, queryDevtoolsAuthOtherScopeCount, queryDevtoolsAuthActive, queryDevtoolsAuthTabLocal, switchQueryDevtoolsAuthSession, renameQueryDevtoolsAuthSession, forgetQueryDevtoolsAuthSession, forgetQueryDevtoolsAuthSessionsFor, logoutQueryDevtoolsAuthSession, setQueryDevtoolsAuthTabLocal, loginQueryDevtoolsAuthAccount, setQueryDevtoolsAuthCredentials, clearQueryDevtoolsAuthCredentials, removeQueryDevtoolsAuthAccount, queryDevtoolsAuthFieldsFor, addQueryDevtoolsAuthAccount, 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, isQueryDevtoolsRepositoryLive, queryDevtoolsFaults, EMPTY_QUERY_DEVTOOLS_FAULT, isQueryDevtoolsFaultArmed, queryDevtoolsFaultsRestored, queryDevtoolsArmedMocksRestored, restoredQueryDevtoolsOverrides, queryDevtoolsRestoredOverridesScope, clearRestoredQueryDevtoolsOverrides, setQueryDevtoolsUiMounted, clearQueryDevtoolsStore, writeQueryDevtoolsStore, registerEthleteVersion, queryDevtoolsOverridePersistence, setQueryDevtoolsOverridePersistence, clearQueryDevtoolsOverrideStore, clearQueryDevtoolsMockStore, createQueryErrorResponse, applyQueryDevtoolsTokenTtl, canOverrideQueryDevtoolsTokenTtl, sumQueryDevtoolsStats } from '@ethlete/query';
5
+ import { queryDevtoolsAbout, hasQueryDevtoolsOverridesAtPath, detectPaginationShape, isDateShapedLeaf, generateQueryDevtoolsStringPreset, generateQueryDevtoolsNumberPreset, collectLeafPaths, armAllQueryDevtoolsMocks, clearQueryDevtoolsArmedMocks, clearQueryDevtoolsFaults, queryDevtoolsResponseHistory, queryDevtoolsMocks, queryDevtoolsAuthSessions, queryDevtoolsArmedMocks, clearQueryDevtoolsAuthSessions, queryDevtoolsApiEnvs, queryDevtoolsApiEnvValues, setQueryDevtoolsApiEnv, queryDevtoolsSettings, queryDevtoolsAllowsLocalAuthSessions, setQueryDevtoolsOverridesScope, setQueryDevtoolsArmedMocksScope, setQueryDevtoolsFaultsScope, setQueryDevtoolsSettings, QUERY_DEVTOOLS_TOKEN_TTL_LIMIT, queryDevtoolsApiEnvIsProduction, queryDevtoolsAuthSessionsFor, queryDevtoolsAuthAccountsFor, queryDevtoolsAuthOtherScopeCount, queryDevtoolsAuthActive, queryDevtoolsAuthTabLocal, switchQueryDevtoolsAuthSession, renameQueryDevtoolsAuthSession, forgetQueryDevtoolsAuthSession, forgetQueryDevtoolsAuthSessionsFor, logoutQueryDevtoolsAuthSession, setQueryDevtoolsAuthTabLocal, loginQueryDevtoolsAuthAccount, setQueryDevtoolsAuthCredentials, clearQueryDevtoolsAuthCredentials, removeQueryDevtoolsAuthAccount, queryDevtoolsAuthFieldsFor, addQueryDevtoolsAuthAccount, 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, isQueryDevtoolsRepositoryLive, queryDevtoolsFaults, EMPTY_QUERY_DEVTOOLS_FAULT, isQueryDevtoolsFaultArmed, queryDevtoolsFaultsRestored, queryDevtoolsArmedMocksRestored, restoredQueryDevtoolsOverrides, queryDevtoolsRestoredOverridesScope, clearRestoredQueryDevtoolsOverrides, setQueryDevtoolsUiMounted, clearQueryDevtoolsStore, writeQueryDevtoolsStore, registerEthleteVersion, queryDevtoolsOverridePersistence, setQueryDevtoolsOverridePersistence, clearQueryDevtoolsOverrideStore, clearQueryDevtoolsMockStore, createQueryErrorResponse, applyQueryDevtoolsTokenTtl, canOverrideQueryDevtoolsTokenTtl, resolveQueryHeaders, sumQueryDevtoolsStats } from '@ethlete/query';
6
6
  import { Subject, switchMap, timer, tap, EMPTY, interval, scan, animationFrames, NEVER, takeUntil, finalize as finalize$1, startWith, merge, map, fromEvent, filter, take } from 'rxjs';
7
7
  import { MenuDirective, MenuTriggerDirective, MenuSurfaceDirective, MenuComponent, MenuItemComponent, MenuSearchDirective, MenuSeparatorComponent, MenuItemShortcutComponent, SelectComponent, SelectSearchDirective, COMPONENTS_VERSION, MenuCheckboxItemComponent, MenuRadioGroupComponent, MenuRadioItemComponent } from '@ethlete/components';
8
8
  import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
@@ -1644,7 +1644,7 @@ class QueryDevtoolsSettingsComponent {
1644
1644
  unavailableReason(row, scope) {
1645
1645
  if (row.key !== 'authSessions' || scope !== 'local' || queryDevtoolsAllowsLocalAuthSessions())
1646
1646
  return null;
1647
- return 'Unavailable outside a development build: live access and refresh tokens, and the credentials you typed in, must not outlive this tab on a deployed origin. Session keeps the vault for as long as the tab.';
1647
+ return 'Unavailable outside a development build: live access and refresh tokens, and the credentials you typed in, must not outlive this tab on a deployed origin. The vault keeps nothing here unless you pick Session, which keeps it for as long as the tab.';
1648
1648
  }
1649
1649
  setScope(key, scope) {
1650
1650
  // Everything armed goes through its own module: changing the scope moves an existing store and
@@ -3554,7 +3554,7 @@ class QueryDevtoolsMocksTabComponent {
3554
3554
  });
3555
3555
  }
3556
3556
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: QueryDevtoolsMocksTabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3557
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: QueryDevtoolsMocksTabComponent, isStandalone: true, selector: "et-query-devtools-mocks-tab", ngImport: i0, template: "<div class=\"et-query-devtools-scroll\">\n <div class=\"et-query-devtools-toolbar et-query-devtools-mocks-toolbar\">\n <p class=\"et-query-devtools-hint\">\n A mock replaces the request, so it answers a route that has never run - or does not exist yet. It bypasses the\n interceptor chain by construction, so a mocked secure route never exercises the token flow. The library is kept;\n whether a mock is <strong>armed</strong> is not, so a reload always stops serving them. GraphQL queries are not\n offered: they all POST one route, so matching them needs the document rather than the path.\n </p>\n <div class=\"et-query-devtools-settings-controls\">\n @if (rows().length && armedCount() < rows().length) {\n <button\n (click)=\"ARM_ALL()\"\n class=\"et-query-devtools-btn\"\n type=\"button\"\n title=\"Serve every mock in the library - the whole screen off the network at once\"\n >\n Arm all ({{ rows().length }})\n </button>\n }\n @if (armedCount()) {\n <button (click)=\"DISARM_ALL()\" class=\"et-query-devtools-btn\" type=\"button\">\n Disarm all ({{ armedCount() }})\n </button>\n }\n <button (click)=\"openDraft()\" class=\"et-query-devtools-btn et-query-devtools-btn--primary\" type=\"button\">\n <span aria-hidden=\"true\">\uFF0B</span>\n New mock\n </button>\n </div>\n </div>\n\n @if (draft(); as form) {\n <div class=\"et-query-devtools-card\">\n <div class=\"et-query-devtools-card-head\">\n <strong>New mock</strong>\n <span class=\"et-query-devtools-count\">for any route, whether a query has ever called it or not</span>\n </div>\n\n <div class=\"et-query-devtools-mock-fields\">\n <label class=\"et-query-devtools-field\">\n <span>Client</span>\n <select #clientSelect (change)=\"pickClient(clientSelect.value)\" class=\"et-query-devtools-select\">\n @for (name of host.clientNames(); track name) {\n <option [value]=\"name\" [selected]=\"name === form.clientName\">{{ name }}</option>\n }\n </select>\n <small>a mock answers one client, matched by name</small>\n </label>\n\n <label class=\"et-query-devtools-field\">\n <span>Method</span>\n <select #methodSelect (change)=\"patchDraft({ method: methodSelect.value })\" class=\"et-query-devtools-select\">\n @for (method of METHODS; track method) {\n <option [value]=\"method\" [selected]=\"method === form.method\">{{ method }}</option>\n }\n </select>\n <small>matched exactly</small>\n </label>\n\n <label class=\"et-query-devtools-field et-query-devtools-field--wide\">\n <span>Path</span>\n <input\n #patternInput\n [value]=\"form.pattern\"\n (input)=\"patchDraft({ pattern: patternInput.value })\"\n class=\"et-query-devtools-input\"\n type=\"text\"\n placeholder=\"/posts/:id/comments\"\n />\n <small><code>:name</code> matches any one segment, so the route is mocked rather than one URL</small>\n </label>\n\n <label class=\"et-query-devtools-field et-query-devtools-field--wide\">\n <span>Query</span>\n <input\n #queryInput\n [value]=\"form.query\"\n (input)=\"patchDraft({ query: queryInput.value })\"\n class=\"et-query-devtools-input\"\n type=\"text\"\n placeholder=\"page=2&limit=10\"\n />\n <small>\n parameters the request must carry; anything else it asks for is ignored. Empty answers whatever the query\n string is - and a mock naming parameters wins over one that does not\n </small>\n </label>\n\n <label class=\"et-query-devtools-field\">\n <span>Status</span>\n <input\n #statusDraftInput\n [value]=\"form.status\"\n (input)=\"patchDraft({ status: statusDraftInput.value })\"\n class=\"et-query-devtools-input et-query-devtools-input--number\"\n type=\"number\"\n min=\"100\"\n max=\"599\"\n step=\"1\"\n />\n <small>400 and above arrive as a real error</small>\n </label>\n\n <label class=\"et-query-devtools-field\">\n <span>Latency</span>\n <input\n #latencyDraftInput\n [value]=\"form.latencyMs\"\n (input)=\"patchDraft({ latencyMs: latencyDraftInput.value })\"\n class=\"et-query-devtools-input et-query-devtools-input--number\"\n type=\"number\"\n min=\"0\"\n max=\"60000\"\n step=\"100\"\n />\n <small>ms before it settles</small>\n </label>\n </div>\n\n @if (hasSchema()) {\n <div class=\"et-query-devtools-seed\">\n <span class=\"et-query-devtools-section\">Seed</span>\n @if (isSchemaReady()) {\n <div class=\"et-query-devtools-seed-pick\">\n <et-select\n [options]=\"routeOptions()\"\n (pickOption)=\"pickRoute($event)\"\n class=\"et-query-devtools-combobox\"\n mirrorPanelWidth=\"false\"\n pickOnly\n >\n <input etSelectSearch placeholder=\"search the routes it declares\u2026\" />\n </et-select>\n <button\n (click)=\"seedFromDraftRoute()\"\n class=\"et-query-devtools-btn\"\n type=\"button\"\n title=\"Generate a body from what the description says this path returns\"\n >\n From this path\n </button>\n </div>\n <div class=\"et-query-devtools-seed-pick\">\n <et-select\n [value]=\"pickedSchema()\"\n [options]=\"schemaNameOptions()\"\n (valueChange)=\"pickSchema($event)\"\n class=\"et-query-devtools-combobox\"\n mirrorPanelWidth=\"false\"\n >\n <input etSelectSearch placeholder=\"search its named types\u2026\" />\n </et-select>\n <button\n [disabled]=\"!pickedSchema()\"\n (click)=\"seedFromSchema()\"\n class=\"et-query-devtools-btn\"\n type=\"button\"\n >\n From this type\n </button>\n </div>\n <select\n #stylePick\n (change)=\"pickSeedStyle(stylePick.value)\"\n class=\"et-query-devtools-select\"\n title=\"How lifelike the generated values are\"\n >\n @for (style of SEED_STYLES; track style.value) {\n <option [value]=\"style.value\" [selected]=\"style.value === seedStyle()\">{{ style.label }}</option>\n }\n </select>\n <small class=\"et-query-devtools-hint et-query-devtools-seed-hint\">{{ seedStyleHint() }}</small>\n } @else if (schemaError(); as error) {\n <span class=\"et-query-devtools-settings-warn\">The API description could not be loaded: {{ error }}</span>\n } @else {\n <span class=\"et-query-devtools-count\">Loading the API description\u2026</span>\n }\n </div>\n }\n\n <label class=\"et-query-devtools-mock-body\">\n <span class=\"et-query-devtools-section\">Body</span>\n <textarea\n #bodyInput\n [value]=\"form.body\"\n (input)=\"patchDraft({ body: bodyInput.value })\"\n class=\"et-query-devtools-input et-query-devtools-mock-textarea\"\n rows=\"8\"\n spellcheck=\"false\"\n ></textarea>\n </label>\n\n @if (draftSeed(); as seed) {\n <p class=\"et-query-devtools-hint\">\n Generated from the API description{{ seed.schemaName ? ' as ' + seed.schemaName : '' }} - a starting point,\n not the contract. Saving opens the designer on it.\n </p>\n @if (seed.notes.length) {\n <ul class=\"et-query-devtools-designer-notes\">\n @for (note of seed.notes; track note) {\n <li>{{ note }}</li>\n }\n </ul>\n }\n }\n\n @if (draftError(); as error) {\n <p class=\"et-query-devtools-settings-warn\">{{ error }}</p>\n }\n\n <div class=\"et-query-devtools-mock-head\">\n <button (click)=\"saveDraft()\" class=\"et-query-devtools-btn et-query-devtools-btn--primary\" type=\"button\">\n Save to the library\n </button>\n <button (click)=\"closeDraft()\" class=\"et-query-devtools-btn\" type=\"button\">Cancel</button>\n </div>\n </div>\n }\n\n <div class=\"et-query-devtools-card\">\n <div class=\"et-query-devtools-card-head\">\n <strong>Designed</strong>\n <span class=\"et-query-devtools-count\">{{ rows().length }} in the library</span>\n @if (rows().length) {\n <div class=\"et-query-devtools-spec-export\">\n <select #formatPick (change)=\"pickSpecFormat(formatPick.value)\" class=\"et-query-devtools-select\">\n @for (format of SPEC_FORMATS; track format.value) {\n <option [value]=\"format.value\" [selected]=\"format.value === specFormat()\">{{ format.label }}</option>\n }\n </select>\n <button\n (click)=\"exportLibrary()\"\n class=\"et-query-devtools-btn\"\n type=\"button\"\n title=\"Download every designed route as one OpenAPI 3.1 document - the response schemas inferred from the designed bodies, each body as an example\"\n >\n <span aria-hidden=\"true\">\u21E9</span>\n Export OpenAPI\n </button>\n </div>\n }\n </div>\n\n @for (row of rows(); track row.mock.id) {\n <div [class.et-query-devtools-mock-row--armed]=\"row.armed\" class=\"et-query-devtools-mock-row\">\n <div class=\"et-query-devtools-mock-head\">\n <button\n [class.et-query-devtools-btn--tampered]=\"row.armed\"\n [attr.aria-pressed]=\"row.armed\"\n (click)=\"toggleArmed(row)\"\n class=\"et-query-devtools-btn\"\n type=\"button\"\n title=\"{{ row.armed ? 'Stop serving this response' : 'Serve this response instead of the request' }}\"\n >\n {{ row.armed ? 'Armed' : 'Arm' }}\n </button>\n <span class=\"et-query-devtools-method\">{{ row.mock.method }}</span>\n <span class=\"et-query-devtools-route et-query-devtools-mono\">{{ row.mock.pattern }}</span>\n @if (row.mock.query) {\n <span class=\"et-query-devtools-url\">?{{ row.mock.query }}</span>\n }\n <span class=\"et-query-devtools-url\">{{ row.mock.clientName }}</span>\n @if (row.mock.capturedAt) {\n <span class=\"et-query-devtools-chip\">captured</span>\n }\n @if (row.mock.schemaName; as schemaName) {\n <span class=\"et-query-devtools-chip\" title=\"Seeded from the API description\">{{ schemaName }}</span>\n }\n @if (row.isSecure) {\n <span\n class=\"et-query-devtools-chip et-query-devtools-chip--error\"\n title=\"A mock sends nothing, so no token is attached and the auth flow is never exercised\"\n >\n no auth\n </span>\n }\n <span class=\"et-query-devtools-count\">{{ host.formatBytes(row.bytes) }}</span>\n <button (click)=\"editBody(row)\" class=\"et-query-devtools-btn\" type=\"button\">Body</button>\n <button\n (click)=\"copyDefinition(row)\"\n class=\"et-query-devtools-btn\"\n type=\"button\"\n title=\"Copy this route as a @ethlete/query definition - the response type inferred from the body, the args contract and the creator call\"\n >\n <span aria-hidden=\"true\">\u29C9</span>\n {{ copiedId() === row.mock.id ? 'Copied' : 'TS' }}\n </button>\n <button\n (click)=\"copyPathItem(row)\"\n class=\"et-query-devtools-btn\"\n type=\"button\"\n title=\"Copy this route as an OpenAPI paths entry, ready to paste under an existing description\"\n >\n <span aria-hidden=\"true\">\u29C9</span>\n {{ copiedSpecId() === row.mock.id ? 'Copied' : 'OAS' }}\n </button>\n <button (click)=\"remove(row)\" class=\"et-query-devtools-btn et-query-devtools-btn--danger\" type=\"button\">\n Delete\n </button>\n </div>\n\n <div class=\"et-query-devtools-mock-fields\">\n <label class=\"et-query-devtools-field\">\n <span>Status</span>\n <input\n #statusInput\n [value]=\"row.mock.status\"\n (change)=\"setStatus(row, statusInput.value)\"\n class=\"et-query-devtools-input et-query-devtools-input--number\"\n type=\"number\"\n min=\"100\"\n max=\"599\"\n step=\"1\"\n />\n <small>400 and above arrive as a real error, body and all</small>\n </label>\n <label class=\"et-query-devtools-field\">\n <span>Latency</span>\n <input\n #latencyInput\n [value]=\"row.mock.latencyMs\"\n (change)=\"setLatency(row, latencyInput.value)\"\n class=\"et-query-devtools-input et-query-devtools-input--number\"\n type=\"number\"\n min=\"0\"\n max=\"60000\"\n step=\"100\"\n />\n <small>ms before it settles, so the route still has a loading state</small>\n </label>\n </div>\n\n @if (editingId() === row.mock.id) {\n <et-query-devtools-mock-designer\n [body]=\"row.mock.body\"\n [annotations]=\"editingAnnotations()\"\n (save)=\"saveBody(row, $event)\"\n (dismiss)=\"cancelBody()\"\n />\n }\n </div>\n } @empty {\n <p class=\"et-query-devtools-empty\">\n Nothing designed yet. <strong>New mock</strong> writes one from scratch; capturing below seeds one from\n something that actually happened.\n </p>\n }\n\n @if (specNotes().length) {\n <ul class=\"et-query-devtools-designer-notes\">\n @for (note of specNotes(); track note) {\n <li>{{ note }}</li>\n }\n </ul>\n }\n </div>\n\n <div class=\"et-query-devtools-card\">\n <div class=\"et-query-devtools-card-head\">\n <strong>Capture</strong>\n <span class=\"et-query-devtools-count\">live queries holding a response</span>\n </div>\n\n @for (row of captureRows(); track row.entry.id) {\n <div class=\"et-query-devtools-mock-head\">\n <span class=\"et-query-devtools-method\">{{ row.method }}</span>\n <button\n (click)=\"host.selectQuery(row.entry.id)\"\n class=\"et-query-devtools-route et-query-devtools-mono et-query-devtools-row-link\"\n type=\"button\"\n title=\"Open this query\"\n >\n {{ row.pattern }}\n </button>\n <span class=\"et-query-devtools-url\">{{ row.clientName }}</span>\n @if (row.shared > 1) {\n <span class=\"et-query-devtools-count\" title=\"A mock answers the route, so it answers all of them\">\n {{ row.shared }} queries\n </span>\n }\n <span class=\"et-query-devtools-count\">{{ host.formatBytes(row.bytes) }}</span>\n <button (click)=\"capture(row)\" class=\"et-query-devtools-btn\" type=\"button\">\n {{ row.exists ? 'Re-capture' : 'Capture' }}\n </button>\n </div>\n } @empty {\n <p class=\"et-query-devtools-empty\">No live query has a response yet.</p>\n }\n </div>\n</div>\n", styles: ["@layer components{.et-query-devtools-mocks-toolbar{align-items:flex-start}.et-query-devtools-mock-row{padding-block:.6rem;border-block-start:1px solid var(--_et-qdt-border)}.et-query-devtools-card>.et-query-devtools-mock-row:first-of-type{border-block-start:0;padding-block-start:0}.et-query-devtools-mock-row--armed{background:color-mix(in srgb,var(--_et-qdt-error) 6%,transparent)}.et-query-devtools-mock-head{display:flex;align-items:center;flex-wrap:wrap;gap:.6rem;min-block-size:var(--_et-qdt-row-h)}.et-query-devtools-mock-fields,.et-query-devtools-seed,.et-query-devtools-mock-body{max-inline-size:96rem}.et-query-devtools-mock-fields{display:grid;grid-template-columns:repeat(auto-fit,minmax(38rem,1fr));align-items:start;gap:.4rem 1.6rem;padding-block-start:.4rem}.et-query-devtools-mock-fields .et-query-devtools-field{display:grid;grid-template-columns:7rem minmax(0,1fr);align-items:baseline;row-gap:.2rem;color:var(--_et-qdt-fg)}.et-query-devtools-mock-fields .et-query-devtools-select{justify-self:start;max-inline-size:100%}.et-query-devtools-mock-fields .et-query-devtools-field small{grid-column:2;color:var(--_et-qdt-subtle);font-size:1.1rem}.et-query-devtools-mock-fields .et-query-devtools-field--wide{grid-column:1 / -1;grid-template-columns:7rem minmax(0,1fr)}.et-query-devtools-spec-export{display:flex;align-items:center;gap:.6rem;margin-inline-start:auto}.et-query-devtools-seed{display:flex;align-items:center;flex-wrap:wrap;gap:.4rem 2.4rem;padding-block-start:.4rem;min-block-size:var(--_et-qdt-row-h)}.et-query-devtools-seed-pick{display:flex;align-items:center;gap:.2rem}.et-query-devtools-seed .et-query-devtools-select{max-inline-size:32rem}.et-query-devtools-combobox{flex:0 1 auto;inline-size:32rem;min-block-size:var(--_et-qdt-control-h);box-sizing:border-box;background:color-mix(in srgb,var(--_et-qdt-interaction) 5%,var(--_et-qdt-bg));color:var(--_et-qdt-fg);border:1px solid transparent;border-radius:var(--_et-qdt-radius);padding:.4rem .6rem;font-size:1.15rem}.et-query-devtools-combobox input{background:none;border:0;padding:0;color:inherit;font:inherit}.et-query-devtools-seed-hint{flex-basis:100%;margin:0}.et-query-devtools-mock-body{display:block;padding-block-start:.4rem}.et-query-devtools-mock-textarea{display:block;inline-size:100%;resize:vertical;font-family:inherit;font-size:1.15rem;line-height:1.5;tab-size:2}}\n"], dependencies: [{ kind: "component", type: QueryDevtoolsMockDesignerComponent, selector: "et-query-devtools-mock-designer", inputs: ["body", "annotations", "notes"], outputs: ["save", "dismiss"] }, { kind: "component", type: SelectComponent, selector: "et-select", inputs: ["loadMoreLabel", "addNewLabel", "createLabel", "clearable", "clearLabel"] }, { kind: "directive", type: SelectSearchDirective, selector: "input[etSelectSearch]", inputs: ["query"], outputs: ["queryChange"], exportAs: ["etSelectSearch"] }], encapsulation: i0.ViewEncapsulation.None }); }
3557
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: QueryDevtoolsMocksTabComponent, isStandalone: true, selector: "et-query-devtools-mocks-tab", ngImport: i0, template: "<div class=\"et-query-devtools-scroll\">\n <div class=\"et-query-devtools-toolbar et-query-devtools-mocks-toolbar\">\n <p class=\"et-query-devtools-hint\">\n A mock replaces the request, so it answers a route that has never run - or does not exist yet. It bypasses the\n interceptor chain by construction, so a mocked secure route never exercises the token flow. The library is kept;\n whether a mock is <strong>armed</strong> is not, so a reload always stops serving them. GraphQL queries are not\n offered: they all POST one route, so matching them needs the document rather than the path.\n </p>\n <div class=\"et-query-devtools-settings-controls\">\n @if (rows().length && armedCount() < rows().length) {\n <button\n (click)=\"ARM_ALL()\"\n class=\"et-query-devtools-btn\"\n type=\"button\"\n title=\"Serve every mock in the library - the whole screen off the network at once\"\n >\n Arm all ({{ rows().length }})\n </button>\n }\n @if (armedCount()) {\n <button (click)=\"DISARM_ALL()\" class=\"et-query-devtools-btn\" type=\"button\">\n Disarm all ({{ armedCount() }})\n </button>\n }\n <button (click)=\"openDraft()\" class=\"et-query-devtools-btn et-query-devtools-btn--primary\" type=\"button\">\n <span aria-hidden=\"true\">\uFF0B</span>\n New mock\n </button>\n </div>\n </div>\n\n @if (draft(); as form) {\n <div class=\"et-query-devtools-card\">\n <div class=\"et-query-devtools-card-head\">\n <strong>New mock</strong>\n <span class=\"et-query-devtools-count\">for any route, whether a query has ever called it or not</span>\n </div>\n\n <div class=\"et-query-devtools-mock-fields\">\n <label class=\"et-query-devtools-field\">\n <span>Client</span>\n <select #clientSelect (change)=\"pickClient(clientSelect.value)\" class=\"et-query-devtools-select\">\n @for (name of host.clientNames(); track name) {\n <option [value]=\"name\" [selected]=\"name === form.clientName\">{{ name }}</option>\n }\n </select>\n <small>a mock answers one client, matched by name</small>\n </label>\n\n <label class=\"et-query-devtools-field\">\n <span>Method</span>\n <select #methodSelect (change)=\"patchDraft({ method: methodSelect.value })\" class=\"et-query-devtools-select\">\n @for (method of METHODS; track method) {\n <option [value]=\"method\" [selected]=\"method === form.method\">{{ method }}</option>\n }\n </select>\n <small>matched exactly</small>\n </label>\n\n <label class=\"et-query-devtools-field et-query-devtools-field--wide\">\n <span>Path</span>\n <input\n #patternInput\n [value]=\"form.pattern\"\n (input)=\"patchDraft({ pattern: patternInput.value })\"\n class=\"et-query-devtools-input\"\n type=\"text\"\n placeholder=\"/posts/:id/comments\"\n />\n <small><code>:name</code> matches any one segment, so the route is mocked rather than one URL</small>\n </label>\n\n <label class=\"et-query-devtools-field et-query-devtools-field--wide\">\n <span>Query</span>\n <input\n #queryInput\n [value]=\"form.query\"\n (input)=\"patchDraft({ query: queryInput.value })\"\n class=\"et-query-devtools-input\"\n type=\"text\"\n placeholder=\"page=2&limit=10\"\n />\n <small>\n parameters the request must carry; anything else it asks for is ignored. Empty answers whatever the query\n string is - and a mock naming parameters wins over one that does not\n </small>\n </label>\n\n <label class=\"et-query-devtools-field\">\n <span>Status</span>\n <input\n #statusDraftInput\n [value]=\"form.status\"\n (input)=\"patchDraft({ status: statusDraftInput.value })\"\n class=\"et-query-devtools-input et-query-devtools-input--number\"\n type=\"number\"\n min=\"100\"\n max=\"599\"\n step=\"1\"\n />\n <small>400 and above arrive as a real error</small>\n </label>\n\n <label class=\"et-query-devtools-field\">\n <span>Latency</span>\n <input\n #latencyDraftInput\n [value]=\"form.latencyMs\"\n (input)=\"patchDraft({ latencyMs: latencyDraftInput.value })\"\n class=\"et-query-devtools-input et-query-devtools-input--number\"\n type=\"number\"\n min=\"0\"\n max=\"60000\"\n step=\"100\"\n />\n <small>ms before it settles</small>\n </label>\n </div>\n\n @if (hasSchema()) {\n <div class=\"et-query-devtools-seed\">\n <span class=\"et-query-devtools-section\">Seed</span>\n @if (isSchemaReady()) {\n <div class=\"et-query-devtools-seed-pick\">\n <et-select\n [options]=\"routeOptions()\"\n (pickOption)=\"pickRoute($event)\"\n class=\"et-query-devtools-combobox\"\n mirrorPanelWidth=\"false\"\n pickOnly\n >\n <input etSelectSearch placeholder=\"search the routes it declares\u2026\" />\n </et-select>\n <button\n (click)=\"seedFromDraftRoute()\"\n class=\"et-query-devtools-btn\"\n type=\"button\"\n title=\"Generate a body from what the description says this path returns\"\n >\n From this path\n </button>\n </div>\n <div class=\"et-query-devtools-seed-pick\">\n <et-select\n [value]=\"pickedSchema()\"\n [options]=\"schemaNameOptions()\"\n (valueChange)=\"pickSchema($event)\"\n class=\"et-query-devtools-combobox\"\n mirrorPanelWidth=\"false\"\n >\n <input etSelectSearch placeholder=\"search its named types\u2026\" />\n </et-select>\n <button\n [disabled]=\"!pickedSchema()\"\n (click)=\"seedFromSchema()\"\n class=\"et-query-devtools-btn\"\n type=\"button\"\n >\n From this type\n </button>\n </div>\n <select\n #stylePick\n (change)=\"pickSeedStyle(stylePick.value)\"\n class=\"et-query-devtools-select\"\n title=\"How lifelike the generated values are\"\n >\n @for (style of SEED_STYLES; track style.value) {\n <option [value]=\"style.value\" [selected]=\"style.value === seedStyle()\">{{ style.label }}</option>\n }\n </select>\n <small class=\"et-query-devtools-hint et-query-devtools-seed-hint\">{{ seedStyleHint() }}</small>\n } @else if (schemaError(); as error) {\n <span class=\"et-query-devtools-settings-warn\">The API description could not be loaded: {{ error }}</span>\n } @else {\n <span class=\"et-query-devtools-count\">Loading the API description\u2026</span>\n }\n </div>\n }\n\n <label class=\"et-query-devtools-mock-body\">\n <span class=\"et-query-devtools-section\">Body</span>\n <textarea\n #bodyInput\n [value]=\"form.body\"\n (input)=\"patchDraft({ body: bodyInput.value })\"\n class=\"et-query-devtools-input et-query-devtools-mock-textarea\"\n rows=\"8\"\n spellcheck=\"false\"\n ></textarea>\n </label>\n\n @if (draftSeed(); as seed) {\n <p class=\"et-query-devtools-hint\">\n Generated from the API description{{ seed.schemaName ? ' as ' + seed.schemaName : '' }} - a starting point,\n not the contract. Saving opens the designer on it.\n </p>\n @if (seed.notes.length) {\n <ul class=\"et-query-devtools-designer-notes\">\n @for (note of seed.notes; track note) {\n <li>{{ note }}</li>\n }\n </ul>\n }\n }\n\n @if (draftError(); as error) {\n <p class=\"et-query-devtools-settings-warn\">{{ error }}</p>\n }\n\n <div class=\"et-query-devtools-mock-head\">\n <button (click)=\"saveDraft()\" class=\"et-query-devtools-btn et-query-devtools-btn--primary\" type=\"button\">\n Save to the library\n </button>\n <button (click)=\"closeDraft()\" class=\"et-query-devtools-btn\" type=\"button\">Cancel</button>\n </div>\n </div>\n }\n\n <div class=\"et-query-devtools-card\">\n <div class=\"et-query-devtools-card-head\">\n <strong>Designed</strong>\n <span class=\"et-query-devtools-count\">{{ rows().length }} in the library</span>\n @if (rows().length) {\n <div class=\"et-query-devtools-spec-export\">\n <select #formatPick (change)=\"pickSpecFormat(formatPick.value)\" class=\"et-query-devtools-select\">\n @for (format of SPEC_FORMATS; track format.value) {\n <option [value]=\"format.value\" [selected]=\"format.value === specFormat()\">{{ format.label }}</option>\n }\n </select>\n <button\n (click)=\"exportLibrary()\"\n class=\"et-query-devtools-btn\"\n type=\"button\"\n title=\"Download every designed route as one OpenAPI 3.1 document - the response schemas inferred from the designed bodies, each body as an example\"\n >\n <span aria-hidden=\"true\">\u21E9</span>\n Export OpenAPI\n </button>\n </div>\n }\n </div>\n\n @for (row of rows(); track row.mock.id) {\n <div [class.et-query-devtools-mock-row--armed]=\"row.armed\" class=\"et-query-devtools-mock-row\">\n <div class=\"et-query-devtools-mock-head\">\n <button\n [class.et-query-devtools-btn--tampered]=\"row.armed\"\n [attr.aria-pressed]=\"row.armed\"\n (click)=\"toggleArmed(row)\"\n class=\"et-query-devtools-btn\"\n type=\"button\"\n title=\"{{ row.armed ? 'Stop serving this response' : 'Serve this response instead of the request' }}\"\n >\n {{ row.armed ? 'Armed' : 'Arm' }}\n </button>\n <span class=\"et-query-devtools-method\">{{ row.mock.method }}</span>\n <span class=\"et-query-devtools-route et-query-devtools-mono\">{{ row.mock.pattern }}</span>\n @if (row.mock.query) {\n <span class=\"et-query-devtools-url\">?{{ row.mock.query }}</span>\n }\n <span class=\"et-query-devtools-url\">{{ row.mock.clientName }}</span>\n @if (row.mock.capturedAt) {\n <span class=\"et-query-devtools-chip\">captured</span>\n }\n @if (row.mock.schemaName; as schemaName) {\n <span class=\"et-query-devtools-chip\" title=\"Seeded from the API description\">{{ schemaName }}</span>\n }\n @if (row.isSecure) {\n <span\n class=\"et-query-devtools-chip et-query-devtools-chip--error\"\n title=\"A mock sends nothing, so no token is attached and the auth flow is never exercised\"\n >\n no auth\n </span>\n }\n <span class=\"et-query-devtools-count\">{{ host.formatBytes(row.bytes) }}</span>\n <button (click)=\"editBody(row)\" class=\"et-query-devtools-btn\" type=\"button\">Body</button>\n <button\n (click)=\"copyDefinition(row)\"\n class=\"et-query-devtools-btn\"\n type=\"button\"\n title=\"Copy this route as a @ethlete/query definition - the response type inferred from the body, the args contract and the creator call\"\n >\n <span aria-hidden=\"true\">\u29C9</span>\n {{ copiedId() === row.mock.id ? 'Copied' : 'TS' }}\n </button>\n <button\n (click)=\"copyPathItem(row)\"\n class=\"et-query-devtools-btn\"\n type=\"button\"\n title=\"Copy this route as an OpenAPI paths entry, ready to paste under an existing description\"\n >\n <span aria-hidden=\"true\">\u29C9</span>\n {{ copiedSpecId() === row.mock.id ? 'Copied' : 'OAS' }}\n </button>\n <button (click)=\"remove(row)\" class=\"et-query-devtools-btn et-query-devtools-btn--danger\" type=\"button\">\n Delete\n </button>\n </div>\n\n <div class=\"et-query-devtools-mock-fields\">\n <label class=\"et-query-devtools-field\">\n <span>Status</span>\n <input\n #statusInput\n [value]=\"row.mock.status\"\n (change)=\"setStatus(row, statusInput.value)\"\n class=\"et-query-devtools-input et-query-devtools-input--number\"\n type=\"number\"\n min=\"100\"\n max=\"599\"\n step=\"1\"\n />\n <small>400 and above arrive as a real error, body and all</small>\n </label>\n <label class=\"et-query-devtools-field\">\n <span>Latency</span>\n <input\n #latencyInput\n [value]=\"row.mock.latencyMs\"\n (change)=\"setLatency(row, latencyInput.value)\"\n class=\"et-query-devtools-input et-query-devtools-input--number\"\n type=\"number\"\n min=\"0\"\n max=\"60000\"\n step=\"100\"\n />\n <small>ms before it settles, so the route still has a loading state</small>\n </label>\n </div>\n\n @if (editingId() === row.mock.id) {\n <et-query-devtools-mock-designer\n [body]=\"row.mock.body\"\n [annotations]=\"editingAnnotations()\"\n (save)=\"saveBody(row, $event)\"\n (dismiss)=\"cancelBody()\"\n />\n }\n </div>\n } @empty {\n <p class=\"et-query-devtools-empty\">\n Nothing designed yet. <strong>New mock</strong> writes one from scratch; capturing below seeds one from\n something that actually happened.\n </p>\n }\n\n @if (specNotes().length) {\n <ul class=\"et-query-devtools-designer-notes\">\n @for (note of specNotes(); track note) {\n <li>{{ note }}</li>\n }\n </ul>\n }\n </div>\n\n <div class=\"et-query-devtools-card\">\n <div class=\"et-query-devtools-card-head\">\n <strong>Capture</strong>\n <span class=\"et-query-devtools-count\">live queries holding a response</span>\n </div>\n\n @for (row of captureRows(); track row.entry.id) {\n <div class=\"et-query-devtools-mock-head\">\n <span class=\"et-query-devtools-method\">{{ row.method }}</span>\n <button\n (click)=\"host.selectQuery(row.entry.id)\"\n class=\"et-query-devtools-route et-query-devtools-mono et-query-devtools-row-link\"\n type=\"button\"\n title=\"Open this query\"\n >\n {{ row.pattern }}\n </button>\n <span class=\"et-query-devtools-url\">{{ row.clientName }}</span>\n @if (row.shared > 1) {\n <span class=\"et-query-devtools-count\" title=\"A mock answers the route, so it answers all of them\">\n {{ row.shared }} queries\n </span>\n }\n <span class=\"et-query-devtools-count\">{{ host.formatBytes(row.bytes) }}</span>\n <button (click)=\"capture(row)\" class=\"et-query-devtools-btn\" type=\"button\">\n {{ row.exists ? 'Re-capture' : 'Capture' }}\n </button>\n </div>\n } @empty {\n <p class=\"et-query-devtools-empty\">No live query has a response yet.</p>\n }\n </div>\n</div>\n", styles: ["@layer components{.et-query-devtools-mocks-toolbar{align-items:flex-start}.et-query-devtools-mock-row{padding-block:.6rem;border-block-start:1px solid var(--_et-qdt-border)}.et-query-devtools-card>.et-query-devtools-mock-row:first-of-type{border-block-start:0;padding-block-start:0}.et-query-devtools-mock-row--armed{background:color-mix(in srgb,var(--_et-qdt-error) 6%,transparent)}.et-query-devtools-mock-head{display:flex;align-items:center;flex-wrap:wrap;gap:.6rem;min-block-size:var(--_et-qdt-row-h)}.et-query-devtools-mock-fields,.et-query-devtools-seed,.et-query-devtools-mock-body{max-inline-size:96rem}.et-query-devtools-mock-fields{display:grid;grid-template-columns:repeat(auto-fit,minmax(38rem,1fr));align-items:start;gap:.4rem 1.6rem;padding-block-start:.4rem}.et-query-devtools-mock-fields .et-query-devtools-field{display:grid;grid-template-columns:7rem minmax(0,1fr);align-items:baseline;row-gap:.2rem;color:var(--_et-qdt-fg)}.et-query-devtools-mock-fields .et-query-devtools-select{justify-self:start;max-inline-size:100%}.et-query-devtools-mock-fields .et-query-devtools-field small{grid-column:2;color:var(--_et-qdt-subtle);font-size:1.1rem}.et-query-devtools-mock-fields .et-query-devtools-field--wide{grid-column:1 / -1;grid-template-columns:7rem minmax(0,1fr)}.et-query-devtools-spec-export{display:flex;align-items:center;gap:.6rem;margin-inline-start:auto}.et-query-devtools-seed{display:flex;align-items:center;flex-wrap:wrap;gap:.4rem 2.4rem;padding-block-start:.4rem;min-block-size:var(--_et-qdt-row-h)}.et-query-devtools-seed-pick{display:flex;align-items:center;gap:.2rem}.et-query-devtools-seed .et-query-devtools-select{max-inline-size:32rem}.et-query-devtools-combobox{flex:0 1 auto;inline-size:32rem;min-block-size:var(--_et-qdt-control-h);box-sizing:border-box;background:color-mix(in srgb,var(--_et-qdt-interaction) 5%,var(--_et-qdt-bg));color:var(--_et-qdt-fg);border:1px solid transparent;border-radius:var(--_et-qdt-radius);padding:.4rem .6rem;font-size:1.15rem}.et-query-devtools-combobox input{background:none;border:0;padding:0;color:inherit;font:inherit}.et-query-devtools-seed-hint{flex-basis:100%;margin:0}.et-query-devtools-mock-body{display:block;padding-block-start:.4rem}.et-query-devtools-mock-textarea{display:block;inline-size:100%;resize:vertical;font-family:inherit;font-size:1.15rem;line-height:1.5;tab-size:2}}\n"], dependencies: [{ kind: "component", type: QueryDevtoolsMockDesignerComponent, selector: "et-query-devtools-mock-designer", inputs: ["body", "annotations", "notes"], outputs: ["save", "dismiss"] }, { kind: "component", type: SelectComponent, selector: "et-select", inputs: ["loadMoreLabel", "addNewLabel", "createLabel", "clearable", "clearLabel", "selectAllLabel"] }, { kind: "directive", type: SelectSearchDirective, selector: "input[etSelectSearch]", inputs: ["query"], outputs: ["queryChange"], exportAs: ["etSelectSearch"] }], encapsulation: i0.ViewEncapsulation.None }); }
3558
3558
  }
3559
3559
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: QueryDevtoolsMocksTabComponent, decorators: [{
3560
3560
  type: Component,
@@ -3696,7 +3696,7 @@ const summarizeLocks = (options) => {
3696
3696
 
3697
3697
  // Generated from package.json by tools/scripts/generate-version.js - do not edit.
3698
3698
  /** The version of `@ethlete/query-devtools` this build was cut from. */
3699
- const QUERY_DEVTOOLS_VERSION = '1.0.0-next.11';
3699
+ const QUERY_DEVTOOLS_VERSION = '1.0.0-next.12';
3700
3700
 
3701
3701
  /**
3702
3702
  * How deep a value is walked before it is replaced by a marker, and how much of a long string or a long
@@ -7067,9 +7067,7 @@ class QueryDevtoolsComponent {
7067
7067
  try {
7068
7068
  if (request)
7069
7069
  return request.subtle.resolveHeaders();
7070
- return typeof args?.headers === 'function'
7071
- ? args.headers()
7072
- : args?.headers;
7070
+ return resolveQueryHeaders(args?.headers);
7073
7071
  }
7074
7072
  catch {
7075
7073
  return undefined;