@oxy-hq/sdk 2.9.1 → 2.10.0

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  React SDK for building **custom-app bundles** on the [Oxy](https://oxygen-hq.com)
4
4
  platform. A bundle is a normal Vite + React app that reads from its linked oxy
5
- project — raw SQL, the semantic layer, agents, and procedures — through a
5
+ project — raw SQL, the semantic model, agents, and procedures — through a
6
6
  small set of hooks, plus a couple of drop-in components.
7
7
 
8
8
  > **v2 is a complete rewrite.** The v1 stack (`OxyClient` / `OxySDK`, the
@@ -80,7 +80,7 @@ couldn't already read.
80
80
  | --- | --- |
81
81
  | `OxyAppProvider` | Resolves identity, provides it via context. `fallback` renders while loading; `errorFallback` gets a structured error report. |
82
82
  | `useQuery({ sql })` | Inline SQL → rows. `SELECT`/`WITH` only, 10k-row cap. |
83
- | `useSemanticQuery({ topic, dimensions, measures, … })` | Semantic-layer query compiled by airlayer. |
83
+ | `useSemanticQuery({ topic, dimensions, measures, … })` | Semantic-model query compiled by airlayer. |
84
84
  | `useAgentRun({ agentId })` | `.ask(question)` starts an analytics agent run; streams events over SSE; `.cancel()`. |
85
85
  | `useProcedureRun({ procedureId })` | Start a long-running procedure, poll, cancel (beta). |
86
86
  | `useFunction(name)` | `.invoke(body?)` runs a server-side **Oxy Function** (`functions/<name>.ts`) on oxy's isolate runtime; returns its JSON `Response`. For work the browser shouldn't do — warehouse writes, ELT, external APIs. |
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  // @oxy/sdk - TypeScript SDK for Oxy data platform
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3
- const require_react = require('./react-CDYGAfGA.cjs');
3
+ const require_react = require('./react-Cas_DVKe.cjs');
4
4
  let react = require("react");
5
5
  react = require_react.__toESM(react, 1);
6
6
 
@@ -498,7 +498,7 @@ function worldModelPath(projectId) {
498
498
  * project's `.world-model.yml` display config server-side.
499
499
  *
500
500
  * @remarks
501
- * This returns the raw semantic-layer entity graph. For the higher-level
501
+ * This returns the raw semantic-model entity graph. For the higher-level
502
502
  * node-paradigm interface (`world.metric(id)` speaking `expand` / `explain` /
503
503
  * `size`), use {@link useWorldModel} from `./world-node` instead.
504
504
  */
@@ -807,7 +807,7 @@ function createWorldModel(projectId, fetcher) {
807
807
  * ```
808
808
  *
809
809
  * @remarks
810
- * This is the node-paradigm hook. For the raw semantic-layer entity/measure
810
+ * This is the node-paradigm hook. For the raw semantic-model entity/measure
811
811
  * graph, use {@link useWorldModelGraph} instead.
812
812
  */
813
813
  function useWorldModel() {
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["getOxyAppLogger","apiErrorFromResponse","React","useOxyApp","useOxyApp","React","apiErrorFromResponse","useOxyApp","React"],"sources":["../src/anomalies.ts","../src/custom-app/base64.ts","../src/custom-app/debug.ts","../src/custom-app/metric-tree-fetch.ts","../src/custom-app/metric-tree-hooks.tsx","../src/custom-app/sse.ts","../src/custom-app/world-model-hooks.tsx","../src/custom-app/world-node.tsx","../src/metricTree.ts"],"sourcesContent":["// Anomaly inbox types + client. Surfaces the `/semantic/anomalies*`\n// endpoints — list, scan, status, explain — so SDK consumers can render\n// the same inbox the Oxy IDE uses.\n\nimport type { OxyConfig } from \"./config\";\nimport type { ExplainResult } from \"./metricTree\";\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport type AnomalyStatus = \"new\" | \"acknowledged\" | \"dismissed\";\nexport type AnomalySeverity = \"low\" | \"medium\" | \"high\";\n\n/** One filter pinning an anomaly (or a failed monitor) to a segment. */\nexport interface AnomalyFilter {\n /** Fully-qualified dimension id, e.g. `\"sales_daily.restaurant_id\"`. */\n member: string;\n /** Matched values (OR within a filter). */\n values: string[];\n}\n\n/**\n * One row in the anomaly inbox. Detected by `oxy-metric-monitoring` per\n * `.monitor.yml` entry; upserted by repeat scans so unresolved anomalies\n * stay visible without piling up duplicates.\n */\nexport interface Anomaly {\n id: string;\n workspace_id: string;\n measure: string;\n time_dimension: string;\n granularity: string;\n period_start: string;\n period_end: string;\n observed: number;\n expected: number;\n lower_bound: number;\n upper_bound: number;\n z_score: number;\n severity: AnomalySeverity | string;\n status: AnomalyStatus | string;\n label?: string | null;\n /**\n * Stable key derived from the monitor's filters (e.g.\n * `\"sales_daily.restaurant_id=loc-abc\"`). Empty for chain-wide monitors.\n */\n dimension_key: string;\n /**\n * Raw filters identifying the segment; `null` for chain-wide monitors.\n * Always present on the wire (the server serializes it unconditionally),\n * hence required-nullable rather than optional — same shape as\n * {@link ScanFailure.filters}.\n */\n filters: AnomalyFilter[] | null;\n /**\n * Groups consecutive flagged buckets of one segment into a single event, so a\n * surge spanning Mon/Wed/Thu reads as one problem rather than three. `null`\n * for rows detected before events existed. This is what\n * {@link AnomaliesClient.updateStatusBulk} wants as `eventIds` — a status\n * action applies to the whole event.\n */\n event_id?: string | null;\n /** Cached ExplainResult — populated by `POST /anomalies/:id/explain`. */\n explain_cache?: ExplainResult | null;\n explain_cached_at?: string | null;\n detected_at: string;\n updated_at: string;\n}\n\nexport interface ListAnomaliesOptions {\n status?: AnomalyStatus | string;\n /**\n * Max **events** (server caps at 500, defaults to 100). Every bucket of a\n * returned event comes back, so the row count is `limit × buckets-per-event`.\n * With `order: \"recent\"` it is a plain row limit instead.\n */\n limit?: number;\n /**\n * How many **events** to skip (rows, with `order: \"recent\"`) — same unit as\n * `limit`, so page `n` is `offset: (n - 1) * limit`. Defaults to 0.\n *\n * Bounded: past the server's maximum depth the request is refused with a 400\n * rather than served a repeat of the last reachable page, so a runaway\n * `offset += limit` loop ends loudly instead of spinning. Every response\n * echoes that depth as `max_offset`, so a loop can stop before reaching it.\n */\n offset?: number;\n /**\n * `\"recent\"` returns latest-first (`detected_at DESC`). Omit for the default\n * worst-first ranking by event severity (active events before dismissed).\n */\n order?: \"recent\";\n}\n\nexport interface ListAnomaliesResponse {\n anomalies: Anomaly[];\n /**\n * Total matching the filter across every page — **events** under the default\n * ranking, rows under `order: \"recent\"`. Same unit as `limit`/`offset`, so\n * `Math.ceil(total / limit)` is the page count. Note it will not equal\n * `anomalies.length` under the default ranking even on a single page: each\n * event returns all of its buckets.\n *\n * **Absent** in two cases, and a client that pages has to handle both. Send\n * neither `limit` nor `offset` and you have asked for \"the top N\", so there\n * is no total behind the answer — the field is omitted rather than filled\n * with the page's own length. Pass a `limit` (with `offset: 0` for the first\n * page) to get a real total to loop against.\n *\n * It is also dropped when the count query itself fails: the page rows are\n * already in hand, and the server serves them without their denominator\n * rather than failing a request it could answer. So a page you asked for\n * with `limit` can still come back untotalled — page off `anomalies.length`\n * and `max_offset` in that case rather than treating it as zero.\n */\n total?: number;\n /**\n * The page actually served. `limit` is clamped to 1..=500, so it can come\n * back smaller than you asked for and every page number you compute must\n * divide by this rather than by what you sent. `offset` is *not* clamped —\n * too deep a request is refused with a 400 (see `max_offset`), so this echoes\n * the offset you sent whenever there is a response at all.\n *\n * Optional because a replica still running a pre-paging build emits neither,\n * which is a live shape during a rolling deploy. Fall back to what you asked\n * for rather than doing arithmetic on `undefined`.\n */\n limit?: number;\n offset?: number;\n /**\n * The deepest `offset` the server will serve — past it a request is refused\n * with a 400. Read it rather than hardcoding a copy: a paging loop bounded by\n * this stops cleanly instead of ending on an error.\n */\n max_offset?: number;\n /**\n * Event keys whose buckets were trimmed to the server's per-event cap (50) —\n * an `event_id`, or `ungrouped:<row id>` for a row detected before events\n * existed. For those events `anomalies` holds the worst buckets, not all of\n * them, so a status write should name the event through `updateStatusBulk`'s\n * `eventIds` rather than enumerating the buckets you received.\n *\n * Only meaningful under the default ranking, which pages *events* and\n * returns each whole — there, an absence means complete. With\n * `order: \"recent\"` the page is row-limited, so an event can straddle its\n * boundary instead; this list stays empty and every event should be treated\n * as possibly partial.\n */\n truncated_events?: string[];\n}\n\nexport interface BulkUpdateStatusResponse {\n /** Buckets actually written. Lower than what you sent when a row was\n * deleted, moved out of `onlyStatus`, or belongs to another workspace. */\n updated: number;\n /** Distinct anomalies behind those buckets — events, plus standalone\n * pre-event rows. The unit a UI counts in, and one only the server can\n * compute: naming an event never told you how many buckets it held.\n *\n * An anomaly counts as updated once *any* of its buckets is written. Name\n * events through `eventIds` and that is the whole anomaly; name one bucket\n * of a long chain through `ids` and this still reports `1` while the rest\n * keep their old status. `ids` is for pre-event rows, which hold one bucket\n * each — using it for anything else buys a partial write. */\n events_updated: number;\n}\n\nexport interface ScanOptions {\n /** Override the reference \"now\" date (YYYY-MM-DD) — useful for demos. */\n as_of?: string;\n}\n\n/** One `.monitor.yml` entry that errored during a scan. */\nexport interface ScanFailure {\n measure: string;\n time_dimension: string;\n granularity: string;\n label: string | null;\n /** Segment key for a `group_by`/filtered monitor; empty for chain-wide. */\n dimension_key: string;\n /** Raw filters identifying the segment; null for chain-wide monitors. */\n filters: AnomalyFilter[] | null;\n error: string;\n}\n\nexport interface ScanResponse {\n monitors_scanned: number;\n monitors_failed: number;\n anomalies_persisted: number;\n /**\n * True when the scan is still running server-side (it exceeded the 55 s\n * synchronous window, or a scan started within the last 60 s and this call\n * was debounced). The counts are all `0` in that case — they are NOT a\n * \"nothing found\" result. Refetch with `list()` after a short delay.\n */\n pending: boolean;\n /**\n * Per-monitor failures. Empty array (never absent) on a clean scan and on\n * the `pending` path, where failures aren't known yet.\n */\n failures: ScanFailure[];\n}\n\nexport interface ExplainOptions {\n /** Recompute even when the row already has a cached result. */\n refresh?: boolean;\n}\n\n// ── Client ───────────────────────────────────────────────────────────────────\n\nexport type RequestFn = <T>(endpoint: string, options?: RequestInit) => Promise<T>;\n\n/** Which buckets a write may touch when the caller didn't say. Live statuses\n * for ack/dismiss; all three for a reopen, which exists to reach dismissed\n * ones. */\nfunction defaultScope(status: AnomalyStatus): AnomalyStatus[] {\n return status === \"new\" ? [\"new\", \"acknowledged\", \"dismissed\"] : [\"new\", \"acknowledged\"];\n}\n\n/**\n * Client for `/semantic/anomalies*`. Construct via `OxyClient.anomalies`\n * rather than instantiating directly — the getter wires the request helper\n * so auth, timeout, and branch propagation come along for free.\n *\n * @example\n * ```typescript\n * const { anomalies } = await client.anomalies.list({ status: \"new\" });\n * for (const a of anomalies) {\n * console.log(a.label ?? a.measure, a.severity, a.z_score.toFixed(2));\n * }\n * ```\n */\nexport class AnomaliesClient {\n private readonly request: RequestFn;\n private readonly config: OxyConfig;\n\n constructor(config: OxyConfig, request: RequestFn) {\n this.config = config;\n this.request = request;\n }\n\n private path(suffix: string): string {\n return `/${this.config.projectId}/semantic/anomalies${suffix}`;\n }\n\n private buildQuery(extra: Record<string, string> = {}): string {\n const params: Record<string, string> = { ...extra };\n if (this.config.branch) params.branch = this.config.branch;\n const qs = new URLSearchParams(params).toString();\n return qs ? `?${qs}` : \"\";\n }\n\n /**\n * List anomalies in the inbox, ranked worst-first by event severity (active\n * events before dismissed). Pass `order: \"recent\"` for latest-first.\n *\n * @example\n * ```typescript\n * // Open / unresolved anomalies only\n * const { anomalies } = await client.anomalies.list({ status: \"new\" });\n *\n * // Second page of 25 events\n * const page2 = await client.anomalies.list({ limit: 25, offset: 25 });\n * console.log(`${(page2.offset ?? 25) + 1}+ of ${page2.total ?? \"?\"}`);\n * ```\n */\n async list(options: ListAnomaliesOptions = {}): Promise<ListAnomaliesResponse> {\n const extra: Record<string, string> = {};\n if (options.status) extra.status = options.status;\n // Presence, not truthiness. The server reads \"is this caller paging?\" off\n // whether these params were sent at all, so dropping `offset: 0` on a\n // falsy check would make the first iteration of a paging loop a non-paging\n // request — one that reports `total` as just the rows it returned, ending\n // the loop after a single page.\n if (options.limit !== undefined) extra.limit = String(options.limit);\n if (options.offset !== undefined) extra.offset = String(options.offset);\n if (options.order) extra.order = options.order;\n // No trailing slash before the query — axum 307-redirects \"/anomalies/\"\n // to \"/anomalies\", and the redirect fails CORS preflight in browsers.\n return this.request<ListAnomaliesResponse>(this.path(this.buildQuery(extra)));\n }\n\n /**\n * Trigger a full scan. Iterates every `.monitor.yml` entry in the\n * workspace, runs the detector, and upserts matching rows into the\n * inbox. Returns counts of scanned / failed / persisted.\n *\n * Long-running: the server waits up to 55 s, then returns\n * `pending: true` with zeroed counts while the scan finishes in the\n * background. Always check `pending` before treating `0` as \"nothing\n * found\", and refetch with {@link list} shortly after.\n *\n * @example\n * ```typescript\n * // Scan against a known-good reference date (matches the seed dataset)\n * const result = await client.anomalies.scan({ as_of: \"2025-12-15\" });\n * if (result.pending) {\n * console.log(\"scan still running — refetch shortly\");\n * } else {\n * console.log(`${result.anomalies_persisted} anomalies detected`);\n * }\n * ```\n */\n async scan(options: ScanOptions = {}): Promise<ScanResponse> {\n const extra: Record<string, string> = {};\n if (options.as_of) extra.as_of = options.as_of;\n return this.request<ScanResponse>(this.path(`/scan${this.buildQuery(extra)}`), {\n method: \"POST\"\n });\n }\n\n /**\n * Update an anomaly's status (acknowledge / dismiss / re-open).\n */\n async updateStatus(anomalyId: string, status: AnomalyStatus): Promise<Anomaly> {\n const query = this.buildQuery();\n return this.request<Anomaly>(this.path(`/${encodeURIComponent(anomalyId)}/status${query}`), {\n method: \"POST\",\n body: JSON.stringify({ status })\n });\n }\n\n /**\n * Update many anomalies in one request — the batch form of\n * {@link updateStatus}. Identifiers outside the workspace are skipped rather\n * than erroring, so `updated` (rows written) can be lower than what you sent.\n * At most 2000 identifiers across both lists.\n *\n * **Prefer `eventIds`.** Inbox actions are per *event*, and a list response\n * caps how many buckets it returns per event — so acking the bucket ids you\n * received can leave the tail of a long chain behind, `new`, under a clean\n * success. Naming the event lets the server write all of it. `ids` is for\n * rows with no `event_id` (detected before events existed), which can only\n * be named individually.\n *\n * `onlyStatuses` says which of an event's buckets may move. An event can span\n * statuses, so an unbounded write resurrects buckets that were dismissed on\n * purpose — which is why omitting it takes a scope rather than no bound at\n * all: the live statuses (`[\"new\", \"acknowledged\"]`) for an ack or dismiss,\n * and all three for `status: \"new\"`, since reopening is how a dismissed\n * anomaly comes back. The server applies that same default, so the safe\n * behaviour does not depend on going through this client. Pass `[]` to opt\n * out of the bound entirely.\n *\n * @example\n * ```typescript\n * const { anomalies } = await client.anomalies.list({ status: \"new\", limit: 50, offset: 0 });\n * // Both lists: events by id, and pre-event rows (no `event_id`) by their own.\n * const eventIds = [...new Set(anomalies.flatMap((a) => (a.event_id ? [a.event_id] : [])))];\n * const ids = anomalies.filter((a) => !a.event_id).map((a) => a.id);\n * const { updated } = await client.anomalies.updateStatusBulk(\n * { ids, eventIds, onlyStatuses: [\"new\", \"acknowledged\"] },\n * \"acknowledged\"\n * );\n * ```\n */\n async updateStatusBulk(\n target: { ids?: string[]; eventIds?: string[]; onlyStatuses?: AnomalyStatus[] },\n status: AnomalyStatus\n ): Promise<BulkUpdateStatusResponse> {\n return this.request<BulkUpdateStatusResponse>(this.path(`/status${this.buildQuery()}`), {\n method: \"POST\",\n body: JSON.stringify({\n ids: target.ids ?? [],\n event_ids: target.eventIds ?? [],\n // Defaults to a scope, never to \"no bound\": an empty list tells the\n // server to write every bucket of the named events, dismissed ones\n // included, and that is the single state this design says must not be\n // reversed by accident.\n //\n // Reopening is the exception. `status: \"new\"` is how a dismissed\n // anomaly comes back, so excluding `dismissed` there would make the\n // one call that needs it a silent no-op.\n only_statuses: target.onlyStatuses ?? defaultScope(status),\n status\n })\n });\n }\n\n /**\n * Run the metric-tree `explain` for an anomaly and cache the result on\n * the row. Subsequent calls return the cached `ExplainResult` instantly;\n * pass `{ refresh: true }` to bust the cache and recompute.\n *\n * The uncached path runs a 20-30 s recursive driver search — budget for it\n * (or read `explain_cache` off the row from {@link list} when it's already\n * populated).\n */\n async explain(anomalyId: string, options: ExplainOptions = {}): Promise<ExplainResult> {\n const extra: Record<string, string> = {};\n if (options.refresh) extra.refresh = \"true\";\n return this.request<ExplainResult>(\n this.path(`/${encodeURIComponent(anomalyId)}/explain${this.buildQuery(extra)}`),\n { method: \"POST\" }\n );\n }\n}\n","// Base64 for binary that has to cross a boundary as text — an email\n// attachment's `content`, a `ctx.storage.put` body.\n//\n// These are plain functions, deliberately NOT `btoa`/`atob`:\n//\n// - `btoa` takes a Latin1 STRING. Handing it a `Uint8Array` is the classic\n// footgun: the spec stringifies it, so a PDF starting `%PDF` silently\n// encodes the text \"37,80,68,70\" and you ship a corrupt file. A named\n// function that takes bytes cannot be misused that way.\n// - Being ordinary bundled JS, they behave identically in the Oxy Functions\n// isolate, in Node/vitest, and in a browser. Anything reached through a\n// global risks being a different implementation in each.\n\nconst B64 = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\n/** Reverse lookup; 255 marks \"not a base64 character\". */\nconst B64R = /* @__PURE__ */ (() => {\n const t = new Uint8Array(256).fill(255);\n for (let i = 0; i < 64; i++) t[B64.charCodeAt(i)] = i;\n return t;\n})();\n\n/**\n * Chunk size for building output in segments. Byte-at-a-time `+=` allocates a\n * rope node per byte, and `String.fromCharCode.apply` blows the argument limit\n * on large inputs; 8k avoids both.\n */\nconst CHUNK = 8192;\n\nfunction asBytes(input: Uint8Array | ArrayBuffer | ArrayBufferView): Uint8Array {\n if (input instanceof Uint8Array) return input;\n if (input instanceof ArrayBuffer) return new Uint8Array(input);\n return new Uint8Array(input.buffer, input.byteOffset, input.byteLength);\n}\n\n/**\n * Encode bytes as standard (padded) base64.\n *\n * ```ts\n * const pdf = new Uint8Array(await renderReport());\n * await ctx.email.send({\n * to: ctx.user.email,\n * subject: \"Report\",\n * text: \"attached\",\n * attachments: [{ filename: \"report.pdf\", content: bytesToBase64(pdf) }]\n * });\n * ```\n *\n * For **text** you generated, skip this entirely and pass the string with\n * `encoding: \"utf8\"` — it needs no encoder and stays byte-exact for non-ASCII.\n */\nexport function bytesToBase64(input: Uint8Array | ArrayBuffer | ArrayBufferView): string {\n const bytes = asBytes(input);\n const parts: string[] = [];\n let buf = \"\";\n for (let i = 0; i < bytes.length; i += 3) {\n const b0 = bytes[i];\n const b1 = i + 1 < bytes.length ? bytes[i + 1] : 0;\n const b2 = i + 2 < bytes.length ? bytes[i + 2] : 0;\n const n = (b0 << 16) | (b1 << 8) | b2;\n buf +=\n B64[(n >> 18) & 63] +\n B64[(n >> 12) & 63] +\n (i + 1 < bytes.length ? B64[(n >> 6) & 63] : \"=\") +\n (i + 2 < bytes.length ? B64[n & 63] : \"=\");\n if (buf.length >= CHUNK) {\n parts.push(buf);\n buf = \"\";\n }\n }\n parts.push(buf);\n return parts.join(\"\");\n}\n\n/**\n * Decode standard base64 to bytes — e.g. the body from\n * `ctx.storage.get(key, { encoding: \"base64\" })`.\n *\n * Throws on malformed input rather than returning a short buffer: a truncated\n * decode that reports success is a corrupt file nobody notices.\n */\nexport function base64ToBytes(base64: string): Uint8Array {\n let s = String(base64).replace(/[ \\t\\n\\f\\r]/g, \"\");\n // Strip padding first, and only at a multiple of 4 — matching WHATWG. A\n // decoder that stopped at the first \"=\" would silently truncate\n // `base64ToBytes(chunkA + chunkB)` when chunkA carries its own padding.\n if (s.length % 4 === 0) {\n let pad = 0;\n while (pad < 2 && s.charCodeAt(s.length - 1) === 61 /* = */) {\n s = s.slice(0, -1);\n pad++;\n }\n }\n if (s.indexOf(\"=\") >= 0) {\n throw new TypeError(\"base64ToBytes: '=' may only appear as trailing padding\");\n }\n if (s.length % 4 === 1) {\n throw new TypeError(\"base64ToBytes: invalid base64 length\");\n }\n const out = new Uint8Array((s.length * 3) >> 2);\n let o = 0;\n let buf = 0;\n let bits = 0;\n for (let i = 0; i < s.length; i++) {\n const code = s.charCodeAt(i);\n const v = code < 256 ? B64R[code] : 255;\n if (v === 255) {\n throw new TypeError(`base64ToBytes: invalid base64 character '${s[i]}'`);\n }\n buf = (buf << 6) | v;\n bits += 6;\n if (bits >= 8) {\n bits -= 8;\n out[o++] = (buf >> bits) & 0xff;\n }\n }\n return out.subarray(0, o);\n}\n","// Bundle-side accessor for the server's diagnostic snapshot.\n//\n// `GET /api/customer-apps/<org>/<app>/debug` returns a structured\n// snapshot of what oxy currently sees about a registered customer\n// app: the app row, bundle dir resolution, and parsed manifest (or\n// parse error). Useful when a bundle isn't loading what you expected\n// and you want to verify what the server actually sees — without\n// needing terminal access.\n//\n// The `products` field on the snapshot is a legacy artifact carried\n// for server-side compatibility; in v2 the bundle owns its queries\n// via `useQuery` and the field is always empty for v2 manifests.\n\nimport { getOxyAppLogger } from \"./logger\";\nimport type { ResolvedCustomAppManifest } from \"./manifest\";\n\n/** Untyped at the boundary — keep it loose so server-side schema\n * additions don't break older bundles. Stable enough for inspection\n * but not a contract clients should depend on field-by-field. */\nexport interface CustomAppDebugSnapshot {\n org_slug: string;\n app_slug: string;\n app: {\n id: string;\n slug: string;\n name: string;\n status: string;\n source_type: string;\n project_id: string;\n branch: string;\n };\n bundle_dir: string | null;\n bundle_dir_exists: boolean;\n /** Raw parsed manifest from the server — kept loose so schema additions don't break older bundles. */\n manifest: Record<string, unknown> | null;\n manifest_error: string | null;\n products: Array<{ name: string; producer: string }>;\n}\n\n/**\n * Fetch the server-side diagnostic snapshot for this bundle. Pair with\n * `loadCustomAppManifest()` — pass its result here. Logs the\n * snapshot through the SDK logger so it appears in the bundle's\n * console at info level.\n */\nexport async function getCustomAppDebug(\n resolved: ResolvedCustomAppManifest\n): Promise<CustomAppDebugSnapshot> {\n const log = getOxyAppLogger();\n const { apiBaseUrl, orgSlug, appSlug } = resolved;\n const url =\n `${apiBaseUrl}/api/customer-apps/` +\n `${encodeURIComponent(orgSlug)}/${encodeURIComponent(appSlug)}/debug`;\n\n log.log(\"debug\", \"fetching debug snapshot\", { url });\n const res = await fetch(url, { credentials: \"same-origin\" });\n if (!res.ok) {\n const detail = await res.text().catch(() => \"\");\n throw new Error(\n `Failed to fetch debug snapshot (HTTP ${res.status}): ${detail || res.statusText}`\n );\n }\n const snapshot = (await res.json()) as CustomAppDebugSnapshot;\n log.log(\"info\", \"debug snapshot\", snapshot as unknown as Record<string, unknown>);\n return snapshot;\n}\n","// Shared fetch helpers for the `/api/projects/{id}/semantic/metric-tree*`\n// endpoints. Both the metric-tree analysis hooks (`metric-tree-hooks.tsx`)\n// and the higher-level World Model node interface (`world-node.tsx`) enter\n// the semantic layer through these, so the request envelope (`{ v: 1, … }`)\n// and error decoding stay identical across the two surfaces.\n\nimport { apiErrorFromResponse } from \"./errors\";\nimport type { AppFetcher } from \"./react\";\n\n/** Base path for the metric-tree endpoints of `projectId`. */\nexport function metricTreePath(projectId: string): string {\n return `/api/projects/${projectId}/semantic/metric-tree`;\n}\n\n/** GET `url`, decoding JSON or throwing a typed {@link OxyApiError}. */\nexport async function getJson<Data>(\n fetcher: AppFetcher,\n url: string,\n signal?: AbortSignal\n): Promise<Data> {\n const resp = await fetcher(url, { method: \"GET\", signal });\n if (!resp.ok) throw await apiErrorFromResponse(resp);\n return (await resp.json()) as Data;\n}\n\n/** POST `body` (tagged `v: 1`) to `url`, decoding JSON or throwing. */\nexport async function postJson<Data>(\n fetcher: AppFetcher,\n url: string,\n body: unknown,\n signal?: AbortSignal\n): Promise<Data> {\n const resp = await fetcher(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ v: 1, ...(body as object) }),\n signal\n });\n if (!resp.ok) throw await apiErrorFromResponse(resp);\n return (await resp.json()) as Data;\n}\n","// React hooks for the metric-tree analysis ops, exposed to custom-app\n// bundles so an app can run drivers / what-if / RCA / opportunity sizing\n// without hand-rolling fetch against the semantic layer.\n//\n// These wrap the `/api/projects/{id}/semantic/metric-tree*` endpoints —\n// the same airlayer analyses the IDE's World Model and Metric Tree\n// surfaces drive — behind the shared `OxyAppProvider` fetcher (session\n// cookie in-workspace, dev-proxy token cross-origin). Response shapes\n// reuse the wire types in `../metricTree`, so a bundle typed against a\n// hook result matches what the server serializes verbatim.\n//\n// Pattern mirrors `useSemanticQuery`: each hook fetches when enabled and\n// its input is present, re-runs on input change (deep-compared via JSON),\n// and exposes `refetch`. Read-only inputs (`null`) keep a hook idle — the\n// natural fit for \"run once the user picks a target measure\".\n\nimport * as React from \"react\";\nimport type {\n DistributionRequest,\n ExplainRequest,\n ExplainResult,\n MetricTree,\n OpportunityRequest,\n OpportunityResult,\n PredictChange,\n PredictResult,\n SensitivityResult,\n TimeDimensionsResponse\n} from \"../metricTree\";\nimport { getJson, metricTreePath, postJson } from \"./metric-tree-fetch\";\nimport { useOxyApp } from \"./react\";\n\n/** Shared result envelope for every metric-tree hook. */\nexport interface MetricTreeHookResult<Data> {\n data: Data | null;\n loading: boolean;\n error: Error | null;\n /** Force a re-run, bypassing nothing — the server honors `?refresh`. */\n refetch: () => void;\n}\n\ninterface EndpointOpts {\n /** Set false to skip the request (e.g. waiting on a user selection). */\n enabled?: boolean;\n}\n\n/**\n * Internal engine shared by every metric-tree hook. Runs `run(signal)`\n * whenever `key` changes (and on `refetch`), tracks loading/error, and\n * cancels in-flight work on unmount or input change.\n *\n * `key` is the deep-compare fingerprint of the request; a `null` key\n * means \"no request yet\" and leaves the hook idle without firing.\n */\nfunction useMetricTreeEndpoint<Data>(\n key: string | null,\n run: (signal: AbortSignal) => Promise<Data>,\n enabled: boolean\n): MetricTreeHookResult<Data> {\n const [data, setData] = React.useState<Data | null>(null);\n const [loading, setLoading] = React.useState<boolean>(enabled && key !== null);\n const [error, setError] = React.useState<Error | null>(null);\n const [_nonce, setNonce] = React.useState(0);\n\n // `run` is re-created each render; pin the latest in a ref so the effect\n // depends only on `key`/`enabled`/`nonce` and doesn't re-fire on every\n // parent render.\n const runRef = React.useRef(run);\n runRef.current = run;\n\n React.useEffect(() => {\n if (!enabled || key === null) {\n setLoading(false);\n return;\n }\n const ctrl = new AbortController();\n let cancelled = false;\n setLoading(true);\n setError(null);\n\n runRef\n .current(ctrl.signal)\n .then((result) => {\n if (cancelled) return;\n setData(result);\n setLoading(false);\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n if (err instanceof DOMException && err.name === \"AbortError\") return;\n setError(err instanceof Error ? err : new Error(String(err)));\n setLoading(false);\n });\n\n return () => {\n cancelled = true;\n ctrl.abort();\n };\n }, [key, enabled]);\n\n const refetch = React.useCallback(() => setNonce((n) => n + 1), []);\n return { data, loading, error, refetch };\n}\n\n// ── useMetricTree ─────────────────────────────────────────────────────────────\n\nexport interface UseMetricTreeOpts extends EndpointOpts {\n /** Optional measure id to root the returned subtree at. */\n root?: string;\n}\n\n/**\n * The project's metric tree — measures (nodes) and their component /\n * driver relationships (edges) — or the subtree rooted at `opts.root`.\n * The structural backbone every other metric-tree analysis reads against.\n */\nexport function useMetricTree(opts: UseMetricTreeOpts = {}): MetricTreeHookResult<MetricTree> {\n const { projectId, fetcher } = useOxyApp();\n const enabled = opts.enabled !== false;\n const root = opts.root;\n const key = projectId ? JSON.stringify({ projectId, root }) : null;\n\n return useMetricTreeEndpoint<MetricTree>(\n key,\n (signal) => {\n const qs = root ? `?root=${encodeURIComponent(root)}` : \"\";\n return getJson<MetricTree>(fetcher, `${metricTreePath(projectId as string)}${qs}`, signal);\n },\n enabled\n );\n}\n\n// ── useSensitivity (drivers) ──────────────────────────────────────────────────\n\n/**\n * Ranked drivers of `measureId`, by influence — the \"what moves this\n * measure\" question. Pass `null` to stay idle until a measure is chosen.\n */\nexport function useSensitivity(\n measureId: string | null,\n opts: EndpointOpts = {}\n): MetricTreeHookResult<SensitivityResult> {\n const { projectId, fetcher } = useOxyApp();\n const enabled = opts.enabled !== false;\n const key = projectId && measureId ? JSON.stringify({ projectId, measureId }) : null;\n\n return useMetricTreeEndpoint<SensitivityResult>(\n key,\n (signal) => {\n const path = `${metricTreePath(projectId as string)}/${encodeURIComponent(\n measureId as string\n )}/sensitivity`;\n return getJson<SensitivityResult>(fetcher, path, signal);\n },\n enabled\n );\n}\n\n// ── usePredict (what-if) ──────────────────────────────────────────────────────\n\n/**\n * Propagate hypothetical `(measure, delta)` changes upward through the\n * tree and return the estimated impact on every downstream measure — a\n * pure metric-tree walk, no warehouse query. Pass `null` to stay idle.\n */\nexport function usePredict(\n changes: PredictChange[] | null,\n opts: EndpointOpts = {}\n): MetricTreeHookResult<PredictResult> {\n const { projectId, fetcher } = useOxyApp();\n const enabled = opts.enabled !== false;\n const key = projectId && changes ? JSON.stringify({ projectId, changes }) : null;\n\n return useMetricTreeEndpoint<PredictResult>(\n key,\n (signal) =>\n postJson<PredictResult>(\n fetcher,\n `${metricTreePath(projectId as string)}/predict`,\n { changes },\n signal\n ),\n enabled\n );\n}\n\n// ── useExplain (RCA) ──────────────────────────────────────────────────────────\n\n/**\n * Period-over-period root-cause decomposition: recursively splits the\n * target measure by components and dimensions until the move concentrates.\n * This is the heavy one — it can fire many warehouse queries and the\n * server caps it at 45s. Pass `null` to defer until periods are chosen.\n */\nexport function useExplain(\n request: ExplainRequest | null,\n opts: EndpointOpts = {}\n): MetricTreeHookResult<ExplainResult> {\n const { projectId, fetcher } = useOxyApp();\n const enabled = opts.enabled !== false;\n const key = projectId && request ? JSON.stringify({ projectId, request }) : null;\n\n return useMetricTreeEndpoint<ExplainResult>(\n key,\n (signal) =>\n postJson<ExplainResult>(\n fetcher,\n `${metricTreePath(projectId as string)}/explain`,\n request,\n signal\n ),\n enabled\n );\n}\n\n// ── useDistribution ───────────────────────────────────────────────────────────\n\n/**\n * Single-period distribution of a measure — an {@link ExplainResult}\n * against an auto-derived immediately-prior baseline. Same renderers as\n * `useExplain`; ignore the delta fields for a pure distribution view.\n */\nexport function useDistribution(\n request: DistributionRequest | null,\n opts: EndpointOpts = {}\n): MetricTreeHookResult<ExplainResult> {\n const { projectId, fetcher } = useOxyApp();\n const enabled = opts.enabled !== false;\n const key = projectId && request ? JSON.stringify({ projectId, request }) : null;\n\n return useMetricTreeEndpoint<ExplainResult>(\n key,\n (signal) =>\n postJson<ExplainResult>(\n fetcher,\n `${metricTreePath(projectId as string)}/distribution`,\n request,\n signal\n ),\n enabled\n );\n}\n\n// ── useOpportunity (sizing) ───────────────────────────────────────────────────\n\n/**\n * Segment opportunity sizing for a measure over a period: finds\n * underperforming segments and sizes the addressable upside of closing\n * each rate gap against a benchmark peer. Pass `null` to stay idle until\n * a target + period are chosen.\n */\nexport function useOpportunity(\n request: OpportunityRequest | null,\n opts: EndpointOpts = {}\n): MetricTreeHookResult<OpportunityResult> {\n const { projectId, fetcher } = useOxyApp();\n const enabled = opts.enabled !== false;\n const key = projectId && request ? JSON.stringify({ projectId, request }) : null;\n\n return useMetricTreeEndpoint<OpportunityResult>(\n key,\n (signal) =>\n postJson<OpportunityResult>(\n fetcher,\n `${metricTreePath(projectId as string)}/opportunity`,\n request,\n signal\n ),\n enabled\n );\n}\n\n// ── useTimeDimensions ─────────────────────────────────────────────────────────\n\n/**\n * The queryable time dimensions per view (`view.dim` ids) — what a\n * bundle offers as the period axis for `explain` / `opportunity` /\n * `distribution` instead of hardcoding a curated map.\n */\nexport function useTimeDimensions(\n opts: EndpointOpts = {}\n): MetricTreeHookResult<TimeDimensionsResponse> {\n const { projectId, fetcher } = useOxyApp();\n const enabled = opts.enabled !== false;\n const key = projectId ? JSON.stringify({ projectId, kind: \"time-dimensions\" }) : null;\n\n return useMetricTreeEndpoint<TimeDimensionsResponse>(\n key,\n (signal) =>\n getJson<TimeDimensionsResponse>(\n fetcher,\n `${metricTreePath(projectId as string)}/time-dimensions`,\n signal\n ),\n enabled\n );\n}\n","// Minimal `text/event-stream` reader for the world-model streams.\n//\n// Unlike `function-sse.ts` (which reads a single terminal function result),\n// the world-model `instance-detail` / `measure-breakdown` endpoints emit a\n// sequence of `kind`-tagged JSON events on the default (unnamed) SSE event,\n// terminating with a `{ kind: \"done\" }` frame and then closing the stream.\n// This reader parses each `data:` frame's JSON and hands it to `onEvent`; the\n// caller folds the events into accumulated state. It resolves when the stream\n// closes (or the signal aborts) — the hook decides what \"done\" means.\n\n/**\n * Read a `text/event-stream` response, invoking `onEvent` with each parsed\n * JSON frame. Frames that fail to parse are skipped (a malformed frame must\n * not tear down the whole stream). Resolves when the body closes.\n */\nexport async function readJsonSseStream<E>(\n resp: Response,\n onEvent: (event: E) => void\n): Promise<void> {\n const reader = resp.body?.getReader();\n if (!reader) {\n throw new Error(\"SSE response has no body stream\");\n }\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n let sep: number;\n while ((sep = buffer.indexOf(\"\\n\\n\")) !== -1) {\n const frame = buffer.slice(0, sep);\n buffer = buffer.slice(sep + 2);\n let data = \"\";\n for (const line of frame.split(\"\\n\")) {\n // Ignore `event:`/`id:`/`retry:` lines — the world-model streams put\n // everything in `data:` on the default event.\n if (line.startsWith(\"data:\")) data += line.slice(5).trim();\n }\n if (!data) continue;\n let parsed: E;\n try {\n parsed = JSON.parse(data) as E;\n } catch {\n continue;\n }\n onEvent(parsed);\n }\n }\n}\n","// React hooks for the world-model surface, exposed to custom-app bundles\n// so an app can render the semantic-layer graph, browse an entity's\n// instances, and drill into an instance's detail / measure driver-tree.\n//\n// Wraps the `/api/projects/{id}/semantic/world-model*` endpoints behind the\n// shared `OxyAppProvider` fetcher. The two drill-down endpoints\n// (`instance-detail`, `measure-breakdown`) stream `kind`-tagged SSE events;\n// their hooks fold the stream into accumulated state so a bundle can render\n// progressively (skeletons fill in as measure values resolve).\n\nimport * as React from \"react\";\nimport type {\n WmInstancesResponse,\n WmMeasureBreakdown,\n WmMeasureBreakdownEvent,\n WorldModel\n} from \"../worldModel\";\nimport { apiErrorFromResponse } from \"./errors\";\nimport { useOxyApp } from \"./react\";\nimport { readJsonSseStream } from \"./sse\";\n\n/** Base path for the world-model endpoints of the active project. */\nfunction worldModelPath(projectId: string): string {\n return `/api/projects/${projectId}/semantic/world-model`;\n}\n\n// ── useWorldModelGraph (graph) ────────────────────────────────────────────────\n\nexport interface UseWorldModelGraphResult {\n data: WorldModel | null;\n loading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\n/**\n * The world-model graph — entities (nodes), their measures/dimensions, and\n * how measures promote across the entity hierarchy (edges). Applies the\n * project's `.world-model.yml` display config server-side.\n *\n * @remarks\n * This returns the raw semantic-layer entity graph. For the higher-level\n * node-paradigm interface (`world.metric(id)` speaking `expand` / `explain` /\n * `size`), use {@link useWorldModel} from `./world-node` instead.\n */\nexport function useWorldModelGraph(opts: { enabled?: boolean } = {}): UseWorldModelGraphResult {\n const { projectId, fetcher } = useOxyApp();\n const enabled = opts.enabled !== false;\n const [data, setData] = React.useState<WorldModel | null>(null);\n const [loading, setLoading] = React.useState<boolean>(enabled && !!projectId);\n const [error, setError] = React.useState<Error | null>(null);\n const [_nonce, setNonce] = React.useState(0);\n\n React.useEffect(() => {\n if (!enabled || !projectId) {\n setLoading(false);\n return;\n }\n const ctrl = new AbortController();\n let cancelled = false;\n setLoading(true);\n setError(null);\n fetcher(worldModelPath(projectId), { method: \"GET\", signal: ctrl.signal })\n .then(async (resp) => {\n if (!resp.ok) throw await apiErrorFromResponse(resp);\n return (await resp.json()) as WorldModel;\n })\n .then((result) => {\n if (cancelled) return;\n setData(result);\n setLoading(false);\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n if (err instanceof DOMException && err.name === \"AbortError\") return;\n setError(err instanceof Error ? err : new Error(String(err)));\n setLoading(false);\n });\n return () => {\n cancelled = true;\n ctrl.abort();\n };\n }, [enabled, projectId, fetcher]);\n\n const refetch = React.useCallback(() => setNonce((n) => n + 1), []);\n return { data, loading, error, refetch };\n}\n\n// ── useWorldModelInstances ────────────────────────────────────────────────────\n\nexport interface UseWorldModelInstancesOpts {\n /** Substring/prefix search over the entity's display field. */\n search?: string;\n /** Max rows to return (default 50 server-side). */\n limit?: number;\n enabled?: boolean;\n}\n\nexport interface UseWorldModelInstancesResult {\n data: WmInstancesResponse | null;\n loading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\n/**\n * List the instances (rows) of `entityId` — a bounded, searchable picker\n * over the entity's primary keys + display label. Pass `null` for `entityId`\n * to stay idle until an entity is chosen.\n */\nexport function useWorldModelInstances(\n entityId: string | null,\n opts: UseWorldModelInstancesOpts = {}\n): UseWorldModelInstancesResult {\n const { projectId, fetcher } = useOxyApp();\n const enabled = opts.enabled !== false;\n const { search, limit } = opts;\n const [data, setData] = React.useState<WmInstancesResponse | null>(null);\n const [loading, setLoading] = React.useState<boolean>(enabled && !!projectId && !!entityId);\n const [error, setError] = React.useState<Error | null>(null);\n const [_nonce, setNonce] = React.useState(0);\n\n React.useEffect(() => {\n if (!enabled || !projectId || !entityId) {\n setLoading(false);\n return;\n }\n const ctrl = new AbortController();\n let cancelled = false;\n setLoading(true);\n setError(null);\n const params = new URLSearchParams({ entity: entityId });\n if (search) params.set(\"search\", search);\n if (limit != null) params.set(\"limit\", String(limit));\n fetcher(`${worldModelPath(projectId)}/instances?${params}`, {\n method: \"GET\",\n signal: ctrl.signal\n })\n .then(async (resp) => {\n if (!resp.ok) throw await apiErrorFromResponse(resp);\n return (await resp.json()) as WmInstancesResponse;\n })\n .then((result) => {\n if (cancelled) return;\n setData(result);\n setLoading(false);\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n if (err instanceof DOMException && err.name === \"AbortError\") return;\n setError(err instanceof Error ? err : new Error(String(err)));\n setLoading(false);\n });\n return () => {\n cancelled = true;\n ctrl.abort();\n };\n }, [enabled, projectId, entityId, search, limit, fetcher]);\n\n const refetch = React.useCallback(() => setNonce((n) => n + 1), []);\n return { data, loading, error, refetch };\n}\n\n// ── useMeasureBreakdown (driver tree, SSE) ────────────────────────────────────\n\nexport interface UseMeasureBreakdownResult {\n /** Accumulated breakdown graph; null until the `init` frame. */\n breakdown: WmMeasureBreakdown | null;\n loading: boolean;\n done: boolean;\n error: Error | null;\n}\n\n/** Fold one measure-breakdown SSE frame into the accumulated graph. */\nfunction foldBreakdown(\n prev: WmMeasureBreakdown | null,\n ev: WmMeasureBreakdownEvent\n): WmMeasureBreakdown | null {\n switch (ev.kind) {\n case \"init\":\n return {\n root: ev.root,\n nodes: ev.nodes.map((n) => ({ ...n, value: null, unvalued_reason: null })),\n edges: ev.edges\n };\n case \"value\": {\n if (!prev) return prev;\n return {\n ...prev,\n nodes: prev.nodes.map((n) =>\n n.id === ev.node_id ? { ...n, value: ev.value, unvalued_reason: ev.unvalued_reason } : n\n )\n };\n }\n default:\n return prev;\n }\n}\n\n/**\n * Stream the driver-tree breakdown of one instance's measure — the metric\n * decomposition (add/sub/mul/div component graph) with each node's value\n * filling in as it resolves. This is the per-instance RCA view. Pass `null`\n * for `measure` to stay idle.\n */\nexport function useMeasureBreakdown(\n entityId: string | null,\n keyValue: string | null,\n measure: string | null\n): UseMeasureBreakdownResult {\n const { projectId, fetcher } = useOxyApp();\n const [breakdown, setBreakdown] = React.useState<WmMeasureBreakdown | null>(null);\n const [loading, setLoading] = React.useState<boolean>(false);\n const [done, setDone] = React.useState<boolean>(false);\n const [error, setError] = React.useState<Error | null>(null);\n\n React.useEffect(() => {\n if (!projectId || !entityId || !keyValue || !measure) {\n setLoading(false);\n return;\n }\n const ctrl = new AbortController();\n let cancelled = false;\n setBreakdown(null);\n setLoading(true);\n setDone(false);\n setError(null);\n\n const params = new URLSearchParams({ entity: entityId, key: keyValue, measure });\n fetcher(`${worldModelPath(projectId)}/measure-breakdown?${params}`, {\n method: \"GET\",\n signal: ctrl.signal\n })\n .then(async (resp) => {\n if (!resp.ok) throw await apiErrorFromResponse(resp);\n await readJsonSseStream<WmMeasureBreakdownEvent>(resp, (ev) => {\n if (cancelled) return;\n if (ev.kind === \"done\") {\n setDone(true);\n return;\n }\n setBreakdown((prev) => foldBreakdown(prev, ev));\n });\n if (!cancelled) setLoading(false);\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n if (err instanceof DOMException && err.name === \"AbortError\") return;\n setError(err instanceof Error ? err : new Error(String(err)));\n setLoading(false);\n });\n return () => {\n cancelled = true;\n ctrl.abort();\n };\n }, [projectId, entityId, keyValue, measure, fetcher]);\n\n return { breakdown, loading, done, error };\n}\n","// The World Model **node interface** — the higher-level \"node paradigm\" from\n// `docs/build/sdk/world-model.mdx`. Everything in the World Model is a node,\n// and every node speaks the same verbs: `expand` (one hop of relationships),\n// `drill` (narrow to a segment), `explain` (period-over-period root cause),\n// and `size` (peer-gap opportunity). Render a node, let the user pick a verb,\n// get more nodes back, recurse.\n//\n// This is a thin composition layer over the metric-tree analyses that already\n// ship (`metric-tree-hooks.tsx` / the `MetricTreeClient`): the verbs map onto\n// the same `/semantic/metric-tree*` endpoints, so a bundle typed against a\n// handle matches what the server serializes verbatim. It adds no new backend.\n//\n// The logic lives in a framework-agnostic `createWorldModel(projectId,\n// fetcher)` factory; `useWorldModel()` is a thin `useMemo` over it, scoped to\n// the active `<OxyAppProvider>` project.\n//\n// **Alpha.** Per the doc, the node paradigm is a design preview and may\n// change. `drill` is the one verb the backend cannot yet honor — the\n// metric-tree endpoints take no segment/instance filter (the opportunity\n// endpoint explicitly refuses it), and structural verbs are scope-invariant.\n// So `drill` returns a scoped handle for interface fidelity, but the value\n// verbs (`explain`/`size`) on a drilled handle throw\n// {@link WorldModelScopeUnsupportedError} rather than silently returning\n// population numbers for a scoped question.\n\nimport * as React from \"react\";\nimport type {\n ExplainRequest,\n ExplainResult,\n MetricEdge,\n MetricNode,\n MetricTree,\n OpportunityRequest,\n OpportunityResult,\n SensitivityResult\n} from \"../metricTree\";\nimport { getJson, metricTreePath, postJson } from \"./metric-tree-fetch\";\nimport type { AppFetcher } from \"./react\";\nimport { useOxyApp } from \"./react\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\n/** A `dimension → value` scope narrowed onto a node via {@link MetricHandle.drill}. */\nexport type MetricScope = Readonly<Record<string, string>>;\n\n/** Options for {@link MetricHandle.explain} — an {@link ExplainRequest} minus\n * the `target`, which the handle supplies from its own id. */\nexport type ExplainOpts = Omit<ExplainRequest, \"target\">;\n\n/** Options for {@link MetricHandle.size} — an {@link OpportunityRequest} minus\n * the `target`. */\nexport type SizeOpts = Omit<OpportunityRequest, \"target\">;\n\n/** One child revealed by {@link MetricHandle.expand}: the child measure's\n * node, the edge that connects it to the parent, and a handle to recurse. */\nexport interface ExpandedNode {\n /** The child measure (a component or a driver of the parent). */\n node: MetricNode;\n /** The parent → child edge — `kind`, `direction`, `strength`, `form`, … */\n edge: MetricEdge;\n /** A live handle on the child, carrying the parent's scope. */\n handle: MetricHandle;\n}\n\n/**\n * A live handle on one metric node. Carry it around and call a verb; every\n * verb returns either more nodes (`expand`), a scoped handle (`drill`), or an\n * analysis result (`explain` / `size` / `drivers`).\n */\nexport interface MetricHandle {\n /** Fully-qualified measure id (`view.measure`). */\n readonly id: string;\n /** The scope narrowed onto this handle by `drill` (empty for a root handle). */\n readonly scope: MetricScope;\n /** The measure's own tree node (label, expr, is_composite). */\n node(signal?: AbortSignal): Promise<MetricNode>;\n /** One hop of relationships — the metric's components and drivers as child nodes. */\n expand(signal?: AbortSignal): Promise<ExpandedNode[]>;\n /** The declared drivers of this measure, ranked by influence (sensitivity). */\n drivers(signal?: AbortSignal): Promise<SensitivityResult>;\n /** Root-cause a period-over-period move: why it dropped or climbed. */\n explain(opts: ExplainOpts, signal?: AbortSignal): Promise<ExplainResult>;\n /** Compare this node to its peers across each dimension and size the gap. */\n size(opts: SizeOpts, signal?: AbortSignal): Promise<OpportunityResult>;\n /** Narrow into a segment or entity instance — returns a scoped handle. */\n drill(scope: Record<string, string>): MetricHandle;\n}\n\n/**\n * The World Model interface, scoped to one project. The whole surface hangs\n * off this: grab a {@link MetricHandle} with `metric(id)` and the handle\n * speaks the verbs, or pull the whole graph with `tree(root?)`.\n */\nexport interface WorldModelApi {\n /** The active project id, or `null` before `<OxyAppProvider>` resolves one. */\n readonly projectId: string | null;\n /** The metric tree, rooted anywhere you like (default: the whole tree). */\n tree(root?: string, signal?: AbortSignal): Promise<MetricTree>;\n /** A live handle on one measure node. */\n metric(id: string): MetricHandle;\n}\n\n/**\n * Thrown by the value verbs (`explain` / `size`) when called on a handle that\n * has been `drill`ed. The metric-tree backend cannot yet scope these analyses\n * to a segment, so failing loud beats returning population numbers for a\n * question that asked about one segment.\n */\nexport class WorldModelScopeUnsupportedError extends Error {\n readonly code = \"world_model_scope_unsupported\";\n readonly scope: MetricScope;\n constructor(verb: string, scope: MetricScope) {\n super(\n `${verb} on a drilled (scoped) node is not yet supported by the backend ` +\n `(scope: ${JSON.stringify(scope)}). Call ${verb} on the un-drilled node ` +\n `for population-level analysis.`\n );\n this.name = \"WorldModelScopeUnsupportedError\";\n this.scope = scope;\n }\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Build a {@link WorldModelApi} over a project id and fetcher. Framework-\n * agnostic — `useWorldModel()` wraps this for React, but it is directly\n * unit-testable with a mock fetcher.\n */\nexport function createWorldModel(projectId: string | null, fetcher: AppFetcher): WorldModelApi {\n const base = (): string => {\n if (!projectId) {\n throw new Error(\n \"World Model unavailable: no active project (are you inside <OxyAppProvider>?)\"\n );\n }\n return metricTreePath(projectId);\n };\n\n const tree = (root?: string, signal?: AbortSignal): Promise<MetricTree> => {\n const qs = root ? `?root=${encodeURIComponent(root)}` : \"\";\n return getJson<MetricTree>(fetcher, `${base()}${qs}`, signal);\n };\n\n const makeHandle = (id: string, scope: MetricScope): MetricHandle => {\n const scoped = Object.keys(scope).length > 0;\n return {\n id,\n scope,\n async node(signal) {\n const t = await tree(id, signal);\n const found = t.nodes.find((n) => n.id === id);\n if (!found) throw new Error(`measure '${id}' not found in the metric tree`);\n return found;\n },\n async expand(signal) {\n const t = await tree(id, signal);\n const byId = new Map(t.nodes.map((n) => [n.id, n] as const));\n // `from` is the parent, `to` the child (component/driver) — the same\n // orientation the IDE metric-tree graph lays out top-down.\n const children: ExpandedNode[] = [];\n for (const edge of t.edges) {\n if (edge.from !== id) continue;\n const childNode = byId.get(edge.to);\n if (!childNode) continue;\n children.push({ node: childNode, edge, handle: makeHandle(edge.to, scope) });\n }\n return children;\n },\n drivers(signal) {\n return getJson<SensitivityResult>(\n fetcher,\n `${base()}/${encodeURIComponent(id)}/sensitivity`,\n signal\n );\n },\n explain(opts, signal) {\n if (scoped) throw new WorldModelScopeUnsupportedError(\"explain\", scope);\n return postJson<ExplainResult>(\n fetcher,\n `${base()}/explain`,\n { target: id, ...opts },\n signal\n );\n },\n size(opts, signal) {\n if (scoped) throw new WorldModelScopeUnsupportedError(\"size\", scope);\n return postJson<OpportunityResult>(\n fetcher,\n `${base()}/opportunity`,\n { target: id, ...opts },\n signal\n );\n },\n drill(next) {\n return makeHandle(id, { ...scope, ...next });\n }\n };\n };\n\n return {\n projectId,\n tree,\n metric: (id: string) => makeHandle(id, {})\n };\n}\n\n// ── Hook ──────────────────────────────────────────────────────────────────────\n\n/**\n * The World Model node interface, scoped to the active `<OxyAppProvider>`\n * project. Returns a stable {@link WorldModelApi} — grab a node with\n * `world.metric(id)` and let it speak the verbs.\n *\n * @example\n * ```tsx\n * const world = useWorldModel();\n * const revenue = world.metric(\"orders.net_revenue\");\n * const children = await revenue.expand(); // components + drivers\n * const rca = await revenue.explain({\n * time_dimension: \"orders.order_date\",\n * current_period: [\"2026-06-01\", \"2026-06-30\"],\n * previous_period: [\"2026-05-01\", \"2026-05-31\"],\n * });\n * ```\n *\n * @remarks\n * This is the node-paradigm hook. For the raw semantic-layer entity/measure\n * graph, use {@link useWorldModelGraph} instead.\n */\nexport function useWorldModel(): WorldModelApi {\n const { projectId, fetcher } = useOxyApp();\n return React.useMemo(() => createWorldModel(projectId ?? null, fetcher), [projectId, fetcher]);\n}\n","// Metric-tree types + client. Mirrors `airlayer::engine::metric_tree*`\n// over the `/<project_id>/semantic/metric-tree*` HTTP endpoints. Serde\n// emits snake_case so these field names match the wire format verbatim.\n\nimport type { OxyConfig } from \"./config\";\n\n// ── Tree ──────────────────────────────────────────────────────────────────────\n\nexport type EdgeKind = \"component\" | \"driver\";\nexport type DriverDirection = \"positive\" | \"negative\" | \"unknown\";\nexport type DriverStrength = \"strong\" | \"moderate\" | \"weak\";\nexport type DriverConfidence = \"high\" | \"medium\" | \"low\";\nexport type DriverForm = \"linear\" | \"log-log\" | \"log-linear\" | \"linear-log\";\n\nexport interface MetricNode {\n id: string;\n view: string;\n measure: string;\n label: string;\n description?: string | null;\n measure_type: string;\n is_composite: boolean;\n expr?: string | null;\n}\n\nexport interface MetricEdge {\n from: string;\n to: string;\n kind: EdgeKind;\n /** Sign of a component edge; omitted (defaults to +1) for most edges. */\n sign?: number;\n direction: DriverDirection;\n strength: DriverStrength;\n confidence: DriverConfidence;\n coefficient?: number | null;\n form: DriverForm;\n intercept?: number | null;\n lag?: number | null;\n description?: string | null;\n refs?: string[] | null;\n}\n\nexport interface MetricTree {\n nodes: MetricNode[];\n edges: MetricEdge[];\n root?: string | null;\n}\n\n// ── Sensitivity ──────────────────────────────────────────────────────────────\n\nexport interface SensitivityDriver {\n measure: string;\n path: string[];\n edge_kind: string;\n effective_coefficient?: number | null;\n form?: DriverForm | null;\n direction: DriverDirection;\n strength: DriverStrength;\n lag?: number | null;\n description?: string | null;\n}\n\nexport interface SensitivityResult {\n target: string;\n drivers: SensitivityDriver[];\n}\n\n// ── Predict ──────────────────────────────────────────────────────────────────\n\nexport interface PredictChange {\n measure: string;\n delta: number;\n}\n\nexport interface PredictImpact {\n measure: string;\n estimated_delta: number;\n confidence: string;\n path: string[];\n form: DriverForm;\n lag?: number | null;\n}\n\nexport interface PredictResult {\n inputs: PredictChange[];\n impacts: PredictImpact[];\n}\n\n// ── Explain (RCA) ────────────────────────────────────────────────────────────\n\nexport type SplitKind =\n | { type: \"component\"; child_measure: string }\n | { type: \"dimension\"; dimension: string; value: string }\n | { type: \"uniform_degradation\"; dimension: string; num_elements: number }\n | { type: \"cross_cutting\"; dimension: string; value: string; measures: string[] };\n\nexport interface ExplainSibling {\n split: SplitKind;\n measure: string;\n delta: number;\n root_fraction: number;\n}\n\nexport interface ExplainNode {\n split: SplitKind;\n measure: string;\n filters: unknown[];\n delta: number;\n concentration: number;\n root_fraction: number;\n siblings?: ExplainSibling[];\n dimension_count?: number;\n children?: ExplainNode[];\n}\n\n/** Whether a driver's observed move pushes the target the way it actually\n * moved (`contributing`) or against it (`counteracting` — it offset part of\n * the move rather than causing it). `unknown` when no signed claim is\n * available: `direction: unknown` with no coefficient, or a flat\n * driver/target. */\nexport type DriverContribution = \"contributing\" | \"counteracting\" | \"unknown\";\n\n/** A driver's move split into the part its base forced and the part its own\n * ratio contributed. Emitted only when the driver genuinely tracks a sibling\n * rather than moving on its own — presence is the claim.\n * `base_driven_delta + ratio_driven_delta === driver_delta`. */\nexport interface PassthroughSplit {\n base_measure: string;\n ratio_previous: number;\n ratio_current: number;\n base_driven_delta: number;\n ratio_driven_delta: number;\n}\n\nexport interface DriverAttribution {\n driver_measure: string;\n driver_previous: number;\n driver_current: number;\n driver_delta: number;\n /** Both optional: an `explain_cache` row written before these fields shipped\n * is served verbatim, so absent means unclassified — not a default. */\n direction?: DriverDirection;\n contribution?: DriverContribution;\n coefficient?: number;\n form: DriverForm;\n /** Absent for a purely qualitative driver (no coefficient). */\n estimated_target_impact?: number;\n description?: string;\n passthrough?: PassthroughSplit;\n}\n\nexport type ExplainWarning =\n | {\n type: \"simpsons_paradox\";\n dimension: string;\n aggregate_delta: number;\n segment_directions: [string, number][];\n }\n | {\n type: \"opposing_offset\";\n component_a: string;\n component_b: string;\n delta_a: number;\n delta_b: number;\n }\n | {\n type: \"non_additive_dimension_split\";\n measure: string;\n measure_type: string;\n dimension: string;\n };\n\nexport interface ExplainConfigOverride {\n deep?: boolean;\n max_depth?: number;\n coverage_threshold?: number;\n}\n\nexport interface ExplainRequest {\n target: string;\n time_dimension: string;\n current_period: [string, string];\n previous_period: [string, string];\n config?: ExplainConfigOverride;\n}\n\nexport interface ExplainResult {\n target: string;\n target_delta: number;\n target_previous: number;\n target_current: number;\n time_dimension: string;\n current_period: [string, string];\n previous_period: [string, string];\n nodes: ExplainNode[];\n coverage: number;\n driver_attribution?: DriverAttribution[];\n alternatives?: unknown[];\n warnings?: ExplainWarning[];\n}\n\n// ── Opportunity ──────────────────────────────────────────────────────────────\n\nexport interface SegmentOpportunity {\n segment: string;\n current_value: number;\n volume: number;\n benchmark: number;\n gap: number;\n /** Match-the-best upside in measure units. */\n upside: number;\n}\n\nexport interface DimensionOpportunity {\n dimension: string;\n cardinality: number;\n /** \"best_peer\" or \"p75\". */\n benchmark_basis: string;\n total_upside: number;\n segments: SegmentOpportunity[];\n other_segments_skipped: number;\n}\n\nexport interface SkippedDimension {\n dimension: string;\n reason: string;\n}\n\nexport interface OpportunityRequest {\n target: string;\n time_dimension: string;\n period: [string, string];\n}\n\nexport interface OpportunityResult {\n target: string;\n period: [string, string];\n overall_value: number;\n /**\n * \"rows\" (rate-based additive sizing — the only basis that yields a sized\n * upside figure), \"value_share\" (additive) or \"equal\" (ratios).\n */\n weight_basis: string;\n dimensions: DimensionOpportunity[];\n skipped_dimensions: SkippedDimension[];\n downstream: PredictImpact[];\n}\n\n// ── Distribution ─────────────────────────────────────────────────────────────\n\n/**\n * Single-period structural decomposition. The server auto-derives the\n * baseline as the equal-length window immediately before `period`, then\n * returns an {@link ExplainResult}-shaped payload (so the same renderers\n * work). Ignore the delta fields when rendering a pure distribution.\n */\nexport interface DistributionRequest {\n target: string;\n time_dimension: string;\n /** `[start, end]` inclusive date strings. */\n period: [string, string];\n}\n\n// ── Time dimensions ──────────────────────────────────────────────────────────\n\nexport interface TimeDimensionsResponse {\n /** view name → fully-qualified time-dimension ids (`view.dim`). */\n by_view: Record<string, string[]>;\n}\n\n// ── Client ───────────────────────────────────────────────────────────────────\n\n/**\n * Shape of the inner request helper exposed by `OxyClient`. The metric-tree\n * client reuses it to inherit auth headers, timeout, baseUrl, and project\n * scoping rather than reimplementing fetch end-to-end.\n */\nexport type RequestFn = <T>(endpoint: string, options?: RequestInit) => Promise<T>;\n\n/**\n * Client for the `/semantic/metric-tree*` endpoints. Surfaces the four\n * airlayer metric-tree analyses (tree introspection, sensitivity, predict,\n * explain, opportunity) over typed methods.\n *\n * Construction is internal to {@link OxyClient} — call `client.metricTree`\n * to access an instance rather than building one yourself.\n *\n * @example\n * ```typescript\n * const client = await OxyClient.create({ projectId: \"...\", apiKey: \"...\" });\n * const tree = await client.metricTree.getTree();\n * const drivers = await client.metricTree.getSensitivity(\"orders.net_revenue\");\n * ```\n */\nexport class MetricTreeClient {\n private readonly request: RequestFn;\n private readonly config: OxyConfig;\n\n constructor(config: OxyConfig, request: RequestFn) {\n this.config = config;\n this.request = request;\n }\n\n private path(suffix: string): string {\n return `/${this.config.projectId}/semantic/metric-tree${suffix}`;\n }\n\n private buildQuery(extra: Record<string, string> = {}): string {\n const params: Record<string, string> = { ...extra };\n if (this.config.branch) params.branch = this.config.branch;\n const qs = new URLSearchParams(params).toString();\n return qs ? `?${qs}` : \"\";\n }\n\n /**\n * Fetch the full metric tree, or the subtree rooted at `root`.\n *\n * @param root - Optional fully-qualified measure id to root the tree at.\n * @returns Nodes (measures) and edges (component / driver relationships).\n *\n * @example\n * ```typescript\n * const tree = await client.metricTree.getTree();\n * const subtree = await client.metricTree.getTree(\"orders.net_revenue\");\n * ```\n */\n async getTree(root?: string): Promise<MetricTree> {\n const query = this.buildQuery(root ? { root } : {});\n return this.request<MetricTree>(this.path(query));\n }\n\n /**\n * Rank the declared drivers of a measure by influence.\n *\n * @param measureId - Fully-qualified measure id (`view.measure`).\n *\n * @example\n * ```typescript\n * const sensitivity = await client.metricTree.getSensitivity(\"orders.net_revenue\");\n * for (const driver of sensitivity.drivers) {\n * console.log(driver.measure, driver.direction, driver.strength);\n * }\n * ```\n */\n async getSensitivity(measureId: string): Promise<SensitivityResult> {\n const query = this.buildQuery();\n return this.request<SensitivityResult>(\n this.path(`/${encodeURIComponent(measureId)}/sensitivity${query}`)\n );\n }\n\n /**\n * Propagate hypothetical `(measure, delta)` changes upward through the\n * tree. Returns the estimated impact on every downstream measure.\n *\n * @example\n * ```typescript\n * const result = await client.metricTree.predict([\n * { measure: \"marketing_spend.total_spend\", delta: 10000 },\n * ]);\n * ```\n */\n async predict(changes: PredictChange[]): Promise<PredictResult> {\n const query = this.buildQuery();\n return this.request<PredictResult>(this.path(`/predict${query}`), {\n method: \"POST\",\n body: JSON.stringify({ changes })\n });\n }\n\n /**\n * Period-over-period root-cause decomposition. Recursively splits the\n * target measure by components and dimensions until the move concentrates.\n *\n * @example\n * ```typescript\n * const result = await client.metricTree.explain({\n * target: \"financials.operating_profit\",\n * time_dimension: \"financials.month\",\n * current_period: [\"2025-09-01\", \"2025-09-30\"],\n * previous_period: [\"2025-08-01\", \"2025-08-31\"],\n * });\n * ```\n */\n async explain(request: ExplainRequest): Promise<ExplainResult> {\n const query = this.buildQuery();\n return this.request<ExplainResult>(this.path(`/explain${query}`), {\n method: \"POST\",\n body: JSON.stringify(request)\n });\n }\n\n /**\n * Size the upside opportunity for a measure by finding underperforming\n * segments. Skips high-cardinality dimensions and trims the long tail.\n *\n * @example\n * ```typescript\n * const result = await client.metricTree.findOpportunities({\n * target: \"orders.net_revenue\",\n * time_dimension: \"orders.order_date\",\n * period: [\"2025-09-01\", \"2025-09-30\"],\n * });\n * for (const dim of result.dimensions) {\n * console.log(dim.dimension, \"+\", dim.total_upside);\n * }\n * ```\n */\n async findOpportunities(request: OpportunityRequest): Promise<OpportunityResult> {\n const query = this.buildQuery();\n return this.request<OpportunityResult>(this.path(`/opportunity${query}`), {\n method: \"POST\",\n body: JSON.stringify(request)\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["getOxyAppLogger","apiErrorFromResponse","React","useOxyApp","useOxyApp","React","apiErrorFromResponse","useOxyApp","React"],"sources":["../src/anomalies.ts","../src/custom-app/base64.ts","../src/custom-app/debug.ts","../src/custom-app/metric-tree-fetch.ts","../src/custom-app/metric-tree-hooks.tsx","../src/custom-app/sse.ts","../src/custom-app/world-model-hooks.tsx","../src/custom-app/world-node.tsx","../src/metricTree.ts"],"sourcesContent":["// Anomaly inbox types + client. Surfaces the `/semantic/anomalies*`\n// endpoints — list, scan, status, explain — so SDK consumers can render\n// the same inbox the Oxy IDE uses.\n\nimport type { OxyConfig } from \"./config\";\nimport type { ExplainResult } from \"./metricTree\";\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport type AnomalyStatus = \"new\" | \"acknowledged\" | \"dismissed\";\nexport type AnomalySeverity = \"low\" | \"medium\" | \"high\";\n\n/** One filter pinning an anomaly (or a failed monitor) to a segment. */\nexport interface AnomalyFilter {\n /** Fully-qualified dimension id, e.g. `\"sales_daily.restaurant_id\"`. */\n member: string;\n /** Matched values (OR within a filter). */\n values: string[];\n}\n\n/**\n * One row in the anomaly inbox. Detected by `oxy-metric-monitoring` per\n * `.monitor.yml` entry; upserted by repeat scans so unresolved anomalies\n * stay visible without piling up duplicates.\n */\nexport interface Anomaly {\n id: string;\n workspace_id: string;\n measure: string;\n time_dimension: string;\n granularity: string;\n period_start: string;\n period_end: string;\n observed: number;\n expected: number;\n lower_bound: number;\n upper_bound: number;\n z_score: number;\n severity: AnomalySeverity | string;\n status: AnomalyStatus | string;\n label?: string | null;\n /**\n * Stable key derived from the monitor's filters (e.g.\n * `\"sales_daily.restaurant_id=loc-abc\"`). Empty for chain-wide monitors.\n */\n dimension_key: string;\n /**\n * Raw filters identifying the segment; `null` for chain-wide monitors.\n * Always present on the wire (the server serializes it unconditionally),\n * hence required-nullable rather than optional — same shape as\n * {@link ScanFailure.filters}.\n */\n filters: AnomalyFilter[] | null;\n /**\n * Groups consecutive flagged buckets of one segment into a single event, so a\n * surge spanning Mon/Wed/Thu reads as one problem rather than three. `null`\n * for rows detected before events existed. This is what\n * {@link AnomaliesClient.updateStatusBulk} wants as `eventIds` — a status\n * action applies to the whole event.\n */\n event_id?: string | null;\n /** Cached ExplainResult — populated by `POST /anomalies/:id/explain`. */\n explain_cache?: ExplainResult | null;\n explain_cached_at?: string | null;\n detected_at: string;\n updated_at: string;\n}\n\nexport interface ListAnomaliesOptions {\n status?: AnomalyStatus | string;\n /**\n * Max **events** (server caps at 500, defaults to 100). Every bucket of a\n * returned event comes back, so the row count is `limit × buckets-per-event`.\n * With `order: \"recent\"` it is a plain row limit instead.\n */\n limit?: number;\n /**\n * How many **events** to skip (rows, with `order: \"recent\"`) — same unit as\n * `limit`, so page `n` is `offset: (n - 1) * limit`. Defaults to 0.\n *\n * Bounded: past the server's maximum depth the request is refused with a 400\n * rather than served a repeat of the last reachable page, so a runaway\n * `offset += limit` loop ends loudly instead of spinning. Every response\n * echoes that depth as `max_offset`, so a loop can stop before reaching it.\n */\n offset?: number;\n /**\n * `\"recent\"` returns latest-first (`detected_at DESC`). Omit for the default\n * worst-first ranking by event severity (active events before dismissed).\n */\n order?: \"recent\";\n}\n\nexport interface ListAnomaliesResponse {\n anomalies: Anomaly[];\n /**\n * Total matching the filter across every page — **events** under the default\n * ranking, rows under `order: \"recent\"`. Same unit as `limit`/`offset`, so\n * `Math.ceil(total / limit)` is the page count. Note it will not equal\n * `anomalies.length` under the default ranking even on a single page: each\n * event returns all of its buckets.\n *\n * **Absent** in two cases, and a client that pages has to handle both. Send\n * neither `limit` nor `offset` and you have asked for \"the top N\", so there\n * is no total behind the answer — the field is omitted rather than filled\n * with the page's own length. Pass a `limit` (with `offset: 0` for the first\n * page) to get a real total to loop against.\n *\n * It is also dropped when the count query itself fails: the page rows are\n * already in hand, and the server serves them without their denominator\n * rather than failing a request it could answer. So a page you asked for\n * with `limit` can still come back untotalled — page off `anomalies.length`\n * and `max_offset` in that case rather than treating it as zero.\n */\n total?: number;\n /**\n * The page actually served. `limit` is clamped to 1..=500, so it can come\n * back smaller than you asked for and every page number you compute must\n * divide by this rather than by what you sent. `offset` is *not* clamped —\n * too deep a request is refused with a 400 (see `max_offset`), so this echoes\n * the offset you sent whenever there is a response at all.\n *\n * Optional because a replica still running a pre-paging build emits neither,\n * which is a live shape during a rolling deploy. Fall back to what you asked\n * for rather than doing arithmetic on `undefined`.\n */\n limit?: number;\n offset?: number;\n /**\n * The deepest `offset` the server will serve — past it a request is refused\n * with a 400. Read it rather than hardcoding a copy: a paging loop bounded by\n * this stops cleanly instead of ending on an error.\n */\n max_offset?: number;\n /**\n * Event keys whose buckets were trimmed to the server's per-event cap (50) —\n * an `event_id`, or `ungrouped:<row id>` for a row detected before events\n * existed. For those events `anomalies` holds the worst buckets, not all of\n * them, so a status write should name the event through `updateStatusBulk`'s\n * `eventIds` rather than enumerating the buckets you received.\n *\n * Only meaningful under the default ranking, which pages *events* and\n * returns each whole — there, an absence means complete. With\n * `order: \"recent\"` the page is row-limited, so an event can straddle its\n * boundary instead; this list stays empty and every event should be treated\n * as possibly partial.\n */\n truncated_events?: string[];\n}\n\nexport interface BulkUpdateStatusResponse {\n /** Buckets actually written. Lower than what you sent when a row was\n * deleted, moved out of `onlyStatus`, or belongs to another workspace. */\n updated: number;\n /** Distinct anomalies behind those buckets — events, plus standalone\n * pre-event rows. The unit a UI counts in, and one only the server can\n * compute: naming an event never told you how many buckets it held.\n *\n * An anomaly counts as updated once *any* of its buckets is written. Name\n * events through `eventIds` and that is the whole anomaly; name one bucket\n * of a long chain through `ids` and this still reports `1` while the rest\n * keep their old status. `ids` is for pre-event rows, which hold one bucket\n * each — using it for anything else buys a partial write. */\n events_updated: number;\n}\n\nexport interface ScanOptions {\n /** Override the reference \"now\" date (YYYY-MM-DD) — useful for demos. */\n as_of?: string;\n}\n\n/** One `.monitor.yml` entry that errored during a scan. */\nexport interface ScanFailure {\n measure: string;\n time_dimension: string;\n granularity: string;\n label: string | null;\n /** Segment key for a `group_by`/filtered monitor; empty for chain-wide. */\n dimension_key: string;\n /** Raw filters identifying the segment; null for chain-wide monitors. */\n filters: AnomalyFilter[] | null;\n error: string;\n}\n\nexport interface ScanResponse {\n monitors_scanned: number;\n monitors_failed: number;\n anomalies_persisted: number;\n /**\n * True when the scan is still running server-side (it exceeded the 55 s\n * synchronous window, or a scan started within the last 60 s and this call\n * was debounced). The counts are all `0` in that case — they are NOT a\n * \"nothing found\" result. Refetch with `list()` after a short delay.\n */\n pending: boolean;\n /**\n * Per-monitor failures. Empty array (never absent) on a clean scan and on\n * the `pending` path, where failures aren't known yet.\n */\n failures: ScanFailure[];\n}\n\nexport interface ExplainOptions {\n /** Recompute even when the row already has a cached result. */\n refresh?: boolean;\n}\n\n// ── Client ───────────────────────────────────────────────────────────────────\n\nexport type RequestFn = <T>(endpoint: string, options?: RequestInit) => Promise<T>;\n\n/** Which buckets a write may touch when the caller didn't say. Live statuses\n * for ack/dismiss; all three for a reopen, which exists to reach dismissed\n * ones. */\nfunction defaultScope(status: AnomalyStatus): AnomalyStatus[] {\n return status === \"new\" ? [\"new\", \"acknowledged\", \"dismissed\"] : [\"new\", \"acknowledged\"];\n}\n\n/**\n * Client for `/semantic/anomalies*`. Construct via `OxyClient.anomalies`\n * rather than instantiating directly — the getter wires the request helper\n * so auth, timeout, and branch propagation come along for free.\n *\n * @example\n * ```typescript\n * const { anomalies } = await client.anomalies.list({ status: \"new\" });\n * for (const a of anomalies) {\n * console.log(a.label ?? a.measure, a.severity, a.z_score.toFixed(2));\n * }\n * ```\n */\nexport class AnomaliesClient {\n private readonly request: RequestFn;\n private readonly config: OxyConfig;\n\n constructor(config: OxyConfig, request: RequestFn) {\n this.config = config;\n this.request = request;\n }\n\n private path(suffix: string): string {\n return `/${this.config.projectId}/semantic/anomalies${suffix}`;\n }\n\n private buildQuery(extra: Record<string, string> = {}): string {\n const params: Record<string, string> = { ...extra };\n if (this.config.branch) params.branch = this.config.branch;\n const qs = new URLSearchParams(params).toString();\n return qs ? `?${qs}` : \"\";\n }\n\n /**\n * List anomalies in the inbox, ranked worst-first by event severity (active\n * events before dismissed). Pass `order: \"recent\"` for latest-first.\n *\n * @example\n * ```typescript\n * // Open / unresolved anomalies only\n * const { anomalies } = await client.anomalies.list({ status: \"new\" });\n *\n * // Second page of 25 events\n * const page2 = await client.anomalies.list({ limit: 25, offset: 25 });\n * console.log(`${(page2.offset ?? 25) + 1}+ of ${page2.total ?? \"?\"}`);\n * ```\n */\n async list(options: ListAnomaliesOptions = {}): Promise<ListAnomaliesResponse> {\n const extra: Record<string, string> = {};\n if (options.status) extra.status = options.status;\n // Presence, not truthiness. The server reads \"is this caller paging?\" off\n // whether these params were sent at all, so dropping `offset: 0` on a\n // falsy check would make the first iteration of a paging loop a non-paging\n // request — one that reports `total` as just the rows it returned, ending\n // the loop after a single page.\n if (options.limit !== undefined) extra.limit = String(options.limit);\n if (options.offset !== undefined) extra.offset = String(options.offset);\n if (options.order) extra.order = options.order;\n // No trailing slash before the query — axum 307-redirects \"/anomalies/\"\n // to \"/anomalies\", and the redirect fails CORS preflight in browsers.\n return this.request<ListAnomaliesResponse>(this.path(this.buildQuery(extra)));\n }\n\n /**\n * Trigger a full scan. Iterates every `.monitor.yml` entry in the\n * workspace, runs the detector, and upserts matching rows into the\n * inbox. Returns counts of scanned / failed / persisted.\n *\n * Long-running: the server waits up to 55 s, then returns\n * `pending: true` with zeroed counts while the scan finishes in the\n * background. Always check `pending` before treating `0` as \"nothing\n * found\", and refetch with {@link list} shortly after.\n *\n * @example\n * ```typescript\n * // Scan against a known-good reference date (matches the seed dataset)\n * const result = await client.anomalies.scan({ as_of: \"2025-12-15\" });\n * if (result.pending) {\n * console.log(\"scan still running — refetch shortly\");\n * } else {\n * console.log(`${result.anomalies_persisted} anomalies detected`);\n * }\n * ```\n */\n async scan(options: ScanOptions = {}): Promise<ScanResponse> {\n const extra: Record<string, string> = {};\n if (options.as_of) extra.as_of = options.as_of;\n return this.request<ScanResponse>(this.path(`/scan${this.buildQuery(extra)}`), {\n method: \"POST\"\n });\n }\n\n /**\n * Update an anomaly's status (acknowledge / dismiss / re-open).\n */\n async updateStatus(anomalyId: string, status: AnomalyStatus): Promise<Anomaly> {\n const query = this.buildQuery();\n return this.request<Anomaly>(this.path(`/${encodeURIComponent(anomalyId)}/status${query}`), {\n method: \"POST\",\n body: JSON.stringify({ status })\n });\n }\n\n /**\n * Update many anomalies in one request — the batch form of\n * {@link updateStatus}. Identifiers outside the workspace are skipped rather\n * than erroring, so `updated` (rows written) can be lower than what you sent.\n * At most 2000 identifiers across both lists.\n *\n * **Prefer `eventIds`.** Inbox actions are per *event*, and a list response\n * caps how many buckets it returns per event — so acking the bucket ids you\n * received can leave the tail of a long chain behind, `new`, under a clean\n * success. Naming the event lets the server write all of it. `ids` is for\n * rows with no `event_id` (detected before events existed), which can only\n * be named individually.\n *\n * `onlyStatuses` says which of an event's buckets may move. An event can span\n * statuses, so an unbounded write resurrects buckets that were dismissed on\n * purpose — which is why omitting it takes a scope rather than no bound at\n * all: the live statuses (`[\"new\", \"acknowledged\"]`) for an ack or dismiss,\n * and all three for `status: \"new\"`, since reopening is how a dismissed\n * anomaly comes back. The server applies that same default, so the safe\n * behaviour does not depend on going through this client. Pass `[]` to opt\n * out of the bound entirely.\n *\n * @example\n * ```typescript\n * const { anomalies } = await client.anomalies.list({ status: \"new\", limit: 50, offset: 0 });\n * // Both lists: events by id, and pre-event rows (no `event_id`) by their own.\n * const eventIds = [...new Set(anomalies.flatMap((a) => (a.event_id ? [a.event_id] : [])))];\n * const ids = anomalies.filter((a) => !a.event_id).map((a) => a.id);\n * const { updated } = await client.anomalies.updateStatusBulk(\n * { ids, eventIds, onlyStatuses: [\"new\", \"acknowledged\"] },\n * \"acknowledged\"\n * );\n * ```\n */\n async updateStatusBulk(\n target: { ids?: string[]; eventIds?: string[]; onlyStatuses?: AnomalyStatus[] },\n status: AnomalyStatus\n ): Promise<BulkUpdateStatusResponse> {\n return this.request<BulkUpdateStatusResponse>(this.path(`/status${this.buildQuery()}`), {\n method: \"POST\",\n body: JSON.stringify({\n ids: target.ids ?? [],\n event_ids: target.eventIds ?? [],\n // Defaults to a scope, never to \"no bound\": an empty list tells the\n // server to write every bucket of the named events, dismissed ones\n // included, and that is the single state this design says must not be\n // reversed by accident.\n //\n // Reopening is the exception. `status: \"new\"` is how a dismissed\n // anomaly comes back, so excluding `dismissed` there would make the\n // one call that needs it a silent no-op.\n only_statuses: target.onlyStatuses ?? defaultScope(status),\n status\n })\n });\n }\n\n /**\n * Run the metric-tree `explain` for an anomaly and cache the result on\n * the row. Subsequent calls return the cached `ExplainResult` instantly;\n * pass `{ refresh: true }` to bust the cache and recompute.\n *\n * The uncached path runs a 20-30 s recursive driver search — budget for it\n * (or read `explain_cache` off the row from {@link list} when it's already\n * populated).\n */\n async explain(anomalyId: string, options: ExplainOptions = {}): Promise<ExplainResult> {\n const extra: Record<string, string> = {};\n if (options.refresh) extra.refresh = \"true\";\n return this.request<ExplainResult>(\n this.path(`/${encodeURIComponent(anomalyId)}/explain${this.buildQuery(extra)}`),\n { method: \"POST\" }\n );\n }\n}\n","// Base64 for binary that has to cross a boundary as text — an email\n// attachment's `content`, a `ctx.storage.put` body.\n//\n// These are plain functions, deliberately NOT `btoa`/`atob`:\n//\n// - `btoa` takes a Latin1 STRING. Handing it a `Uint8Array` is the classic\n// footgun: the spec stringifies it, so a PDF starting `%PDF` silently\n// encodes the text \"37,80,68,70\" and you ship a corrupt file. A named\n// function that takes bytes cannot be misused that way.\n// - Being ordinary bundled JS, they behave identically in the Oxy Functions\n// isolate, in Node/vitest, and in a browser. Anything reached through a\n// global risks being a different implementation in each.\n\nconst B64 = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\n/** Reverse lookup; 255 marks \"not a base64 character\". */\nconst B64R = /* @__PURE__ */ (() => {\n const t = new Uint8Array(256).fill(255);\n for (let i = 0; i < 64; i++) t[B64.charCodeAt(i)] = i;\n return t;\n})();\n\n/**\n * Chunk size for building output in segments. Byte-at-a-time `+=` allocates a\n * rope node per byte, and `String.fromCharCode.apply` blows the argument limit\n * on large inputs; 8k avoids both.\n */\nconst CHUNK = 8192;\n\nfunction asBytes(input: Uint8Array | ArrayBuffer | ArrayBufferView): Uint8Array {\n if (input instanceof Uint8Array) return input;\n if (input instanceof ArrayBuffer) return new Uint8Array(input);\n return new Uint8Array(input.buffer, input.byteOffset, input.byteLength);\n}\n\n/**\n * Encode bytes as standard (padded) base64.\n *\n * ```ts\n * const pdf = new Uint8Array(await renderReport());\n * await ctx.email.send({\n * to: ctx.user.email,\n * subject: \"Report\",\n * text: \"attached\",\n * attachments: [{ filename: \"report.pdf\", content: bytesToBase64(pdf) }]\n * });\n * ```\n *\n * For **text** you generated, skip this entirely and pass the string with\n * `encoding: \"utf8\"` — it needs no encoder and stays byte-exact for non-ASCII.\n */\nexport function bytesToBase64(input: Uint8Array | ArrayBuffer | ArrayBufferView): string {\n const bytes = asBytes(input);\n const parts: string[] = [];\n let buf = \"\";\n for (let i = 0; i < bytes.length; i += 3) {\n const b0 = bytes[i];\n const b1 = i + 1 < bytes.length ? bytes[i + 1] : 0;\n const b2 = i + 2 < bytes.length ? bytes[i + 2] : 0;\n const n = (b0 << 16) | (b1 << 8) | b2;\n buf +=\n B64[(n >> 18) & 63] +\n B64[(n >> 12) & 63] +\n (i + 1 < bytes.length ? B64[(n >> 6) & 63] : \"=\") +\n (i + 2 < bytes.length ? B64[n & 63] : \"=\");\n if (buf.length >= CHUNK) {\n parts.push(buf);\n buf = \"\";\n }\n }\n parts.push(buf);\n return parts.join(\"\");\n}\n\n/**\n * Decode standard base64 to bytes — e.g. the body from\n * `ctx.storage.get(key, { encoding: \"base64\" })`.\n *\n * Throws on malformed input rather than returning a short buffer: a truncated\n * decode that reports success is a corrupt file nobody notices.\n */\nexport function base64ToBytes(base64: string): Uint8Array {\n let s = String(base64).replace(/[ \\t\\n\\f\\r]/g, \"\");\n // Strip padding first, and only at a multiple of 4 — matching WHATWG. A\n // decoder that stopped at the first \"=\" would silently truncate\n // `base64ToBytes(chunkA + chunkB)` when chunkA carries its own padding.\n if (s.length % 4 === 0) {\n let pad = 0;\n while (pad < 2 && s.charCodeAt(s.length - 1) === 61 /* = */) {\n s = s.slice(0, -1);\n pad++;\n }\n }\n if (s.indexOf(\"=\") >= 0) {\n throw new TypeError(\"base64ToBytes: '=' may only appear as trailing padding\");\n }\n if (s.length % 4 === 1) {\n throw new TypeError(\"base64ToBytes: invalid base64 length\");\n }\n const out = new Uint8Array((s.length * 3) >> 2);\n let o = 0;\n let buf = 0;\n let bits = 0;\n for (let i = 0; i < s.length; i++) {\n const code = s.charCodeAt(i);\n const v = code < 256 ? B64R[code] : 255;\n if (v === 255) {\n throw new TypeError(`base64ToBytes: invalid base64 character '${s[i]}'`);\n }\n buf = (buf << 6) | v;\n bits += 6;\n if (bits >= 8) {\n bits -= 8;\n out[o++] = (buf >> bits) & 0xff;\n }\n }\n return out.subarray(0, o);\n}\n","// Bundle-side accessor for the server's diagnostic snapshot.\n//\n// `GET /api/customer-apps/<org>/<app>/debug` returns a structured\n// snapshot of what oxy currently sees about a registered customer\n// app: the app row, bundle dir resolution, and parsed manifest (or\n// parse error). Useful when a bundle isn't loading what you expected\n// and you want to verify what the server actually sees — without\n// needing terminal access.\n//\n// The `products` field on the snapshot is a legacy artifact carried\n// for server-side compatibility; in v2 the bundle owns its queries\n// via `useQuery` and the field is always empty for v2 manifests.\n\nimport { getOxyAppLogger } from \"./logger\";\nimport type { ResolvedCustomAppManifest } from \"./manifest\";\n\n/** Untyped at the boundary — keep it loose so server-side schema\n * additions don't break older bundles. Stable enough for inspection\n * but not a contract clients should depend on field-by-field. */\nexport interface CustomAppDebugSnapshot {\n org_slug: string;\n app_slug: string;\n app: {\n id: string;\n slug: string;\n name: string;\n status: string;\n source_type: string;\n project_id: string;\n branch: string;\n };\n bundle_dir: string | null;\n bundle_dir_exists: boolean;\n /** Raw parsed manifest from the server — kept loose so schema additions don't break older bundles. */\n manifest: Record<string, unknown> | null;\n manifest_error: string | null;\n products: Array<{ name: string; producer: string }>;\n}\n\n/**\n * Fetch the server-side diagnostic snapshot for this bundle. Pair with\n * `loadCustomAppManifest()` — pass its result here. Logs the\n * snapshot through the SDK logger so it appears in the bundle's\n * console at info level.\n */\nexport async function getCustomAppDebug(\n resolved: ResolvedCustomAppManifest\n): Promise<CustomAppDebugSnapshot> {\n const log = getOxyAppLogger();\n const { apiBaseUrl, orgSlug, appSlug } = resolved;\n const url =\n `${apiBaseUrl}/api/customer-apps/` +\n `${encodeURIComponent(orgSlug)}/${encodeURIComponent(appSlug)}/debug`;\n\n log.log(\"debug\", \"fetching debug snapshot\", { url });\n const res = await fetch(url, { credentials: \"same-origin\" });\n if (!res.ok) {\n const detail = await res.text().catch(() => \"\");\n throw new Error(\n `Failed to fetch debug snapshot (HTTP ${res.status}): ${detail || res.statusText}`\n );\n }\n const snapshot = (await res.json()) as CustomAppDebugSnapshot;\n log.log(\"info\", \"debug snapshot\", snapshot as unknown as Record<string, unknown>);\n return snapshot;\n}\n","// Shared fetch helpers for the `/api/projects/{id}/semantic/metric-tree*`\n// endpoints. Both the metric-tree analysis hooks (`metric-tree-hooks.tsx`)\n// and the higher-level World Model node interface (`world-node.tsx`) enter\n// the semantic model through these, so the request envelope (`{ v: 1, … }`)\n// and error decoding stay identical across the two surfaces.\n\nimport { apiErrorFromResponse } from \"./errors\";\nimport type { AppFetcher } from \"./react\";\n\n/** Base path for the metric-tree endpoints of `projectId`. */\nexport function metricTreePath(projectId: string): string {\n return `/api/projects/${projectId}/semantic/metric-tree`;\n}\n\n/** GET `url`, decoding JSON or throwing a typed {@link OxyApiError}. */\nexport async function getJson<Data>(\n fetcher: AppFetcher,\n url: string,\n signal?: AbortSignal\n): Promise<Data> {\n const resp = await fetcher(url, { method: \"GET\", signal });\n if (!resp.ok) throw await apiErrorFromResponse(resp);\n return (await resp.json()) as Data;\n}\n\n/** POST `body` (tagged `v: 1`) to `url`, decoding JSON or throwing. */\nexport async function postJson<Data>(\n fetcher: AppFetcher,\n url: string,\n body: unknown,\n signal?: AbortSignal\n): Promise<Data> {\n const resp = await fetcher(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ v: 1, ...(body as object) }),\n signal\n });\n if (!resp.ok) throw await apiErrorFromResponse(resp);\n return (await resp.json()) as Data;\n}\n","// React hooks for the metric-tree analysis ops, exposed to custom-app\n// bundles so an app can run drivers / what-if / RCA / opportunity sizing\n// without hand-rolling fetch against the semantic model.\n//\n// These wrap the `/api/projects/{id}/semantic/metric-tree*` endpoints —\n// the same airlayer analyses the IDE's World Model and Metric Tree\n// surfaces drive — behind the shared `OxyAppProvider` fetcher (session\n// cookie in-workspace, dev-proxy token cross-origin). Response shapes\n// reuse the wire types in `../metricTree`, so a bundle typed against a\n// hook result matches what the server serializes verbatim.\n//\n// Pattern mirrors `useSemanticQuery`: each hook fetches when enabled and\n// its input is present, re-runs on input change (deep-compared via JSON),\n// and exposes `refetch`. Read-only inputs (`null`) keep a hook idle — the\n// natural fit for \"run once the user picks a target measure\".\n\nimport * as React from \"react\";\nimport type {\n DistributionRequest,\n ExplainRequest,\n ExplainResult,\n MetricTree,\n OpportunityRequest,\n OpportunityResult,\n PredictChange,\n PredictResult,\n SensitivityResult,\n TimeDimensionsResponse\n} from \"../metricTree\";\nimport { getJson, metricTreePath, postJson } from \"./metric-tree-fetch\";\nimport { useOxyApp } from \"./react\";\n\n/** Shared result envelope for every metric-tree hook. */\nexport interface MetricTreeHookResult<Data> {\n data: Data | null;\n loading: boolean;\n error: Error | null;\n /** Force a re-run, bypassing nothing — the server honors `?refresh`. */\n refetch: () => void;\n}\n\ninterface EndpointOpts {\n /** Set false to skip the request (e.g. waiting on a user selection). */\n enabled?: boolean;\n}\n\n/**\n * Internal engine shared by every metric-tree hook. Runs `run(signal)`\n * whenever `key` changes (and on `refetch`), tracks loading/error, and\n * cancels in-flight work on unmount or input change.\n *\n * `key` is the deep-compare fingerprint of the request; a `null` key\n * means \"no request yet\" and leaves the hook idle without firing.\n */\nfunction useMetricTreeEndpoint<Data>(\n key: string | null,\n run: (signal: AbortSignal) => Promise<Data>,\n enabled: boolean\n): MetricTreeHookResult<Data> {\n const [data, setData] = React.useState<Data | null>(null);\n const [loading, setLoading] = React.useState<boolean>(enabled && key !== null);\n const [error, setError] = React.useState<Error | null>(null);\n const [_nonce, setNonce] = React.useState(0);\n\n // `run` is re-created each render; pin the latest in a ref so the effect\n // depends only on `key`/`enabled`/`nonce` and doesn't re-fire on every\n // parent render.\n const runRef = React.useRef(run);\n runRef.current = run;\n\n React.useEffect(() => {\n if (!enabled || key === null) {\n setLoading(false);\n return;\n }\n const ctrl = new AbortController();\n let cancelled = false;\n setLoading(true);\n setError(null);\n\n runRef\n .current(ctrl.signal)\n .then((result) => {\n if (cancelled) return;\n setData(result);\n setLoading(false);\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n if (err instanceof DOMException && err.name === \"AbortError\") return;\n setError(err instanceof Error ? err : new Error(String(err)));\n setLoading(false);\n });\n\n return () => {\n cancelled = true;\n ctrl.abort();\n };\n }, [key, enabled]);\n\n const refetch = React.useCallback(() => setNonce((n) => n + 1), []);\n return { data, loading, error, refetch };\n}\n\n// ── useMetricTree ─────────────────────────────────────────────────────────────\n\nexport interface UseMetricTreeOpts extends EndpointOpts {\n /** Optional measure id to root the returned subtree at. */\n root?: string;\n}\n\n/**\n * The project's metric tree — measures (nodes) and their component /\n * driver relationships (edges) — or the subtree rooted at `opts.root`.\n * The structural backbone every other metric-tree analysis reads against.\n */\nexport function useMetricTree(opts: UseMetricTreeOpts = {}): MetricTreeHookResult<MetricTree> {\n const { projectId, fetcher } = useOxyApp();\n const enabled = opts.enabled !== false;\n const root = opts.root;\n const key = projectId ? JSON.stringify({ projectId, root }) : null;\n\n return useMetricTreeEndpoint<MetricTree>(\n key,\n (signal) => {\n const qs = root ? `?root=${encodeURIComponent(root)}` : \"\";\n return getJson<MetricTree>(fetcher, `${metricTreePath(projectId as string)}${qs}`, signal);\n },\n enabled\n );\n}\n\n// ── useSensitivity (drivers) ──────────────────────────────────────────────────\n\n/**\n * Ranked drivers of `measureId`, by influence — the \"what moves this\n * measure\" question. Pass `null` to stay idle until a measure is chosen.\n */\nexport function useSensitivity(\n measureId: string | null,\n opts: EndpointOpts = {}\n): MetricTreeHookResult<SensitivityResult> {\n const { projectId, fetcher } = useOxyApp();\n const enabled = opts.enabled !== false;\n const key = projectId && measureId ? JSON.stringify({ projectId, measureId }) : null;\n\n return useMetricTreeEndpoint<SensitivityResult>(\n key,\n (signal) => {\n const path = `${metricTreePath(projectId as string)}/${encodeURIComponent(\n measureId as string\n )}/sensitivity`;\n return getJson<SensitivityResult>(fetcher, path, signal);\n },\n enabled\n );\n}\n\n// ── usePredict (what-if) ──────────────────────────────────────────────────────\n\n/**\n * Propagate hypothetical `(measure, delta)` changes upward through the\n * tree and return the estimated impact on every downstream measure — a\n * pure metric-tree walk, no warehouse query. Pass `null` to stay idle.\n */\nexport function usePredict(\n changes: PredictChange[] | null,\n opts: EndpointOpts = {}\n): MetricTreeHookResult<PredictResult> {\n const { projectId, fetcher } = useOxyApp();\n const enabled = opts.enabled !== false;\n const key = projectId && changes ? JSON.stringify({ projectId, changes }) : null;\n\n return useMetricTreeEndpoint<PredictResult>(\n key,\n (signal) =>\n postJson<PredictResult>(\n fetcher,\n `${metricTreePath(projectId as string)}/predict`,\n { changes },\n signal\n ),\n enabled\n );\n}\n\n// ── useExplain (RCA) ──────────────────────────────────────────────────────────\n\n/**\n * Period-over-period root-cause decomposition: recursively splits the\n * target measure by components and dimensions until the move concentrates.\n * This is the heavy one — it can fire many warehouse queries and the\n * server caps it at 45s. Pass `null` to defer until periods are chosen.\n */\nexport function useExplain(\n request: ExplainRequest | null,\n opts: EndpointOpts = {}\n): MetricTreeHookResult<ExplainResult> {\n const { projectId, fetcher } = useOxyApp();\n const enabled = opts.enabled !== false;\n const key = projectId && request ? JSON.stringify({ projectId, request }) : null;\n\n return useMetricTreeEndpoint<ExplainResult>(\n key,\n (signal) =>\n postJson<ExplainResult>(\n fetcher,\n `${metricTreePath(projectId as string)}/explain`,\n request,\n signal\n ),\n enabled\n );\n}\n\n// ── useDistribution ───────────────────────────────────────────────────────────\n\n/**\n * Single-period distribution of a measure — an {@link ExplainResult}\n * against an auto-derived immediately-prior baseline. Same renderers as\n * `useExplain`; ignore the delta fields for a pure distribution view.\n */\nexport function useDistribution(\n request: DistributionRequest | null,\n opts: EndpointOpts = {}\n): MetricTreeHookResult<ExplainResult> {\n const { projectId, fetcher } = useOxyApp();\n const enabled = opts.enabled !== false;\n const key = projectId && request ? JSON.stringify({ projectId, request }) : null;\n\n return useMetricTreeEndpoint<ExplainResult>(\n key,\n (signal) =>\n postJson<ExplainResult>(\n fetcher,\n `${metricTreePath(projectId as string)}/distribution`,\n request,\n signal\n ),\n enabled\n );\n}\n\n// ── useOpportunity (sizing) ───────────────────────────────────────────────────\n\n/**\n * Segment opportunity sizing for a measure over a period: finds\n * underperforming segments and sizes the addressable upside of closing\n * each rate gap against a benchmark peer. Pass `null` to stay idle until\n * a target + period are chosen.\n */\nexport function useOpportunity(\n request: OpportunityRequest | null,\n opts: EndpointOpts = {}\n): MetricTreeHookResult<OpportunityResult> {\n const { projectId, fetcher } = useOxyApp();\n const enabled = opts.enabled !== false;\n const key = projectId && request ? JSON.stringify({ projectId, request }) : null;\n\n return useMetricTreeEndpoint<OpportunityResult>(\n key,\n (signal) =>\n postJson<OpportunityResult>(\n fetcher,\n `${metricTreePath(projectId as string)}/opportunity`,\n request,\n signal\n ),\n enabled\n );\n}\n\n// ── useTimeDimensions ─────────────────────────────────────────────────────────\n\n/**\n * The queryable time dimensions per view (`view.dim` ids) — what a\n * bundle offers as the period axis for `explain` / `opportunity` /\n * `distribution` instead of hardcoding a curated map.\n */\nexport function useTimeDimensions(\n opts: EndpointOpts = {}\n): MetricTreeHookResult<TimeDimensionsResponse> {\n const { projectId, fetcher } = useOxyApp();\n const enabled = opts.enabled !== false;\n const key = projectId ? JSON.stringify({ projectId, kind: \"time-dimensions\" }) : null;\n\n return useMetricTreeEndpoint<TimeDimensionsResponse>(\n key,\n (signal) =>\n getJson<TimeDimensionsResponse>(\n fetcher,\n `${metricTreePath(projectId as string)}/time-dimensions`,\n signal\n ),\n enabled\n );\n}\n","// Minimal `text/event-stream` reader for the world-model streams.\n//\n// Unlike `function-sse.ts` (which reads a single terminal function result),\n// the world-model `instance-detail` / `measure-breakdown` endpoints emit a\n// sequence of `kind`-tagged JSON events on the default (unnamed) SSE event,\n// terminating with a `{ kind: \"done\" }` frame and then closing the stream.\n// This reader parses each `data:` frame's JSON and hands it to `onEvent`; the\n// caller folds the events into accumulated state. It resolves when the stream\n// closes (or the signal aborts) — the hook decides what \"done\" means.\n\n/**\n * Read a `text/event-stream` response, invoking `onEvent` with each parsed\n * JSON frame. Frames that fail to parse are skipped (a malformed frame must\n * not tear down the whole stream). Resolves when the body closes.\n */\nexport async function readJsonSseStream<E>(\n resp: Response,\n onEvent: (event: E) => void\n): Promise<void> {\n const reader = resp.body?.getReader();\n if (!reader) {\n throw new Error(\"SSE response has no body stream\");\n }\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n let sep: number;\n while ((sep = buffer.indexOf(\"\\n\\n\")) !== -1) {\n const frame = buffer.slice(0, sep);\n buffer = buffer.slice(sep + 2);\n let data = \"\";\n for (const line of frame.split(\"\\n\")) {\n // Ignore `event:`/`id:`/`retry:` lines — the world-model streams put\n // everything in `data:` on the default event.\n if (line.startsWith(\"data:\")) data += line.slice(5).trim();\n }\n if (!data) continue;\n let parsed: E;\n try {\n parsed = JSON.parse(data) as E;\n } catch {\n continue;\n }\n onEvent(parsed);\n }\n }\n}\n","// React hooks for the world-model surface, exposed to custom-app bundles\n// so an app can render the semantic-model graph, browse an entity's\n// instances, and drill into an instance's detail / measure driver-tree.\n//\n// Wraps the `/api/projects/{id}/semantic/world-model*` endpoints behind the\n// shared `OxyAppProvider` fetcher. The two drill-down endpoints\n// (`instance-detail`, `measure-breakdown`) stream `kind`-tagged SSE events;\n// their hooks fold the stream into accumulated state so a bundle can render\n// progressively (skeletons fill in as measure values resolve).\n\nimport * as React from \"react\";\nimport type {\n WmInstancesResponse,\n WmMeasureBreakdown,\n WmMeasureBreakdownEvent,\n WorldModel\n} from \"../worldModel\";\nimport { apiErrorFromResponse } from \"./errors\";\nimport { useOxyApp } from \"./react\";\nimport { readJsonSseStream } from \"./sse\";\n\n/** Base path for the world-model endpoints of the active project. */\nfunction worldModelPath(projectId: string): string {\n return `/api/projects/${projectId}/semantic/world-model`;\n}\n\n// ── useWorldModelGraph (graph) ────────────────────────────────────────────────\n\nexport interface UseWorldModelGraphResult {\n data: WorldModel | null;\n loading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\n/**\n * The world-model graph — entities (nodes), their measures/dimensions, and\n * how measures promote across the entity hierarchy (edges). Applies the\n * project's `.world-model.yml` display config server-side.\n *\n * @remarks\n * This returns the raw semantic-model entity graph. For the higher-level\n * node-paradigm interface (`world.metric(id)` speaking `expand` / `explain` /\n * `size`), use {@link useWorldModel} from `./world-node` instead.\n */\nexport function useWorldModelGraph(opts: { enabled?: boolean } = {}): UseWorldModelGraphResult {\n const { projectId, fetcher } = useOxyApp();\n const enabled = opts.enabled !== false;\n const [data, setData] = React.useState<WorldModel | null>(null);\n const [loading, setLoading] = React.useState<boolean>(enabled && !!projectId);\n const [error, setError] = React.useState<Error | null>(null);\n const [_nonce, setNonce] = React.useState(0);\n\n React.useEffect(() => {\n if (!enabled || !projectId) {\n setLoading(false);\n return;\n }\n const ctrl = new AbortController();\n let cancelled = false;\n setLoading(true);\n setError(null);\n fetcher(worldModelPath(projectId), { method: \"GET\", signal: ctrl.signal })\n .then(async (resp) => {\n if (!resp.ok) throw await apiErrorFromResponse(resp);\n return (await resp.json()) as WorldModel;\n })\n .then((result) => {\n if (cancelled) return;\n setData(result);\n setLoading(false);\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n if (err instanceof DOMException && err.name === \"AbortError\") return;\n setError(err instanceof Error ? err : new Error(String(err)));\n setLoading(false);\n });\n return () => {\n cancelled = true;\n ctrl.abort();\n };\n }, [enabled, projectId, fetcher]);\n\n const refetch = React.useCallback(() => setNonce((n) => n + 1), []);\n return { data, loading, error, refetch };\n}\n\n// ── useWorldModelInstances ────────────────────────────────────────────────────\n\nexport interface UseWorldModelInstancesOpts {\n /** Substring/prefix search over the entity's display field. */\n search?: string;\n /** Max rows to return (default 50 server-side). */\n limit?: number;\n enabled?: boolean;\n}\n\nexport interface UseWorldModelInstancesResult {\n data: WmInstancesResponse | null;\n loading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\n/**\n * List the instances (rows) of `entityId` — a bounded, searchable picker\n * over the entity's primary keys + display label. Pass `null` for `entityId`\n * to stay idle until an entity is chosen.\n */\nexport function useWorldModelInstances(\n entityId: string | null,\n opts: UseWorldModelInstancesOpts = {}\n): UseWorldModelInstancesResult {\n const { projectId, fetcher } = useOxyApp();\n const enabled = opts.enabled !== false;\n const { search, limit } = opts;\n const [data, setData] = React.useState<WmInstancesResponse | null>(null);\n const [loading, setLoading] = React.useState<boolean>(enabled && !!projectId && !!entityId);\n const [error, setError] = React.useState<Error | null>(null);\n const [_nonce, setNonce] = React.useState(0);\n\n React.useEffect(() => {\n if (!enabled || !projectId || !entityId) {\n setLoading(false);\n return;\n }\n const ctrl = new AbortController();\n let cancelled = false;\n setLoading(true);\n setError(null);\n const params = new URLSearchParams({ entity: entityId });\n if (search) params.set(\"search\", search);\n if (limit != null) params.set(\"limit\", String(limit));\n fetcher(`${worldModelPath(projectId)}/instances?${params}`, {\n method: \"GET\",\n signal: ctrl.signal\n })\n .then(async (resp) => {\n if (!resp.ok) throw await apiErrorFromResponse(resp);\n return (await resp.json()) as WmInstancesResponse;\n })\n .then((result) => {\n if (cancelled) return;\n setData(result);\n setLoading(false);\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n if (err instanceof DOMException && err.name === \"AbortError\") return;\n setError(err instanceof Error ? err : new Error(String(err)));\n setLoading(false);\n });\n return () => {\n cancelled = true;\n ctrl.abort();\n };\n }, [enabled, projectId, entityId, search, limit, fetcher]);\n\n const refetch = React.useCallback(() => setNonce((n) => n + 1), []);\n return { data, loading, error, refetch };\n}\n\n// ── useMeasureBreakdown (driver tree, SSE) ────────────────────────────────────\n\nexport interface UseMeasureBreakdownResult {\n /** Accumulated breakdown graph; null until the `init` frame. */\n breakdown: WmMeasureBreakdown | null;\n loading: boolean;\n done: boolean;\n error: Error | null;\n}\n\n/** Fold one measure-breakdown SSE frame into the accumulated graph. */\nfunction foldBreakdown(\n prev: WmMeasureBreakdown | null,\n ev: WmMeasureBreakdownEvent\n): WmMeasureBreakdown | null {\n switch (ev.kind) {\n case \"init\":\n return {\n root: ev.root,\n nodes: ev.nodes.map((n) => ({ ...n, value: null, unvalued_reason: null })),\n edges: ev.edges\n };\n case \"value\": {\n if (!prev) return prev;\n return {\n ...prev,\n nodes: prev.nodes.map((n) =>\n n.id === ev.node_id ? { ...n, value: ev.value, unvalued_reason: ev.unvalued_reason } : n\n )\n };\n }\n default:\n return prev;\n }\n}\n\n/**\n * Stream the driver-tree breakdown of one instance's measure — the metric\n * decomposition (add/sub/mul/div component graph) with each node's value\n * filling in as it resolves. This is the per-instance RCA view. Pass `null`\n * for `measure` to stay idle.\n */\nexport function useMeasureBreakdown(\n entityId: string | null,\n keyValue: string | null,\n measure: string | null\n): UseMeasureBreakdownResult {\n const { projectId, fetcher } = useOxyApp();\n const [breakdown, setBreakdown] = React.useState<WmMeasureBreakdown | null>(null);\n const [loading, setLoading] = React.useState<boolean>(false);\n const [done, setDone] = React.useState<boolean>(false);\n const [error, setError] = React.useState<Error | null>(null);\n\n React.useEffect(() => {\n if (!projectId || !entityId || !keyValue || !measure) {\n setLoading(false);\n return;\n }\n const ctrl = new AbortController();\n let cancelled = false;\n setBreakdown(null);\n setLoading(true);\n setDone(false);\n setError(null);\n\n const params = new URLSearchParams({ entity: entityId, key: keyValue, measure });\n fetcher(`${worldModelPath(projectId)}/measure-breakdown?${params}`, {\n method: \"GET\",\n signal: ctrl.signal\n })\n .then(async (resp) => {\n if (!resp.ok) throw await apiErrorFromResponse(resp);\n await readJsonSseStream<WmMeasureBreakdownEvent>(resp, (ev) => {\n if (cancelled) return;\n if (ev.kind === \"done\") {\n setDone(true);\n return;\n }\n setBreakdown((prev) => foldBreakdown(prev, ev));\n });\n if (!cancelled) setLoading(false);\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n if (err instanceof DOMException && err.name === \"AbortError\") return;\n setError(err instanceof Error ? err : new Error(String(err)));\n setLoading(false);\n });\n return () => {\n cancelled = true;\n ctrl.abort();\n };\n }, [projectId, entityId, keyValue, measure, fetcher]);\n\n return { breakdown, loading, done, error };\n}\n","// The World Model **node interface** — the higher-level \"node paradigm\" from\n// `docs/build/sdk/world-model.mdx`. Everything in the World Model is a node,\n// and every node speaks the same verbs: `expand` (one hop of relationships),\n// `drill` (narrow to a segment), `explain` (period-over-period root cause),\n// and `size` (peer-gap opportunity). Render a node, let the user pick a verb,\n// get more nodes back, recurse.\n//\n// This is a thin composition layer over the metric-tree analyses that already\n// ship (`metric-tree-hooks.tsx` / the `MetricTreeClient`): the verbs map onto\n// the same `/semantic/metric-tree*` endpoints, so a bundle typed against a\n// handle matches what the server serializes verbatim. It adds no new backend.\n//\n// The logic lives in a framework-agnostic `createWorldModel(projectId,\n// fetcher)` factory; `useWorldModel()` is a thin `useMemo` over it, scoped to\n// the active `<OxyAppProvider>` project.\n//\n// **Alpha.** Per the doc, the node paradigm is a design preview and may\n// change. `drill` is the one verb the backend cannot yet honor — the\n// metric-tree endpoints take no segment/instance filter (the opportunity\n// endpoint explicitly refuses it), and structural verbs are scope-invariant.\n// So `drill` returns a scoped handle for interface fidelity, but the value\n// verbs (`explain`/`size`) on a drilled handle throw\n// {@link WorldModelScopeUnsupportedError} rather than silently returning\n// population numbers for a scoped question.\n\nimport * as React from \"react\";\nimport type {\n ExplainRequest,\n ExplainResult,\n MetricEdge,\n MetricNode,\n MetricTree,\n OpportunityRequest,\n OpportunityResult,\n SensitivityResult\n} from \"../metricTree\";\nimport { getJson, metricTreePath, postJson } from \"./metric-tree-fetch\";\nimport type { AppFetcher } from \"./react\";\nimport { useOxyApp } from \"./react\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\n/** A `dimension → value` scope narrowed onto a node via {@link MetricHandle.drill}. */\nexport type MetricScope = Readonly<Record<string, string>>;\n\n/** Options for {@link MetricHandle.explain} — an {@link ExplainRequest} minus\n * the `target`, which the handle supplies from its own id. */\nexport type ExplainOpts = Omit<ExplainRequest, \"target\">;\n\n/** Options for {@link MetricHandle.size} — an {@link OpportunityRequest} minus\n * the `target`. */\nexport type SizeOpts = Omit<OpportunityRequest, \"target\">;\n\n/** One child revealed by {@link MetricHandle.expand}: the child measure's\n * node, the edge that connects it to the parent, and a handle to recurse. */\nexport interface ExpandedNode {\n /** The child measure (a component or a driver of the parent). */\n node: MetricNode;\n /** The parent → child edge — `kind`, `direction`, `strength`, `form`, … */\n edge: MetricEdge;\n /** A live handle on the child, carrying the parent's scope. */\n handle: MetricHandle;\n}\n\n/**\n * A live handle on one metric node. Carry it around and call a verb; every\n * verb returns either more nodes (`expand`), a scoped handle (`drill`), or an\n * analysis result (`explain` / `size` / `drivers`).\n */\nexport interface MetricHandle {\n /** Fully-qualified measure id (`view.measure`). */\n readonly id: string;\n /** The scope narrowed onto this handle by `drill` (empty for a root handle). */\n readonly scope: MetricScope;\n /** The measure's own tree node (label, expr, is_composite). */\n node(signal?: AbortSignal): Promise<MetricNode>;\n /** One hop of relationships — the metric's components and drivers as child nodes. */\n expand(signal?: AbortSignal): Promise<ExpandedNode[]>;\n /** The declared drivers of this measure, ranked by influence (sensitivity). */\n drivers(signal?: AbortSignal): Promise<SensitivityResult>;\n /** Root-cause a period-over-period move: why it dropped or climbed. */\n explain(opts: ExplainOpts, signal?: AbortSignal): Promise<ExplainResult>;\n /** Compare this node to its peers across each dimension and size the gap. */\n size(opts: SizeOpts, signal?: AbortSignal): Promise<OpportunityResult>;\n /** Narrow into a segment or entity instance — returns a scoped handle. */\n drill(scope: Record<string, string>): MetricHandle;\n}\n\n/**\n * The World Model interface, scoped to one project. The whole surface hangs\n * off this: grab a {@link MetricHandle} with `metric(id)` and the handle\n * speaks the verbs, or pull the whole graph with `tree(root?)`.\n */\nexport interface WorldModelApi {\n /** The active project id, or `null` before `<OxyAppProvider>` resolves one. */\n readonly projectId: string | null;\n /** The metric tree, rooted anywhere you like (default: the whole tree). */\n tree(root?: string, signal?: AbortSignal): Promise<MetricTree>;\n /** A live handle on one measure node. */\n metric(id: string): MetricHandle;\n}\n\n/**\n * Thrown by the value verbs (`explain` / `size`) when called on a handle that\n * has been `drill`ed. The metric-tree backend cannot yet scope these analyses\n * to a segment, so failing loud beats returning population numbers for a\n * question that asked about one segment.\n */\nexport class WorldModelScopeUnsupportedError extends Error {\n readonly code = \"world_model_scope_unsupported\";\n readonly scope: MetricScope;\n constructor(verb: string, scope: MetricScope) {\n super(\n `${verb} on a drilled (scoped) node is not yet supported by the backend ` +\n `(scope: ${JSON.stringify(scope)}). Call ${verb} on the un-drilled node ` +\n `for population-level analysis.`\n );\n this.name = \"WorldModelScopeUnsupportedError\";\n this.scope = scope;\n }\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Build a {@link WorldModelApi} over a project id and fetcher. Framework-\n * agnostic — `useWorldModel()` wraps this for React, but it is directly\n * unit-testable with a mock fetcher.\n */\nexport function createWorldModel(projectId: string | null, fetcher: AppFetcher): WorldModelApi {\n const base = (): string => {\n if (!projectId) {\n throw new Error(\n \"World Model unavailable: no active project (are you inside <OxyAppProvider>?)\"\n );\n }\n return metricTreePath(projectId);\n };\n\n const tree = (root?: string, signal?: AbortSignal): Promise<MetricTree> => {\n const qs = root ? `?root=${encodeURIComponent(root)}` : \"\";\n return getJson<MetricTree>(fetcher, `${base()}${qs}`, signal);\n };\n\n const makeHandle = (id: string, scope: MetricScope): MetricHandle => {\n const scoped = Object.keys(scope).length > 0;\n return {\n id,\n scope,\n async node(signal) {\n const t = await tree(id, signal);\n const found = t.nodes.find((n) => n.id === id);\n if (!found) throw new Error(`measure '${id}' not found in the metric tree`);\n return found;\n },\n async expand(signal) {\n const t = await tree(id, signal);\n const byId = new Map(t.nodes.map((n) => [n.id, n] as const));\n // `from` is the parent, `to` the child (component/driver) — the same\n // orientation the IDE metric-tree graph lays out top-down.\n const children: ExpandedNode[] = [];\n for (const edge of t.edges) {\n if (edge.from !== id) continue;\n const childNode = byId.get(edge.to);\n if (!childNode) continue;\n children.push({ node: childNode, edge, handle: makeHandle(edge.to, scope) });\n }\n return children;\n },\n drivers(signal) {\n return getJson<SensitivityResult>(\n fetcher,\n `${base()}/${encodeURIComponent(id)}/sensitivity`,\n signal\n );\n },\n explain(opts, signal) {\n if (scoped) throw new WorldModelScopeUnsupportedError(\"explain\", scope);\n return postJson<ExplainResult>(\n fetcher,\n `${base()}/explain`,\n { target: id, ...opts },\n signal\n );\n },\n size(opts, signal) {\n if (scoped) throw new WorldModelScopeUnsupportedError(\"size\", scope);\n return postJson<OpportunityResult>(\n fetcher,\n `${base()}/opportunity`,\n { target: id, ...opts },\n signal\n );\n },\n drill(next) {\n return makeHandle(id, { ...scope, ...next });\n }\n };\n };\n\n return {\n projectId,\n tree,\n metric: (id: string) => makeHandle(id, {})\n };\n}\n\n// ── Hook ──────────────────────────────────────────────────────────────────────\n\n/**\n * The World Model node interface, scoped to the active `<OxyAppProvider>`\n * project. Returns a stable {@link WorldModelApi} — grab a node with\n * `world.metric(id)` and let it speak the verbs.\n *\n * @example\n * ```tsx\n * const world = useWorldModel();\n * const revenue = world.metric(\"orders.net_revenue\");\n * const children = await revenue.expand(); // components + drivers\n * const rca = await revenue.explain({\n * time_dimension: \"orders.order_date\",\n * current_period: [\"2026-06-01\", \"2026-06-30\"],\n * previous_period: [\"2026-05-01\", \"2026-05-31\"],\n * });\n * ```\n *\n * @remarks\n * This is the node-paradigm hook. For the raw semantic-model entity/measure\n * graph, use {@link useWorldModelGraph} instead.\n */\nexport function useWorldModel(): WorldModelApi {\n const { projectId, fetcher } = useOxyApp();\n return React.useMemo(() => createWorldModel(projectId ?? null, fetcher), [projectId, fetcher]);\n}\n","// Metric-tree types + client. Mirrors `airlayer::engine::metric_tree*`\n// over the `/<project_id>/semantic/metric-tree*` HTTP endpoints. Serde\n// emits snake_case so these field names match the wire format verbatim.\n\nimport type { OxyConfig } from \"./config\";\n\n// ── Tree ──────────────────────────────────────────────────────────────────────\n\nexport type EdgeKind = \"component\" | \"driver\";\nexport type DriverDirection = \"positive\" | \"negative\" | \"unknown\";\nexport type DriverStrength = \"strong\" | \"moderate\" | \"weak\";\nexport type DriverConfidence = \"high\" | \"medium\" | \"low\";\nexport type DriverForm = \"linear\" | \"log-log\" | \"log-linear\" | \"linear-log\";\n\nexport interface MetricNode {\n id: string;\n view: string;\n measure: string;\n label: string;\n description?: string | null;\n measure_type: string;\n is_composite: boolean;\n expr?: string | null;\n}\n\nexport interface MetricEdge {\n from: string;\n to: string;\n kind: EdgeKind;\n /** Sign of a component edge; omitted (defaults to +1) for most edges. */\n sign?: number;\n direction: DriverDirection;\n strength: DriverStrength;\n confidence: DriverConfidence;\n coefficient?: number | null;\n form: DriverForm;\n intercept?: number | null;\n lag?: number | null;\n description?: string | null;\n refs?: string[] | null;\n}\n\nexport interface MetricTree {\n nodes: MetricNode[];\n edges: MetricEdge[];\n root?: string | null;\n}\n\n// ── Sensitivity ──────────────────────────────────────────────────────────────\n\nexport interface SensitivityDriver {\n measure: string;\n path: string[];\n edge_kind: string;\n effective_coefficient?: number | null;\n form?: DriverForm | null;\n direction: DriverDirection;\n strength: DriverStrength;\n lag?: number | null;\n description?: string | null;\n}\n\nexport interface SensitivityResult {\n target: string;\n drivers: SensitivityDriver[];\n}\n\n// ── Predict ──────────────────────────────────────────────────────────────────\n\nexport interface PredictChange {\n measure: string;\n delta: number;\n}\n\nexport interface PredictImpact {\n measure: string;\n estimated_delta: number;\n confidence: string;\n path: string[];\n form: DriverForm;\n lag?: number | null;\n}\n\nexport interface PredictResult {\n inputs: PredictChange[];\n impacts: PredictImpact[];\n}\n\n// ── Explain (RCA) ────────────────────────────────────────────────────────────\n\nexport type SplitKind =\n | { type: \"component\"; child_measure: string }\n | { type: \"dimension\"; dimension: string; value: string }\n | { type: \"uniform_degradation\"; dimension: string; num_elements: number }\n | { type: \"cross_cutting\"; dimension: string; value: string; measures: string[] };\n\nexport interface ExplainSibling {\n split: SplitKind;\n measure: string;\n delta: number;\n root_fraction: number;\n}\n\nexport interface ExplainNode {\n split: SplitKind;\n measure: string;\n filters: unknown[];\n delta: number;\n concentration: number;\n root_fraction: number;\n siblings?: ExplainSibling[];\n dimension_count?: number;\n children?: ExplainNode[];\n}\n\n/** Whether a driver's observed move pushes the target the way it actually\n * moved (`contributing`) or against it (`counteracting` — it offset part of\n * the move rather than causing it). `unknown` when no signed claim is\n * available: `direction: unknown` with no coefficient, or a flat\n * driver/target. */\nexport type DriverContribution = \"contributing\" | \"counteracting\" | \"unknown\";\n\n/** A driver's move split into the part its base forced and the part its own\n * ratio contributed. Emitted only when the driver genuinely tracks a sibling\n * rather than moving on its own — presence is the claim.\n * `base_driven_delta + ratio_driven_delta === driver_delta`. */\nexport interface PassthroughSplit {\n base_measure: string;\n ratio_previous: number;\n ratio_current: number;\n base_driven_delta: number;\n ratio_driven_delta: number;\n}\n\nexport interface DriverAttribution {\n driver_measure: string;\n driver_previous: number;\n driver_current: number;\n driver_delta: number;\n /** Both optional: an `explain_cache` row written before these fields shipped\n * is served verbatim, so absent means unclassified — not a default. */\n direction?: DriverDirection;\n contribution?: DriverContribution;\n coefficient?: number;\n form: DriverForm;\n /** Absent for a purely qualitative driver (no coefficient). */\n estimated_target_impact?: number;\n description?: string;\n passthrough?: PassthroughSplit;\n}\n\nexport type ExplainWarning =\n | {\n type: \"simpsons_paradox\";\n dimension: string;\n aggregate_delta: number;\n segment_directions: [string, number][];\n }\n | {\n type: \"opposing_offset\";\n component_a: string;\n component_b: string;\n delta_a: number;\n delta_b: number;\n }\n | {\n type: \"non_additive_dimension_split\";\n measure: string;\n measure_type: string;\n dimension: string;\n };\n\nexport interface ExplainConfigOverride {\n deep?: boolean;\n max_depth?: number;\n coverage_threshold?: number;\n}\n\nexport interface ExplainRequest {\n target: string;\n time_dimension: string;\n current_period: [string, string];\n previous_period: [string, string];\n config?: ExplainConfigOverride;\n}\n\nexport interface ExplainResult {\n target: string;\n target_delta: number;\n target_previous: number;\n target_current: number;\n time_dimension: string;\n current_period: [string, string];\n previous_period: [string, string];\n nodes: ExplainNode[];\n coverage: number;\n driver_attribution?: DriverAttribution[];\n alternatives?: unknown[];\n warnings?: ExplainWarning[];\n}\n\n// ── Opportunity ──────────────────────────────────────────────────────────────\n\nexport interface SegmentOpportunity {\n segment: string;\n current_value: number;\n volume: number;\n benchmark: number;\n gap: number;\n /** Match-the-best upside in measure units. */\n upside: number;\n}\n\nexport interface DimensionOpportunity {\n dimension: string;\n cardinality: number;\n /** \"best_peer\" or \"p75\". */\n benchmark_basis: string;\n total_upside: number;\n segments: SegmentOpportunity[];\n other_segments_skipped: number;\n}\n\nexport interface SkippedDimension {\n dimension: string;\n reason: string;\n}\n\nexport interface OpportunityRequest {\n target: string;\n time_dimension: string;\n period: [string, string];\n}\n\nexport interface OpportunityResult {\n target: string;\n period: [string, string];\n overall_value: number;\n /**\n * \"rows\" (rate-based additive sizing — the only basis that yields a sized\n * upside figure), \"value_share\" (additive) or \"equal\" (ratios).\n */\n weight_basis: string;\n dimensions: DimensionOpportunity[];\n skipped_dimensions: SkippedDimension[];\n downstream: PredictImpact[];\n}\n\n// ── Distribution ─────────────────────────────────────────────────────────────\n\n/**\n * Single-period structural decomposition. The server auto-derives the\n * baseline as the equal-length window immediately before `period`, then\n * returns an {@link ExplainResult}-shaped payload (so the same renderers\n * work). Ignore the delta fields when rendering a pure distribution.\n */\nexport interface DistributionRequest {\n target: string;\n time_dimension: string;\n /** `[start, end]` inclusive date strings. */\n period: [string, string];\n}\n\n// ── Time dimensions ──────────────────────────────────────────────────────────\n\nexport interface TimeDimensionsResponse {\n /** view name → fully-qualified time-dimension ids (`view.dim`). */\n by_view: Record<string, string[]>;\n}\n\n// ── Client ───────────────────────────────────────────────────────────────────\n\n/**\n * Shape of the inner request helper exposed by `OxyClient`. The metric-tree\n * client reuses it to inherit auth headers, timeout, baseUrl, and project\n * scoping rather than reimplementing fetch end-to-end.\n */\nexport type RequestFn = <T>(endpoint: string, options?: RequestInit) => Promise<T>;\n\n/**\n * Client for the `/semantic/metric-tree*` endpoints. Surfaces the four\n * airlayer metric-tree analyses (tree introspection, sensitivity, predict,\n * explain, opportunity) over typed methods.\n *\n * Construction is internal to {@link OxyClient} — call `client.metricTree`\n * to access an instance rather than building one yourself.\n *\n * @example\n * ```typescript\n * const client = await OxyClient.create({ projectId: \"...\", apiKey: \"...\" });\n * const tree = await client.metricTree.getTree();\n * const drivers = await client.metricTree.getSensitivity(\"orders.net_revenue\");\n * ```\n */\nexport class MetricTreeClient {\n private readonly request: RequestFn;\n private readonly config: OxyConfig;\n\n constructor(config: OxyConfig, request: RequestFn) {\n this.config = config;\n this.request = request;\n }\n\n private path(suffix: string): string {\n return `/${this.config.projectId}/semantic/metric-tree${suffix}`;\n }\n\n private buildQuery(extra: Record<string, string> = {}): string {\n const params: Record<string, string> = { ...extra };\n if (this.config.branch) params.branch = this.config.branch;\n const qs = new URLSearchParams(params).toString();\n return qs ? `?${qs}` : \"\";\n }\n\n /**\n * Fetch the full metric tree, or the subtree rooted at `root`.\n *\n * @param root - Optional fully-qualified measure id to root the tree at.\n * @returns Nodes (measures) and edges (component / driver relationships).\n *\n * @example\n * ```typescript\n * const tree = await client.metricTree.getTree();\n * const subtree = await client.metricTree.getTree(\"orders.net_revenue\");\n * ```\n */\n async getTree(root?: string): Promise<MetricTree> {\n const query = this.buildQuery(root ? { root } : {});\n return this.request<MetricTree>(this.path(query));\n }\n\n /**\n * Rank the declared drivers of a measure by influence.\n *\n * @param measureId - Fully-qualified measure id (`view.measure`).\n *\n * @example\n * ```typescript\n * const sensitivity = await client.metricTree.getSensitivity(\"orders.net_revenue\");\n * for (const driver of sensitivity.drivers) {\n * console.log(driver.measure, driver.direction, driver.strength);\n * }\n * ```\n */\n async getSensitivity(measureId: string): Promise<SensitivityResult> {\n const query = this.buildQuery();\n return this.request<SensitivityResult>(\n this.path(`/${encodeURIComponent(measureId)}/sensitivity${query}`)\n );\n }\n\n /**\n * Propagate hypothetical `(measure, delta)` changes upward through the\n * tree. Returns the estimated impact on every downstream measure.\n *\n * @example\n * ```typescript\n * const result = await client.metricTree.predict([\n * { measure: \"marketing_spend.total_spend\", delta: 10000 },\n * ]);\n * ```\n */\n async predict(changes: PredictChange[]): Promise<PredictResult> {\n const query = this.buildQuery();\n return this.request<PredictResult>(this.path(`/predict${query}`), {\n method: \"POST\",\n body: JSON.stringify({ changes })\n });\n }\n\n /**\n * Period-over-period root-cause decomposition. Recursively splits the\n * target measure by components and dimensions until the move concentrates.\n *\n * @example\n * ```typescript\n * const result = await client.metricTree.explain({\n * target: \"financials.operating_profit\",\n * time_dimension: \"financials.month\",\n * current_period: [\"2025-09-01\", \"2025-09-30\"],\n * previous_period: [\"2025-08-01\", \"2025-08-31\"],\n * });\n * ```\n */\n async explain(request: ExplainRequest): Promise<ExplainResult> {\n const query = this.buildQuery();\n return this.request<ExplainResult>(this.path(`/explain${query}`), {\n method: \"POST\",\n body: JSON.stringify(request)\n });\n }\n\n /**\n * Size the upside opportunity for a measure by finding underperforming\n * segments. Skips high-cardinality dimensions and trims the long tail.\n *\n * @example\n * ```typescript\n * const result = await client.metricTree.findOpportunities({\n * target: \"orders.net_revenue\",\n * time_dimension: \"orders.order_date\",\n * period: [\"2025-09-01\", \"2025-09-30\"],\n * });\n * for (const dim of result.dimensions) {\n * console.log(dim.dimension, \"+\", dim.total_upside);\n * }\n * ```\n */\n async findOpportunities(request: OpportunityRequest): Promise<OpportunityResult> {\n const query = this.buildQuery();\n return this.request<OpportunityResult>(this.path(`/opportunity${query}`), {\n method: \"POST\",\n body: JSON.stringify(request)\n });\n }\n}\n"],"mappings":"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"}