From ac95873d864fee855072f8cb530d6533e14a6259 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Tue, 10 Nov 2026 02:56:11 +0100 Subject: [PATCH 1/3] chore(web): remove dead front-end code left by the Foldkit migration Deletions only; nothing a user sees changes. Every symbol was confirmed unreferenced by a whole-word grep of apps, packages, lib, scripts and examples before removal. packages/ui - 70 style constants in components/ui/*.styles.ts with no reference anywhere (toast 13, sidebar 11, autocomplete 8, combobox 8, context-menu 6, item 3, sheet 3, and smaller sets in 11 more files). - Dead exports: linearYScale (and its scaleLinear import), BucketAxis, three sample-data sets, EMPTY_SERVICE_MAP_LAYOUT, DEFAULT_SERVICE_MAP_VIEW_PREFS, TimelineAction and three timeline constants, CACHE_OPERATION_COLORS, LATENCY_COLOR, ERROR_RATE_COLOR, getDepthColorClass (with CHART_BG_CLASSES, which only it read), sanitizeCssIdentifier. DECLUTTER_OFF moves into the one test that used it. LatencyLevel is no longer exported. packages/web-data - Drop the `.` and `./collections` exports and their two barrels: no importer. - Dead functions: getGcpCloudSqlInstancesOfDatabase, identityFromKey, orgScopedKeyPayload (and the timestamp-dropping branch only they reached), isAutomaticRetryError, toolSeriesMode. - Dead types: AlertsOverviewData, AiSessionSpansPage, CustomChartTimeSeriesResponse, TracesResponse, TracesFacetsResponse. UserDetailInput and WebAnalyticsFacetRow are no longer exported. apps/web - lib/anomalies/anomaly-filters.ts: only its own test imported it. - 17 exports only their tests used, with those test cases. Tests of live code that went through them now call the live function (formatAttributeClause, customLayoutOf, decorateRoutes). - VITE_MAPLE_CAPTURE: nothing sets it since the perf harness went. - Lab descriptions no longer advertise ?mode= and ?renderer= options that no bench reads. - The Vite override list gains the three VITE_* keys the app reads and the list missed. - theme.css: two selectors for a chart library that is gone. - index.html: the theme comment names the files that hold the store. Also: packages/widgets tsconfig no longer includes *.tsx (it has none), four unreferenced onboarding pixel glyphs, and the regenerated kit index. --- apps/web/index.html | 3 +- .../components/infra/gcp/inventory.test.ts | 24 --- .../web/src/components/infra/gcp/inventory.ts | 29 --- .../web/src/components/infra/gcp/tabs.test.ts | 24 --- apps/web/src/components/infra/gcp/tabs.ts | 8 - .../integrations/gcp-connector-state.test.ts | 22 +-- .../integrations/gcp-connector-state.ts | 21 --- .../service-map/three/factory-routing.test.ts | 19 +- .../service-map/three/factory-routing.ts | 17 -- .../settings/audit-log-filters.test.ts | 20 --- .../components/settings/audit-log-filters.ts | 19 -- apps/web/src/lab/registry.ts | 11 +- .../src/lib/anomalies/anomaly-filters.test.ts | 56 ------ apps/web/src/lib/anomalies/anomaly-filters.ts | 94 ---------- .../src/lib/billing/billing-profile.test.ts | 7 +- apps/web/src/lib/billing/billing-profile.ts | 4 - apps/web/src/lib/billing/plan-gating.test.ts | 26 --- apps/web/src/lib/billing/plan-gating.ts | 8 - .../web/src/lib/services/error-issues.test.ts | 17 +- apps/web/src/lib/services/error-issues.ts | 30 ---- .../lib/traces/advanced-filter-sync.test.ts | 168 +++++------------- .../src/lib/traces/advanced-filter-sync.ts | 84 --------- apps/web/src/page/kit/relative-time.ts | 5 - .../src/page/service-map/scene-layout.test.ts | 9 +- apps/web/src/page/service-map/scene-layout.ts | 6 - .../data-platform/data-platform.scene.test.ts | 5 +- .../src/page/settings/data-platform/update.ts | 12 -- apps/web/src/platform/telemetry.ts | 3 +- apps/web/src/theme.css | 6 +- apps/web/vite.config.ts | 4 +- .../components/charts/_shared/sample-data.ts | 27 --- .../ui/src/components/plot/bucket-axis.ts | 2 - .../ui/src/components/plot/plot-scales.ts | 7 +- .../service-map/service-map-declutter.test.ts | 8 +- .../service-map/service-map-declutter.ts | 6 - .../service-map/service-map-layout-state.ts | 7 - .../components/traces/trace-timeline-types.ts | 23 --- .../src/components/ui/alert-dialog.styles.ts | 2 - .../src/components/ui/autocomplete.styles.ts | 20 --- .../ui/src/components/ui/bubble.styles.ts | 20 --- .../ui/src/components/ui/combobox.styles.ts | 18 -- .../ui/src/components/ui/command.styles.ts | 5 - .../src/components/ui/context-menu.styles.ts | 14 -- .../ui/src/components/ui/dialog.styles.ts | 3 - packages/ui/src/components/ui/field.styles.ts | 5 - packages/ui/src/components/ui/input.styles.ts | 3 - packages/ui/src/components/ui/item.styles.ts | 5 - .../components/ui/navigation-menu.styles.ts | 6 - .../ui/src/components/ui/popover.styles.ts | 5 - .../ui/src/components/ui/select.styles.ts | 4 - packages/ui/src/components/ui/sheet.styles.ts | 8 - .../ui/src/components/ui/sidebar.styles.ts | 35 ---- packages/ui/src/components/ui/table.styles.ts | 5 - packages/ui/src/components/ui/toast.styles.ts | 37 ---- .../src/components/ui/toggle-group.styles.ts | 4 - packages/ui/src/lib/__tests__/colors.test.ts | 16 -- .../ui/src/lib/__tests__/sanitizers.test.ts | 12 -- packages/ui/src/lib/cache.ts | 6 - packages/ui/src/lib/colors.ts | 19 -- packages/ui/src/lib/error-rate.ts | 8 +- packages/ui/src/lib/latency-tone.ts | 11 +- packages/ui/src/lib/sanitizers.ts | 15 -- packages/views/src/ui/INDEX.md | 2 +- packages/views/src/ui/icons.generated.ts | 81 --------- packages/web-data/package.json | 2 - packages/web-data/src/alerts/overview.ts | 6 - packages/web-data/src/collections/index.ts | 7 - packages/web-data/src/index.ts | 6 - packages/web-data/src/lib/cache-key.test.ts | 81 +-------- packages/web-data/src/lib/cache-key.ts | 74 +------- .../web-data/src/lib/error-messages.test.ts | 8 +- packages/web-data/src/lib/error-messages.ts | 2 - .../web-data/src/lib/tool-analytics.test.ts | 13 -- packages/web-data/src/lib/tool-analytics.ts | 17 -- .../web-data/src/warehouse/ai-sessions.ts | 1 - .../web-data/src/warehouse/custom-charts.ts | 9 - packages/web-data/src/warehouse/gcp-infra.ts | 39 ---- packages/web-data/src/warehouse/traces.ts | 21 --- packages/web-data/src/warehouse/users.ts | 2 +- .../web-data/src/warehouse/web-analytics.ts | 2 +- packages/widgets/tsconfig.json | 2 +- 81 files changed, 96 insertions(+), 1406 deletions(-) delete mode 100644 apps/web/src/lib/anomalies/anomaly-filters.test.ts delete mode 100644 apps/web/src/lib/anomalies/anomaly-filters.ts delete mode 100644 packages/ui/src/lib/__tests__/colors.test.ts delete mode 100644 packages/web-data/src/collections/index.ts delete mode 100644 packages/web-data/src/index.ts diff --git a/apps/web/index.html b/apps/web/index.html index fb757e153d..b81b0250ac 100644 --- a/apps/web/index.html +++ b/apps/web/index.html @@ -38,7 +38,8 @@ ` cannot break out of the surrounding - * `--color-…` declaration. - */ -export const sanitizeCssIdentifier = (key: string): string => { - if (typeof CSS !== "undefined" && typeof CSS.escape === "function") { - return CSS.escape(key) - } - // Fallback for environments without CSS.escape: hex-encode anything - // that isn't [a-zA-Z0-9_-]. - // oxlint-disable-next-line effecttsgo/prefer-effect-string -- function replacer; Str.replace only takes a string - return key.replace(/[^a-zA-Z0-9_-]/g, (ch) => `\\${ch.charCodeAt(0).toString(16)} `) -} - /** * Returns `value` only if it parses as a known-safe CSS color expression. * Drops anything containing CSS structural tokens like `;`, `}`, `<` etc. diff --git a/packages/views/src/ui/INDEX.md b/packages/views/src/ui/INDEX.md index efea20d0f7..56017760a3 100644 --- a/packages/views/src/ui/INDEX.md +++ b/packages/views/src/ui/INDEX.md @@ -220,7 +220,7 @@ existing copies onto it (see `apps/web/CLAUDE.md`). - `apps/web/src/page/kit/query-builder-legend.ts` `queryBuilderLegend`: `QueryBuilderLegend` (packages/ui/src/components/charts/_shared/query-builder-legend.tsx), bottom layout, for hosts outside a chart. - `apps/web/src/page/kit/query-panel-bar.ts` `queryPanelBar`: A query panel's header bar on the sunken wash: its collapse toggle, query badge, source and actions. - `apps/web/src/page/kit/refresh-controls.ts` `formatRefreshInterval`, `refreshControls`: `RefreshControls`: Reload joined to the auto-refresh cadence menu. -- `apps/web/src/page/kit/relative-time.ts` `formatAbsolute`, `relativeTime`, `listRelativeTime`: `RelativeTime`: `timestamp` with relative text. +- `apps/web/src/page/kit/relative-time.ts` `relativeTime`, `listRelativeTime`: `RelativeTime`: `timestamp` with relative text. - `apps/web/src/page/kit/resource-not-found.ts` `resourceNotFound` - `apps/web/src/page/kit/route-window.ts` `isWindowChange`, `snapModeOf`, `routeWindowStep`: `useEffectiveTimeRange` + `useRefreshableAtomValue` for a page's `routeChanged`: the window is re-resolved only when its own fields moved, and only Reload revalidates unchanged... - `apps/web/src/page/kit/section-heading.ts` `sectionHeading`: `SectionHeading`: the `title` row and the `eyebrow` overline. diff --git a/packages/views/src/ui/icons.generated.ts b/packages/views/src/ui/icons.generated.ts index 972ed92c0f..3f59ba3750 100644 --- a/packages/views/src/ui/icons.generated.ts +++ b/packages/views/src/ui/icons.generated.ts @@ -21429,85 +21429,4 @@ export const PIXEL_GLYPHS = { "M21 5L21 13", "M19 3L5 3", ], - "gear-2": [ - "M12 23L12 23.0001", - "M20 20L20 20.0001", - "M16 20L8 20", - "M4 20L4 20.0001", - "M18 18H18.01", - "M6 18H6.01", - "M8 16L8 16.0001", - "M10 14L10 14.0001", - "M20 12.0001L12 12.0001", - "M23.0051 11.9951L23.005 11.9951", - "M1.005 11.9951L1.0049 11.9951", - "M10 10L10 10.0001", - "M20 16L20 8", - "M8 8L8 8.0001", - "M4 16L4 8", - "M18 6H18.01", - "M6 6H6.01", - "M20 4L20 4.0001", - "M16 4L8 4", - "M4 4L4 4.0001", - "M12 1L12 1.0001", - ], - "users-2": [ - "M7 4H9", - "M7 10H9", - "M11 6L11 8", - "M5 6L5 8", - "M17.5 4H18.5", - "M17.5 9H18.5", - "M20.5 6L20.5 7", - "M15.5 6L15.5 7", - "M5 14H11", - "M16 13H20", - "M1 18V20H15V18", - "M19 18H22V15", - "M3 16H3.01", - "M13 16H13.01", - ], - rocket: [ - "M13 5H13.01", - "M15 7H15.01", - "M17 9H17.01", - "M19 11H19.01", - "M17 13H17.01", - "M17 20H17.01", - "M4 7H4.01", - "M15 15H15.01", - "M9 9H9.01", - "M11 15H11.01", - "M9 13H9.01", - "M11 7H11.01", - "M15 3H21V9", - "M19 15V18", - "M9 5L6 5", - "M13 17V22H15", - "M7 11L2 11L2 9", - "M5 16H6", - "M8 19L8 18", - "M3 18V21H6", - ], - "pen-writing": [ - "M5 15H5.01", - "M7 13H7.01", - "M9 11H9.01", - "M11 9H11.01", - "M13 7H13.01", - "M15 5H15.01", - "M17 3H17.01", - "M19 5H19.01", - "M9 19H9.01", - "M11 17H11.01", - "M13 21H21", - "M13 15H13.01", - "M15 13H15.01", - "M17 11H17.01", - "M19 9H19.01", - "M21 7H21.01", - "M3 17V21H7", - "M15 9H15.01", - ], } as const satisfies Record> diff --git a/packages/web-data/package.json b/packages/web-data/package.json index 07439ab545..147d3fc269 100644 --- a/packages/web-data/package.json +++ b/packages/web-data/package.json @@ -4,8 +4,6 @@ "type": "module", "sideEffects": false, "exports": { - ".": "./src/index.ts", - "./collections": "./src/collections/index.ts", "./collections/*": "./src/collections/*.ts", "./alerts/*": "./src/alerts/*.ts", "./anomalies/*": "./src/anomalies/*.ts", diff --git a/packages/web-data/src/alerts/overview.ts b/packages/web-data/src/alerts/overview.ts index 1eed696d2d..16f4d90d2b 100644 --- a/packages/web-data/src/alerts/overview.ts +++ b/packages/web-data/src/alerts/overview.ts @@ -41,12 +41,6 @@ export interface AlertsOverviewReady { readonly timelineRange: { readonly min: number; readonly max: number } } -/** The derived overview, in the phases the tab renders distinctly. */ -export type AlertsOverviewData = - | { readonly phase: "loading" } - | { readonly phase: "error"; readonly message: string } - | AlertsOverviewReady - export interface OverviewInputs { readonly rules: ReadonlyArray readonly incidents: ReadonlyArray diff --git a/packages/web-data/src/collections/index.ts b/packages/web-data/src/collections/index.ts deleted file mode 100644 index 8f9c792b43..0000000000 --- a/packages/web-data/src/collections/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Electric shape row schemas and row-to-domain mappers, React-free. Each app builds its own -// collections over them (apps/web: `lib/collections/synced-collections.ts`). -export * from "./alerts" -export * from "./api-keys" -export * from "./dashboards" -export * from "./investigations" -export * from "./shape-parsers" diff --git a/packages/web-data/src/index.ts b/packages/web-data/src/index.ts deleted file mode 100644 index 2928a7879c..0000000000 --- a/packages/web-data/src/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Framework-free data layer used by apps/web. Query modules are imported -// by subpath (`@maple/web-data/warehouse/`); this barrel carries the shared core. -export * from "./warehouse/effect-utils" -export * from "./warehouse/execute-batcher" -export * from "./lib/cache-key" -export * from "./lib/time-range" diff --git a/packages/web-data/src/lib/cache-key.test.ts b/packages/web-data/src/lib/cache-key.test.ts index 6c32d55c35..68deb3fd8c 100644 --- a/packages/web-data/src/lib/cache-key.test.ts +++ b/packages/web-data/src/lib/cache-key.test.ts @@ -1,12 +1,6 @@ import { describe, expect, it } from "vitest" -import { encodeKey, encodeOrgScopedKey, identityFromKey, orgScopedKeyPayload } from "./cache-key" -import { String as Str } from "effect" - -/** Mirrors `ORG_KEY_SEPARATOR` in cache-key.ts — NUL cannot occur in JSON or a Clerk org id. */ -const SEP = "\u0000" -const MALFORMED = `org_1${SEP}not-json` -const SEPARATED = `org_1${SEP}` +import { encodeKey } from "./cache-key" describe("encodeKey", () => { it("is order-insensitive across object keys", () => { @@ -23,76 +17,3 @@ describe("encodeKey", () => { ) }) }) - -describe("identityFromKey", () => { - const key = (orgId: string, input: unknown) => encodeOrgScopedKey(orgId, input) - - it("collapses two keys that differ only in their time window", () => { - const a = key("org_1", { - serviceName: "api", - startTime: "2026-03-08 02:30:00", - endTime: "2026-03-08 14:30:00", - }) - const b = key("org_1", { - serviceName: "api", - startTime: "2026-03-08 02:35:00", - endTime: "2026-03-08 14:35:00", - }) - - expect(a).not.toBe(b) - expect(identityFromKey(a)).toBe(identityFromKey(b)) - }) - - it("strips millisecond-precision and T-separated timestamps too", () => { - const spaced = key("org_1", { at: "2026-03-08 14:30:00" }) - const fractional = key("org_1", { at: "2026-03-08 14:30:00.123" }) - const tSeparated = key("org_1", { at: "2026-03-08T14:30:00" }) - - expect(identityFromKey(fractional)).toBe(identityFromKey(spaced)) - expect(identityFromKey(tSeparated)).toBe(identityFromKey(spaced)) - }) - - it("separates identities across orgs", () => { - const input = { serviceName: "api", startTime: "2026-03-08 02:30:00" } - - expect(identityFromKey(key("org_1", input))).not.toBe(identityFromKey(key("org_2", input))) - }) - - it("separates identities across non-time inputs", () => { - const at = { startTime: "2026-03-08 02:30:00" } - - expect(identityFromKey(key("org_1", { ...at, serviceName: "api" }))).not.toBe( - identityFromKey(key("org_1", { ...at, serviceName: "web" })), - ) - expect(identityFromKey(key("org_1", { ...at, limit: 50 }))).not.toBe( - identityFromKey(key("org_1", { ...at, limit: 100 })), - ) - }) - - it("strips timestamps nested in objects and arrays", () => { - const a = key("org_1", { windows: [{ from: "2026-03-08 02:30:00" }], name: "api" }) - const b = key("org_1", { windows: [{ from: "2026-03-08 09:45:15" }], name: "api" }) - - expect(identityFromKey(a)).toBe(identityFromKey(b)) - }) - - it("keeps a filter value that merely contains a date distinct", () => { - // Only the exact warehouse shape is stripped; a free-text filter that - // happens to mention a date is still part of the query. - const a = key("org_1", { q: "deploy at 2026-03-08 14:30:00 failed" }) - const b = key("org_1", { q: "deploy at 2026-03-09 14:30:00 failed" }) - - expect(identityFromKey(a)).not.toBe(identityFromKey(b)) - }) - - it("falls back to the key itself when the payload is not JSON", () => { - expect(identityFromKey(MALFORMED)).toBe(MALFORMED) - }) - - it("round-trips the org prefix that orgScopedKeyPayload strips", () => { - const identity = identityFromKey(key("org_1", { serviceName: "api" })) - - expect(Str.startsWith(SEPARATED)(identity)).toBe(true) - expect(JSON.parse(orgScopedKeyPayload(identity))).toEqual({ serviceName: "api" }) - }) -}) diff --git a/packages/web-data/src/lib/cache-key.ts b/packages/web-data/src/lib/cache-key.ts index 7a9751d82f..235924bac9 100644 --- a/packages/web-data/src/lib/cache-key.ts +++ b/packages/web-data/src/lib/cache-key.ts @@ -1,32 +1,16 @@ // BOUNDARY-FILE: This module intentionally carries opaque values; callers decode them before domain use. -import { Option, Schema, Array as Arr, Record as Rec, String as Str } from "effect" +import { Schema, Array as Arr, Record as Rec, String as Str } from "effect" import { snapTimestamp } from "./time-range" const encodeJson = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown)) -const decodeJson = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Unknown)) -/** How a leaf string is treated: snapped to the 15s grid, or dropped entirely. */ -type TimestampMode = "snap" | "drop" - -/** Matches the warehouse `YYYY-MM-DD HH:MM:SS[.fff]` shape, with or without a `T`. */ -const TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}(?:\.\d+)?$/ - -const DROPPED = Symbol("dropped") - -function normalize(value: unknown, mode: TimestampMode): unknown { +function normalize(value: unknown): unknown { if (value === null || typeof value !== "object") { - if (typeof value === "string") { - if (mode === "snap") return snapTimestamp(value) - return TIMESTAMP_PATTERN.test(value) ? DROPPED : value - } - return value + return typeof value === "string" ? snapTimestamp(value) : value } if (Arr.isArray(value)) { - return Arr.filter( - Arr.map(value, (entry) => normalize(entry, mode)), - (entry) => entry !== DROPPED, - ) + return Arr.map(value, (entry) => normalize(entry)) } const entries = Arr.filter( @@ -36,19 +20,14 @@ function normalize(value: unknown, mode: TimestampMode): unknown { const normalized: Record = {} for (const [key, entryValue] of entries) { - const entry = normalize(entryValue, mode) - if (entry !== DROPPED) normalized[key] = entry + normalized[key] = normalize(entryValue) } return normalized } -function normalizeForKey(value: unknown): unknown { - return normalize(value, "snap") -} - export function encodeKey(value: unknown): string { - const normalized = normalizeForKey(value) + const normalized = normalize(value) return encodeJson(normalized === undefined ? null : normalized) } @@ -74,44 +53,3 @@ const ORG_KEY_SEPARATOR = "\u0000" export function encodeOrgScopedKey(orgId: string | null | undefined, value: unknown): string { return `${orgId ?? ""}${ORG_KEY_SEPARATOR}${encodeKey(value)}` } - -/** Recover just the encoded input from a key built by `encodeOrgScopedKey`. */ -export function orgScopedKeyPayload(key: string): string { - return Option.match(Str.indexOf(ORG_KEY_SEPARATOR)(key), { - onNone: () => key, - onSome: (separatorIndex) => Str.slice(separatorIndex + 1)(key), - }) -} - -/** - * The *logical* identity of an atom key: the same query, with the time window - * removed. Two keys that differ only in their range collapse to one identity. - * - * This is what lets a remount serve the previous result while the new window - * loads. Snapping the range (see `snapRangeForCache`) keeps a key stable for - * minutes at a time, but the boundary still has to be crossed eventually, and - * on a 1h preset the grid is only 15s — at that point the key is genuinely new - * and only an identity that ignores time can find what was there before. - * - * Derived from the key rather than the input because `Atom.family` factories - * only ever see the key. - * - * The org prefix is preserved deliberately. An identity that spanned orgs would - * revive exactly the bug `ORG_KEY_SEPARATOR` above exists to prevent: switching - * orgs and being shown the previous org's rows. - */ -export function identityFromKey(key: string): string { - const orgPrefix = Option.match(Str.indexOf(ORG_KEY_SEPARATOR)(key), { - onNone: () => "", - onSome: (separatorIndex) => Str.slice(0, separatorIndex)(key), - }) - const payload = orgScopedKeyPayload(key) - - const parsed = decodeJson(payload) - // Not decodable: fall back to the key itself. Identity stays correct - // (never over-shares), it just can't match across windows. - if (Option.isNone(parsed)) return key - - const stripped = normalize(parsed.value, "drop") - return `${orgPrefix}${ORG_KEY_SEPARATOR}${encodeJson(stripped === DROPPED ? null : stripped)}` -} diff --git a/packages/web-data/src/lib/error-messages.test.ts b/packages/web-data/src/lib/error-messages.test.ts index 697f418512..fd1598d468 100644 --- a/packages/web-data/src/lib/error-messages.test.ts +++ b/packages/web-data/src/lib/error-messages.test.ts @@ -7,7 +7,6 @@ import { NetworkErrorTag, UnexpectedErrorTag, displayError, - isAutomaticRetryError, isUnexpectedError, publicError, } from "./error-messages" @@ -158,7 +157,6 @@ describe("displayError", () => { retryable: true, recovery: "retry", }) - expect(isAutomaticRetryError(displayed)).toBe(true) }) it("automatically retries typed transport timeouts declared retryable", () => { @@ -173,9 +171,9 @@ describe("displayError", () => { expect(displayed).toMatchObject({ _tag: "@maple/web/errors/TimeoutError", message: "The API did not respond in time. Try again when you're ready.", + retryable: true, recovery: "retry", }) - expect(isAutomaticRetryError(displayed)).toBe(true) }) it("automatically retries decoded v2 failures only when the body opts in", () => { @@ -186,8 +184,8 @@ describe("displayError", () => { recovery: "retry", }), ) - expect(isAutomaticRetryError(retryable)).toBe(true) - expect(isAutomaticRetryError(displayError(errorEnvelope()))).toBe(false) + expect(retryable.retryable).toBe(true) + expect(displayError(errorEnvelope()).retryable).toBe(false) }) it("does not interpret raw tags or human-readable messages", () => { diff --git a/packages/web-data/src/lib/error-messages.ts b/packages/web-data/src/lib/error-messages.ts index 1681fb861d..1865632cf1 100644 --- a/packages/web-data/src/lib/error-messages.ts +++ b/packages/web-data/src/lib/error-messages.ts @@ -135,6 +135,4 @@ const displayErrorInternal = (input: unknown, depth: number): AnyPublicHttpError /** Resolve any application failure to the single safe public error contract. */ export const displayError = (input: unknown): AnyPublicHttpErrorBody => displayErrorInternal(input, 0) -export const isAutomaticRetryError = (error: AnyPublicHttpErrorBody): boolean => error.retryable - export const isUnexpectedError = (error: AnyPublicHttpErrorBody): boolean => error._tag === UnexpectedErrorTag diff --git a/packages/web-data/src/lib/tool-analytics.test.ts b/packages/web-data/src/lib/tool-analytics.test.ts index 1a024a89c8..b35851df7f 100644 --- a/packages/web-data/src/lib/tool-analytics.test.ts +++ b/packages/web-data/src/lib/tool-analytics.test.ts @@ -17,7 +17,6 @@ import { toolMetricLabel, toolsTableFooter, toolSeriesColors, - toolSeriesMode, type ToolBreakdownRow, type ToolSeriesPoint, } from "./tool-analytics" @@ -32,18 +31,6 @@ const point = ( over: Partial = {}, ): ToolSeriesPoint => ({ bucket, seriesKey, ...measures(over) }) -describe("toolSeriesMode", () => { - it("splits by tool until a tool is picked, then by model, then not at all", () => { - expect(toolSeriesMode(undefined, undefined)).toBe("tools") - expect(toolSeriesMode("run_tests", undefined)).toBe("models") - expect(toolSeriesMode("run_tests", "claude-opus-5")).toBe("single") - }) - - it("splits by tool when only a model is picked — the question is still which tool", () => { - expect(toolSeriesMode(undefined, "claude-opus-5")).toBe("tools") - }) -}) - describe("metricValue", () => { const row = measures({ calls: 200, sessions: 12, errors: 10, p50: 1e9, p90: 4e9, p95: 9e9 }) diff --git a/packages/web-data/src/lib/tool-analytics.ts b/packages/web-data/src/lib/tool-analytics.ts index 1b5056cd08..f05b74d9e4 100644 --- a/packages/web-data/src/lib/tool-analytics.ts +++ b/packages/web-data/src/lib/tool-analytics.ts @@ -145,23 +145,6 @@ export interface ToolErrorOccurrenceRow { export const EMPTY_MEASURES: ToolMeasures = { calls: 0, sessions: 0, errors: 0, p50: 0, p90: 0, p95: 0 } -/* ------------------------------------------------------------------------------------------------- - * Series mode - * -----------------------------------------------------------------------------------------------*/ - -/** - * What the chart splits by, which is decided entirely by how far the scope has - * been narrowed: no tool means the question is still "which tool", a tool with - * no model means "which model runs it", and both means there is one line left. - */ -export type ToolSeriesMode = "tools" | "models" | "single" - -export function toolSeriesMode(tool: string | undefined, model: string | undefined): ToolSeriesMode { - if (tool === undefined) return "tools" - if (model === undefined) return "models" - return "single" -} - /* ------------------------------------------------------------------------------------------------- * Reading a metric off a row * -----------------------------------------------------------------------------------------------*/ diff --git a/packages/web-data/src/warehouse/ai-sessions.ts b/packages/web-data/src/warehouse/ai-sessions.ts index 67ef944bde..244a2f6eff 100644 --- a/packages/web-data/src/warehouse/ai-sessions.ts +++ b/packages/web-data/src/warehouse/ai-sessions.ts @@ -238,7 +238,6 @@ export const getAiSessionSpans = Effect.fn("AiSessions.aiSessionSpans")(function ) return { data: result.data, nextCursor: result.nextCursor } }) -export type AiSessionSpansPage = Effect.Success> const AiSessionSummaryInput = Schema.Struct({ sessionId: Schema.String.check(Schema.isMinLength(1)), diff --git a/packages/web-data/src/warehouse/custom-charts.ts b/packages/web-data/src/warehouse/custom-charts.ts index 3fdc2d25ab..83f80864c0 100644 --- a/packages/web-data/src/warehouse/custom-charts.ts +++ b/packages/web-data/src/warehouse/custom-charts.ts @@ -295,15 +295,6 @@ const CustomChartTimeSeriesInputSchema = Schema.Struct({ export type CustomChartTimeSeriesInput = (typeof CustomChartTimeSeriesInputSchema)["Encoded"] type CustomChartTimeSeriesDecoded = (typeof CustomChartTimeSeriesInputSchema)["Type"] -interface CustomChartTimeSeriesPoint { - bucket: string - series: Record -} - -export interface CustomChartTimeSeriesResponse { - data: CustomChartTimeSeriesPoint[] -} - const tracesMetrics = new Set([ "count", "avg_duration", diff --git a/packages/web-data/src/warehouse/gcp-infra.ts b/packages/web-data/src/warehouse/gcp-infra.ts index ca3cb9ddc6..8db3e483ae 100644 --- a/packages/web-data/src/warehouse/gcp-infra.ts +++ b/packages/web-data/src/warehouse/gcp-infra.ts @@ -195,45 +195,6 @@ export const getGcpCloudSqlStatementSpans = Effect.fn("QueryEngine.getGcpCloudSq return { spans: result.data } }) -const DatabaseInstancesInputSchema = Schema.Struct({ - ...WindowInputSchema.fields, - /** A `db.namespace` the traces query. */ - database: Schema.String, - /** The identity values of each Cloud SQL instance to look in. */ - instances: Schema.Array(Schema.Array(Schema.String)), -}) - -/** - * The PostgreSQL instances that hold a database of the traces' name, from the connections the - * poller stored per database. An instance whose read fails is left out. - */ -export const getGcpCloudSqlInstancesOfDatabase = Effect.fn("QueryEngine.getGcpCloudSqlInstancesOfDatabase")( - function* ({ data }: { data: (typeof DatabaseInstancesInputSchema)["Encoded"] }) { - const { database, instances, ...window } = yield* decodeInput( - DatabaseInstancesInputSchema, - data, - "getGcpCloudSqlInstancesOfDatabase", - ) - const owners = yield* Effect.forEach( - instances, - (keys) => - runWarehouseQuery("gcpCloudSqlDatabases", () => - Effect.gen(function* () { - const client = yield* MapleInternalHttp - return yield* client.queryEngine.gcpCloudSqlDatabases({ - payload: new GcpCloudSqlInstanceRequest({ ...window, keys }), - }) - }), - ).pipe( - Effect.map((result) => (result.databases.includes(database) ? [keys] : [])), - Effect.orElseSucceed(() => []), - ), - { concurrency: 4 }, - ) - return { instances: owners.flat() } - }, -) - /** Cloud SQL's own databases, and PostgreSQL's templates: no application queries them. */ const SYSTEM_DATABASES = new Set(["cloudsqladmin", "template0", "template1", "postgres"]) diff --git a/packages/web-data/src/warehouse/traces.ts b/packages/web-data/src/warehouse/traces.ts index 0e6bca5cd9..f57ac1c7bf 100644 --- a/packages/web-data/src/warehouse/traces.ts +++ b/packages/web-data/src/warehouse/traces.ts @@ -162,23 +162,6 @@ export interface Trace { hasError: boolean } -export interface TracesResponse { - data: Trace[] - meta: { - limit: number - offset: number - /** - * Rows the warehouse page actually produced before the noise filter ran. - * Pagination must advance by pages, not by `data.length`: a filtered page - * returns fewer rows than it consumed, and `scannedCount === limit` — not - * a full `data` array — is what means "more pages exist". - */ - scannedCount: number - /** Noise traces dropped from this page (`scannedCount - data.length`). */ - hiddenCount: number - } -} - /** Fold the singular alias and the array spelling of a facet into one list. */ function oneOrMany( list: readonly T[] | undefined, @@ -656,10 +639,6 @@ interface TracesFacets { } } -export interface TracesFacetsResponse { - data: TracesFacets -} - const GetTracesFacetsInputSchema = Schema.Struct({ startTime: Schema.optional(WarehouseDateTimeString), endTime: Schema.optional(WarehouseDateTimeString), diff --git a/packages/web-data/src/warehouse/users.ts b/packages/web-data/src/warehouse/users.ts index 724c02fdf7..7e1f16b861 100644 --- a/packages/web-data/src/warehouse/users.ts +++ b/packages/web-data/src/warehouse/users.ts @@ -103,7 +103,7 @@ export const getUsersFacets = Effect.fn("Users.facets")(function* ({ data }: { d }) const UserDetailInput = Schema.Struct({ ...UsersWindowInput.fields, userId: EndUserId }) -export type UserDetailInput = typeof UserDetailInput.Encoded +type UserDetailInput = typeof UserDetailInput.Encoded const detailRequest = (input: typeof UserDetailInput.Type) => new UserDetailRequest({ ...windowOf(input), userId: input.userId }) diff --git a/packages/web-data/src/warehouse/web-analytics.ts b/packages/web-data/src/warehouse/web-analytics.ts index aad54e86a9..4d42eb91e6 100644 --- a/packages/web-data/src/warehouse/web-analytics.ts +++ b/packages/web-data/src/warehouse/web-analytics.ts @@ -175,7 +175,7 @@ export interface WebAnalyticsEvent { sessions: number } -export interface WebAnalyticsFacetRow { +interface WebAnalyticsFacetRow { name: string count: number } diff --git a/packages/widgets/tsconfig.json b/packages/widgets/tsconfig.json index 3a83057723..2d7dd47332 100644 --- a/packages/widgets/tsconfig.json +++ b/packages/widgets/tsconfig.json @@ -1,5 +1,5 @@ { - "include": ["**/*.ts", "**/*.tsx"], + "include": ["**/*.ts"], "compilerOptions": { "noUncheckedIndexedAccess": true, "target": "ES2022", From 5d051f8a2543090089e0fe18be517bc8a7585826 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Tue, 10 Nov 2026 04:23:16 +0100 Subject: [PATCH 2/3] chore(web): drop the type of the removed VITE_MAPLE_CAPTURE flag --- apps/web/src/vite-env.d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/web/src/vite-env.d.ts b/apps/web/src/vite-env.d.ts index df9ada2521..953cfd32d7 100644 --- a/apps/web/src/vite-env.d.ts +++ b/apps/web/src/vite-env.d.ts @@ -10,7 +10,6 @@ interface ImportMetaEnv { readonly VITE_ELECTRIC_SYNC_URL?: string readonly VITE_INGEST_URL?: string readonly VITE_MAPLE_AUTH_MODE?: string - readonly VITE_MAPLE_CAPTURE?: string readonly VITE_MAPLE_COOKIE_DOMAIN?: string readonly VITE_MAPLE_ENVIRONMENT?: string readonly VITE_MAPLE_INGEST_KEY?: string From 03adff37e81fee69f5722303d872f25a46422e68 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Tue, 10 Nov 2026 11:17:32 +0100 Subject: [PATCH 3/3] test(query-engine): give the list-nesting property test room on a loaded runner Each run compiles a query of up to 2,000 items. Under the 5s default the test timed out twice in one day on shared CI runners while passing in about a second locally. --- .../ch/queries/list-input-nesting.property.test.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/query-engine/src/ch/queries/list-input-nesting.property.test.ts b/packages/query-engine/src/ch/queries/list-input-nesting.property.test.ts index c0a33ba4d6..9c7ba48e69 100644 --- a/packages/query-engine/src/ch/queries/list-input-nesting.property.test.ts +++ b/packages/query-engine/src/ch/queries/list-input-nesting.property.test.ts @@ -87,8 +87,14 @@ const cases: ReadonlyArray<{ readonly name: string; readonly sql: (n: number) => describe("list inputs never deepen the SQL", () => { for (const { name, sql } of cases) { const twoItems = nestingDepth(sql(2)) - it.prop(name, [listSize], ([n]) => { - expect(nestingDepth(sql(n))).toBeLessThanOrEqual(twoItems) - }) + // Each run compiles a query of up to 2,000 items; a loaded CI runner needs more than the 5s default. + it.prop( + name, + [listSize], + ([n]) => { + expect(nestingDepth(sql(n))).toBeLessThanOrEqual(twoItems) + }, + 30_000, + ) } })