@oxy-hq/sdk 2.12.0 → 2.16.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.
Files changed (47) hide show
  1. package/README.md +67 -0
  2. package/dist/{function-context-D8eyZuw_.d.cts → function-context-BNpL5bFb.d.cts} +223 -20
  3. package/dist/function-context-BNpL5bFb.d.cts.map +1 -0
  4. package/dist/{function-context-D8eyZuw_.d.mts → function-context-BNpL5bFb.d.mts} +223 -20
  5. package/dist/function-context-BNpL5bFb.d.mts.map +1 -0
  6. package/dist/index.cjs +80 -16
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.d.cts +164 -3
  9. package/dist/index.d.cts.map +1 -1
  10. package/dist/index.d.mts +164 -3
  11. package/dist/index.d.mts.map +1 -1
  12. package/dist/index.mjs +78 -16
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/ops.d.cts +1 -1
  15. package/dist/ops.d.mts +1 -1
  16. package/dist/{react-DW7Z96sD.d.mts → react-CljeXJuw.d.cts} +142 -8
  17. package/dist/react-CljeXJuw.d.cts.map +1 -0
  18. package/dist/{react-DW7Z96sD.d.cts → react-CljeXJuw.d.mts} +142 -8
  19. package/dist/react-CljeXJuw.d.mts.map +1 -0
  20. package/dist/{react-DcT-mUPj.cjs → react-Dvkv2deI.cjs} +110 -59
  21. package/dist/react-Dvkv2deI.cjs.map +1 -0
  22. package/dist/{react-BXGyzgz0.mjs → react-OW1t_J0M.mjs} +103 -26
  23. package/dist/react-OW1t_J0M.mjs.map +1 -0
  24. package/dist/rolldown-runtime-KC0qvQup.cjs +34 -0
  25. package/dist/shell.cjs +38 -3
  26. package/dist/shell.cjs.map +1 -1
  27. package/dist/shell.d.cts +26 -3
  28. package/dist/shell.d.cts.map +1 -1
  29. package/dist/shell.d.mts +26 -3
  30. package/dist/shell.d.mts.map +1 -1
  31. package/dist/shell.mjs +34 -2
  32. package/dist/shell.mjs.map +1 -1
  33. package/dist/testing.cjs +4431 -0
  34. package/dist/testing.cjs.map +1 -0
  35. package/dist/testing.d.cts +656 -0
  36. package/dist/testing.d.cts.map +1 -0
  37. package/dist/testing.d.mts +656 -0
  38. package/dist/testing.d.mts.map +1 -0
  39. package/dist/testing.mjs +4395 -0
  40. package/dist/testing.mjs.map +1 -0
  41. package/package.json +22 -9
  42. package/dist/function-context-D8eyZuw_.d.cts.map +0 -1
  43. package/dist/function-context-D8eyZuw_.d.mts.map +0 -1
  44. package/dist/react-BXGyzgz0.mjs.map +0 -1
  45. package/dist/react-DW7Z96sD.d.cts.map +0 -1
  46. package/dist/react-DW7Z96sD.d.mts.map +0 -1
  47. package/dist/react-DcT-mUPj.cjs.map +0 -1
@@ -3,12 +3,27 @@
3
3
  /**
4
4
  * The request passed as the first argument to a function's default export.
5
5
  *
6
- * The host hands the isolate the raw request body as a string (see
7
- * `req_json` in `runtime.rs`); parse it yourself, e.g.
8
- * `JSON.parse(req.body || "{}")`. This is intentionally *not* a full Web
9
- * `Request` — there is no `.json()` / headers object in v1.
6
+ * The host builds this as `{ method, headers, body }` (`req_json` in
7
+ * `runtime.rs`). It is intentionally *not* a full Web `Request`: the body
8
+ * arrives as a string, so parse it yourself — `JSON.parse(req.body || "{}")`
9
+ * — and there is no `.json()`, no `url`, and no `Headers` object.
10
10
  */
11
11
  interface OxyFunctionRequest {
12
+ /**
13
+ * HTTP method of the triggering request, upper-case (`"POST"`, `"GET"`).
14
+ * A scheduled or Airway run has no real request and reports `"POST"`.
15
+ */
16
+ method: string;
17
+ /**
18
+ * The headers the function is allowed to see, keyed by **lower-case** name.
19
+ *
20
+ * The host drops everything that is not on its passthrough list and not
21
+ * `x-*`, plus its own `x-oxy-*` prefix and a blocked list; a header sent
22
+ * twice collapses to its **first** value, and one whose bytes are not UTF-8
23
+ * is dropped rather than lossily decoded. A scheduled or Airway run has no
24
+ * headers, so this is `{}` — never absent.
25
+ */
26
+ headers: Record<string, string>;
12
27
  /** Raw request body as received (JSON string for a JSON POST). */
13
28
  body: string;
14
29
  }
@@ -191,6 +206,12 @@ type OxyFetchInit = RequestInit & {
191
206
  * `query` does not, because that allowlist is about modifying a project's
192
207
  * warehouse, and a `postgres_managed` database resolves the read-only analyst
193
208
  * for every caller regardless.
209
+ *
210
+ * **Shape: the customer's warehouse — read it, don't write it.** Writes to a
211
+ * customer warehouse (anything but `airhouse` / `airhouse_managed`) are refused
212
+ * unless the function names the database in `customerWarehouseWrites` with a
213
+ * reason. Facts your app records go to `ctx.airhouse`, records it edits to
214
+ * `ctx.oltp`, files to `ctx.storage`.
194
215
  */
195
216
  interface OxyWarehouseApi {
196
217
  /**
@@ -206,11 +227,21 @@ interface OxyWarehouseApi {
206
227
  }>;
207
228
  insert(database: string, table: string, rows: OxyFunctionRow[]): Promise<unknown>;
208
229
  exec(database: string, sql: string): Promise<unknown>;
230
+ /**
231
+ * Insert rows, updating any whose `conflictColumns` already exist.
232
+ *
233
+ * Compiles to `INSERT … ON CONFLICT … DO UPDATE`, which only Postgres and
234
+ * DuckDB parse, and which needs a primary key or unique constraint on
235
+ * `conflictColumns`. On any other warehouse — ClickHouse, Snowflake, BigQuery,
236
+ * MySQL — the call is refused by name before anything is sent; use `exec` with
237
+ * that warehouse's own upsert statement instead. Airhouse (DuckLake) tables
238
+ * carry no such constraint, so there the engine refuses it.
239
+ */
209
240
  upsert(database: string, table: string, rows: OxyFunctionRow[], conflictColumns: string[]): Promise<unknown>;
210
241
  }
211
242
  /**
212
- * The handle `ctx.tx` passes to your callback — a pinned connection with an
213
- * open transaction.
243
+ * The handle `ctx.tx` and `ctx.oltp.tx` pass to your callback — a pinned
244
+ * connection with an open transaction.
214
245
  *
215
246
  * Both methods take **bound parameters** (`$1`, `$2`, …). Never build SQL by
216
247
  * concatenating request data: `ctx.warehouse.exec` takes a bare string, but a
@@ -227,6 +258,11 @@ interface OxyTransaction {
227
258
  exec(sql: string, params?: unknown[]): Promise<number>;
228
259
  }
229
260
  /**
261
+ * **Shape: records your app edits** — current state that needs constraints or
262
+ * transactions: a booking, a shift assignment, a template, a count. What
263
+ * happened (history that will not change) goes to `ctx.airhouse`; bytes go to
264
+ * `ctx.storage`.
265
+ *
230
266
  * `ctx.oltp` — read and WRITE the app's OWN per-org OLTP schema (`app_<writer>`)
231
267
  * on the managed Postgres tenant, and nothing else.
232
268
  *
@@ -253,8 +289,9 @@ interface OxyTransaction {
253
289
  * handshake, and a wake-up if the compute was idle) and its own transaction, so
254
290
  * a per-row loop pays that per row. Prefer one statement over many — a
255
291
  * multi-row `INSERT`, an `INSERT … SELECT`, or `INSERT … RETURNING` to avoid a
256
- * follow-up read — and reach for `ctx.oltp` a handful of times per request, not
257
- * in a hot loop.
292
+ * follow-up read — or run several inside one `ctx.oltp.tx`, which holds a single
293
+ * connection. Reach for `ctx.oltp` a handful of times per request, not in a hot
294
+ * loop.
258
295
  *
259
296
  * ```ts
260
297
  * const [row] = await ctx.oltp.query(
@@ -268,11 +305,147 @@ interface OxyOltpApi {
268
305
  query(sql: string, params?: unknown[]): Promise<OxyFunctionRow[]>;
269
306
  /** Run a statement for its effect; resolves to the number of rows affected. */
270
307
  exec(sql: string, params?: unknown[]): Promise<number>;
308
+ /**
309
+ * Run several statements as one transaction on one connection: commits when
310
+ * `fn` resolves, rolls back when it throws, and rethrows your error. The same
311
+ * handle and rules as `ctx.tx` — let a failed statement's error propagate —
312
+ * without naming a database: the app's own store is implicit.
313
+ *
314
+ * ```ts
315
+ * await ctx.oltp.tx(async (tx) => {
316
+ * await tx.exec("UPDATE shifts SET status = 'closed' WHERE id = $1", [shiftId]);
317
+ * await tx.exec("INSERT INTO shift_notes (shift_id, body) VALUES ($1, $2)", [shiftId, note]);
318
+ * });
319
+ * ```
320
+ */
321
+ tx<T>(fn: (tx: OxyTransaction) => Promise<T> | T): Promise<T>;
322
+ }
323
+ /**
324
+ * **Shape: facts — what happened, which will not change**: an order, a completed
325
+ * checklist, a delivery, a reading. `ctx.airhouse` appends them to your app's own
326
+ * schema in the workspace's Airhouse, where the analytics agent, semantic views
327
+ * and other apps read them as history. Records your app edits in place belong in
328
+ * `ctx.oltp`; files in `ctx.storage`.
329
+ *
330
+ * Gated by the fail-closed `airhouse` manifest capability (`"airhouse": {
331
+ * "enabled": true }`), a pure gate: the schema is `app_<writer>`, derived from
332
+ * the app's slug (`store-ops` → `app_store_ops`) and exposed as `schema`. Writes
333
+ * run **as the app**, whoever invoked the function — a schedule, a webhook and a
334
+ * click write the same way.
335
+ *
336
+ * Every statement is checked before it is sent. Reads may name any schema;
337
+ * writes must target `<schema>.<table>`; `exec` runs no DDL — declare tables in
338
+ * `airhouseMigrations` files, which run once at publish. One statement per call.
339
+ *
340
+ * Airhouse is DuckLake: **no primary keys, UNIQUE, indexes or foreign keys**, and
341
+ * no bound parameters. Give every fact the id its source assigned and a
342
+ * `recorded_at`, append a correction as a new fact instead of updating, and keep
343
+ * one row per id when reading — a retried function can append twice:
344
+ *
345
+ * ```ts
346
+ * await ctx.airhouse.append("checklist_completions", [
347
+ * { completion_id: id, store_guid: store, recorded_at: new Date().toISOString() },
348
+ * ]);
349
+ *
350
+ * const { rows } = await ctx.airhouse.query(`
351
+ * SELECT * FROM ${ctx.airhouse.schema}.checklist_completions
352
+ * QUALIFY row_number() OVER (PARTITION BY completion_id ORDER BY recorded_at DESC) = 1
353
+ * `);
354
+ * ```
355
+ */
356
+ interface OxyAirhouseApi {
357
+ /** `app_<writer>` when the function declares the capability, else `null`. */
358
+ readonly schema: string | null;
359
+ /** Read, from any schema. Capped like `ctx.warehouse.query`; `truncated` says the cap cut rows. */
360
+ query(sql: string): Promise<{
361
+ rows: OxyFunctionRow[];
362
+ truncated: boolean;
363
+ }>;
364
+ /**
365
+ * Append rows to `<schema>.<table>`. Pass the bare table name; every row must
366
+ * carry the first row's columns. Values are sent as literals, safely quoted.
367
+ * Resolves to the number of rows sent.
368
+ */
369
+ append(table: string, rows: OxyFunctionRow[]): Promise<number>;
370
+ /**
371
+ * One write statement against your own schema: a `DELETE` for retention or
372
+ * erasure, an `INSERT … SELECT` deriving facts from other facts. Nothing is
373
+ * bound, so never interpolate request data here — that is what `append` is for.
374
+ */
375
+ exec(sql: string): Promise<void>;
271
376
  }
272
- /** `ctx.secrets` — write app-scoped secrets (gated by the `secrets.write` capability). */
377
+ /**
378
+ * `ctx.secrets` — write app-scoped secrets (gated by the `secrets.write` capability).
379
+ *
380
+ * **Credentials only — not state.** A secret is for a value you authenticate
381
+ * with, like a rotated token. A cursor, a counter or a JSON blob that changes
382
+ * between runs is a record: keep it in `ctx.oltp`.
383
+ */
273
384
  interface OxySecretsApi {
274
385
  set(key: string, value: string): Promise<void>;
275
386
  }
387
+ /**
388
+ * `ctx.crypto` — HMAC signing and verification, and a constant-time compare.
389
+ * **Synchronous**: pure CPU inside the isolate, so these skip the host-call
390
+ * channel and there is nothing to `await`. Mirrors `crypto` in `__buildCtx`
391
+ * (`runtime.rs`); the three members here are the three the host binds.
392
+ *
393
+ * `key` and `data` are read as UTF-8 — every webhook scheme in the wild signs a
394
+ * UTF-8 base string with a UTF-8 secret (GitHub the body, Slack `v0:ts:body`,
395
+ * Stripe `ts.body`) — so there is deliberately no per-argument encoding knob.
396
+ *
397
+ * Who controls an input decides what its absence does. An unset or empty `key`,
398
+ * or an unknown `algorithm` / `encoding`, is the author's mistake and **throws**.
399
+ * A `signature` that is absent or will not decode is the caller's, and returns
400
+ * **`false`** — throwing would turn a forged request into a 500. Both sides of
401
+ * `timingSafeEqual` are symmetric, so an absent or empty side is `false` and
402
+ * never a throw: with the secret unset, every request is rejected.
403
+ */
404
+ interface OxyCryptoApi {
405
+ /**
406
+ * The digest of `data` under `key`, as a string in `encoding`. For signing an
407
+ * **outbound** request; `verifyHmac` is the inverse direction.
408
+ */
409
+ hmac(input: OxyHmacInput): string;
410
+ /**
411
+ * Whether `signature` is the digest of `data` under `key`, compared in
412
+ * constant time. Strip the provider's prefix first (`sha256=`, `v0=`) and
413
+ * pass the bare digest — prefix formats are per-provider. A header the caller
414
+ * omitted can be passed as-is: absent is `false`, not a throw.
415
+ */
416
+ verifyHmac(input: OxyVerifyHmacInput): boolean;
417
+ /**
418
+ * Constant-time equality for a plain shared secret where there is no HMAC.
419
+ * Use it, not `===`, for any secret comparison: `===` short-circuits at the
420
+ * first differing byte and leaks the secret one byte at a time to anyone who
421
+ * can time the endpoint. `false` when either side is absent or empty.
422
+ */
423
+ timingSafeEqual(a: string | null | undefined, b: string | null | undefined): boolean;
424
+ }
425
+ /** The inputs `ctx.crypto.hmac` and `ctx.crypto.verifyHmac` share. */
426
+ interface OxyHmacInput {
427
+ /** `"sha256"` (default) or `"sha512"`. Anything else throws. */
428
+ algorithm?: "sha256" | "sha512";
429
+ /**
430
+ * The secret, from configuration (`ctx.env.…`), never from the request.
431
+ * Required and non-empty: an absent one throws rather than signing with `""`
432
+ * or the literal `"undefined"` — keys an attacker guesses as easily as you do.
433
+ */
434
+ key: string;
435
+ /** The base string to sign — for a webhook, the body or `v0:{ts}:{body}`. */
436
+ data: string;
437
+ /** How the digest is written (`hmac`) or read (`verifyHmac`): `"hex"` (default) or `"base64"`. */
438
+ encoding?: "hex" | "base64";
439
+ }
440
+ /** `ctx.crypto.verifyHmac`'s input: {@link OxyHmacInput} plus the signature to check. */
441
+ interface OxyVerifyHmacInput extends OxyHmacInput {
442
+ /**
443
+ * The bare digest the caller sent, provider prefix (`sha256=`, `v0=`) already
444
+ * stripped. Attacker-controlled, so a header the caller omitted may be passed
445
+ * as-is: absent, or not decodable in `encoding`, is `false`, never a throw.
446
+ */
447
+ signature: string | null | undefined;
448
+ }
276
449
  /** `ctx.semantic` — airlayer-compiled semantic queries (inherits the pre-agg fast path). */
277
450
  interface OxySemanticApi {
278
451
  /**
@@ -544,7 +717,10 @@ interface OxyStorageApi {
544
717
  delete(keyOrKeys: string | string[]): Promise<{
545
718
  deleted: number;
546
719
  }>;
547
- /** Server-side copy within the app's silo (requires `storage.write`). */
720
+ /**
721
+ * Server-side copy within the app's silo (requires `storage.read` **and**
722
+ * `storage.write`: it reads the source and writes the destination).
723
+ */
548
724
  copy(fromKey: string, toPathname: string, opts?: {
549
725
  allowOverwrite?: boolean;
550
726
  }): Promise<StoragePutResult>;
@@ -589,7 +765,8 @@ interface OxyOrgAssignment {
589
765
  /**
590
766
  * The data-plane context passed as the second argument to a function's default
591
767
  * export. Mirrors the host-assembled `ctx` (`__buildCtx` in `runtime.rs`);
592
- * every member is a host-provided async function bridged to a Rust backend.
768
+ * every member is a host-provided async function bridged to a Rust backend,
769
+ * except `crypto`, which is synchronous (pure CPU inside the isolate).
593
770
  */
594
771
  interface OxyFunctionContext {
595
772
  /** Invoking user (route) or system identity (schedule/airway). */
@@ -655,8 +832,22 @@ interface OxyFunctionContext {
655
832
  env: Record<string, string>;
656
833
  /** Structured per-invocation logging (captured + surfaced with the response). */
657
834
  log(...args: unknown[]): void;
658
- /** Read-only SQL (SELECT/WITH only), function-scoped row cap. Resolves to the rows. */
659
- query(sql: string): Promise<OxyFunctionRow[]>;
835
+ /**
836
+ * HMAC sign / verify and a constant-time compare. Synchronous — no `await`.
837
+ * See {@link OxyCryptoApi}.
838
+ */
839
+ crypto: OxyCryptoApi;
840
+ /**
841
+ * Read-only SQL (`SELECT` / `WITH` only) against the app's default database,
842
+ * capped at the function row limit. Resolves to `{ rows, truncated }` — the
843
+ * shape the host sends (`host.rs` `query`), the same as `ctx.warehouse.query`;
844
+ * `truncated` says the cap cut rows. Destructure it:
845
+ * `const { rows } = await ctx.query(sql)`.
846
+ */
847
+ query(sql: string): Promise<{
848
+ rows: OxyFunctionRow[];
849
+ truncated: boolean;
850
+ }>;
660
851
  /** Read-only SQL with a higher row cap, yielded to the caller in batches. */
661
852
  queryStream(sql: string, opts?: {
662
853
  batchSize?: number;
@@ -675,7 +866,11 @@ interface OxyFunctionContext {
675
866
  *
676
867
  * `database` must be in this function's manifest `destinations` — a
677
868
  * transaction is a write, and the same fail-closed allowlist applies. Postgres
678
- * only; other backends reject `ctx.tx` rather than faking it.
869
+ * only; other backends reject `ctx.tx` rather than faking it. A customer
870
+ * warehouse is refused unless named in `customerWarehouseWrites`; for the app's
871
+ * own OLTP store use `ctx.oltp.tx`, which needs no destination. A transaction
872
+ * counts as a write even when your callback only reads — `begin` cannot know —
873
+ * so for reads alone use `ctx.warehouse.query`.
679
874
  *
680
875
  * **Do not catch a failed statement and return normally.** A statement the
681
876
  * server rejects aborts the whole transaction, and `COMMIT` on an aborted
@@ -701,20 +896,28 @@ interface OxyFunctionContext {
701
896
  */
702
897
  tx<T>(database: string, fn: (tx: OxyTransaction) => Promise<T> | T): Promise<T>;
703
898
  /**
704
- * Read/write the app's OWN per-org OLTP schema (derived from its slug). The
705
- * write half `ctx.warehouse` cannot give an app on a managed database. Gated
706
- * by the fail-closed `oltp` manifest capability (`{ enabled: true }`). See
707
- * {@link OxyOltpApi}.
899
+ * **Records your app edits.** Read/write the app's OWN per-org OLTP schema
900
+ * (derived from its slug), with `ctx.oltp.tx` for several statements at once.
901
+ * Gated by the fail-closed `oltp` manifest capability (`{ enabled: true }`).
902
+ * See {@link OxyOltpApi}.
708
903
  */
709
904
  oltp: OxyOltpApi;
905
+ /**
906
+ * **Facts: what happened.** Append-only history in the app's own Airhouse
907
+ * schema, written as the app. Gated by the fail-closed `airhouse` manifest
908
+ * capability (`{ enabled: true }`). See {@link OxyAirhouseApi}.
909
+ */
910
+ airhouse: OxyAirhouseApi;
911
+ /** Credentials the app rotates — not a state store. See {@link OxySecretsApi}. */
710
912
  secrets: OxySecretsApi;
711
913
  semantic: OxySemanticApi;
712
914
  airway: OxyAirwayApi;
713
915
  email: OxyEmailApi;
916
+ /** **Files: bytes.** The app's private storage silo. See {@link OxyStorageApi}. */
714
917
  storage: OxyStorageApi;
715
918
  }
716
919
  /** Signature of a function's default export: `export default async (req, ctx) => Response`. */
717
920
  type OxyFunctionHandler = (req: OxyFunctionRequest, ctx: OxyFunctionContext) => Promise<Response> | Response;
718
921
  //#endregion
719
- export { StorageDownloadUrl as C, StoragePutResult as D, StoragePutOptions as E, StorageUploadUrl as O, OxyWarehouseApi as S, StorageObject as T, OxyReach as _, OxyEmailApi as a, OxyStorageApi as b, OxyFunctionHandler as c, OxyFunctionUser as d, OxyIdentityKind as f, OxyOrgTeam as g, OxyOrgPlace as h, OxyAirwayApi as i, StorageUploadUrlInput as k, OxyFunctionRequest as l, OxyOrgAssignment as m, EmailSendInput as n, OxyFetchResult as o, OxyOltpApi as p, EmailSendResult as r, OxyFunctionContext as s, EmailAttachment as t, OxyFunctionRow as u, OxySecretsApi as v, StorageListPage as w, OxyTransaction as x, OxySemanticApi as y };
720
- //# sourceMappingURL=function-context-D8eyZuw_.d.mts.map
922
+ export { StoragePutOptions as A, OxyStorageApi as C, StorageDownloadUrl as D, OxyWarehouseApi as E, StorageUploadUrl as M, StorageUploadUrlInput as N, StorageListPage as O, OxySemanticApi as S, OxyVerifyHmacInput as T, OxyOrgAssignment as _, OxyAirwayApi as a, OxyReach as b, OxyFetchResult as c, OxyFunctionRequest as d, OxyFunctionRow as f, OxyOltpApi as g, OxyIdentityKind as h, OxyAirhouseApi as i, StoragePutResult as j, StorageObject as k, OxyFunctionContext as l, OxyHmacInput as m, EmailSendInput as n, OxyCryptoApi as o, OxyFunctionUser as p, EmailSendResult as r, OxyEmailApi as s, EmailAttachment as t, OxyFunctionHandler as u, OxyOrgPlace as v, OxyTransaction as w, OxySecretsApi as x, OxyOrgTeam as y };
923
+ //# sourceMappingURL=function-context-BNpL5bFb.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"function-context-BNpL5bFb.d.mts","names":[],"sources":["../src/custom-app/function-context.ts"],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -1,8 +1,9 @@
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-DcT-mUPj.cjs');
3
+ const require_rolldown_runtime = require('./rolldown-runtime-KC0qvQup.cjs');
4
+ const require_react = require('./react-Dvkv2deI.cjs');
4
5
  let react = require("react");
5
- react = require_react.__toESM(react, 1);
6
+ react = require_rolldown_runtime.__toESM(react, 1);
6
7
 
7
8
  //#region src/anomalies.ts
8
9
  /** Which buckets a write may touch when the caller didn't say. Live statuses
@@ -318,7 +319,7 @@ function useMetricTreeEndpoint(key, run, enabled) {
318
319
  const [data, setData] = react.useState(null);
319
320
  const [loading, setLoading] = react.useState(enabled && key !== null);
320
321
  const [error, setError] = react.useState(null);
321
- const [_nonce, setNonce] = react.useState(0);
322
+ const [nonce, setNonce] = react.useState(0);
322
323
  const runRef = react.useRef(run);
323
324
  runRef.current = run;
324
325
  react.useEffect(() => {
@@ -336,15 +337,18 @@ function useMetricTreeEndpoint(key, run, enabled) {
336
337
  setLoading(false);
337
338
  }).catch((err) => {
338
339
  if (cancelled) return;
339
- if (err instanceof DOMException && err.name === "AbortError") return;
340
- setError(err instanceof Error ? err : new Error(String(err)));
340
+ setError(require_react.asReportableError(err));
341
341
  setLoading(false);
342
342
  });
343
343
  return () => {
344
344
  cancelled = true;
345
345
  ctrl.abort();
346
346
  };
347
- }, [key, enabled]);
347
+ }, [
348
+ key,
349
+ enabled,
350
+ nonce
351
+ ]);
348
352
  return {
349
353
  data,
350
354
  loading,
@@ -563,7 +567,7 @@ function useWorldModelGraph(opts = {}) {
563
567
  const [data, setData] = react.useState(null);
564
568
  const [loading, setLoading] = react.useState(enabled && !!projectId);
565
569
  const [error, setError] = react.useState(null);
566
- const [_nonce, setNonce] = react.useState(0);
570
+ const [nonce, setNonce] = react.useState(0);
567
571
  react.useEffect(() => {
568
572
  if (!enabled || !projectId) {
569
573
  setLoading(false);
@@ -585,8 +589,7 @@ function useWorldModelGraph(opts = {}) {
585
589
  setLoading(false);
586
590
  }).catch((err) => {
587
591
  if (cancelled) return;
588
- if (err instanceof DOMException && err.name === "AbortError") return;
589
- setError(err instanceof Error ? err : new Error(String(err)));
592
+ setError(require_react.asReportableError(err));
590
593
  setLoading(false);
591
594
  });
592
595
  return () => {
@@ -596,7 +599,8 @@ function useWorldModelGraph(opts = {}) {
596
599
  }, [
597
600
  enabled,
598
601
  projectId,
599
- fetcher
602
+ fetcher,
603
+ nonce
600
604
  ]);
601
605
  return {
602
606
  data,
@@ -617,7 +621,7 @@ function useWorldModelInstances(entityId, opts = {}) {
617
621
  const [data, setData] = react.useState(null);
618
622
  const [loading, setLoading] = react.useState(enabled && !!projectId && !!entityId);
619
623
  const [error, setError] = react.useState(null);
620
- const [_nonce, setNonce] = react.useState(0);
624
+ const [nonce, setNonce] = react.useState(0);
621
625
  react.useEffect(() => {
622
626
  if (!enabled || !projectId || !entityId) {
623
627
  setLoading(false);
@@ -646,8 +650,7 @@ function useWorldModelInstances(entityId, opts = {}) {
646
650
  setLoading(false);
647
651
  }).catch((err) => {
648
652
  if (cancelled) return;
649
- if (err instanceof DOMException && err.name === "AbortError") return;
650
- setError(err instanceof Error ? err : new Error(String(err)));
653
+ setError(require_react.asReportableError(err));
651
654
  setLoading(false);
652
655
  });
653
656
  return () => {
@@ -662,7 +665,8 @@ function useWorldModelInstances(entityId, opts = {}) {
662
665
  limit,
663
666
  fetcher,
664
667
  scope,
665
- appId
668
+ appId,
669
+ nonce
666
670
  ]);
667
671
  return {
668
672
  data,
@@ -740,8 +744,7 @@ function useMeasureBreakdown(entityId, keyValue, measure) {
740
744
  if (!cancelled) setLoading(false);
741
745
  }).catch((err) => {
742
746
  if (cancelled) return;
743
- if (err instanceof DOMException && err.name === "AbortError") return;
744
- setError(err instanceof Error ? err : new Error(String(err)));
747
+ setError(require_react.asReportableError(err));
745
748
  setLoading(false);
746
749
  });
747
750
  return () => {
@@ -1086,6 +1089,66 @@ var MetricTreeClient = class {
1086
1089
  }
1087
1090
  };
1088
1091
 
1092
+ //#endregion
1093
+ //#region src/peerCohort.ts
1094
+ /**
1095
+ * Client for the `/semantic/cohort` endpoint. Surfaces airlayer's peer-cohort
1096
+ * benchmarking — comparing an entity's subjects against their declared peers
1097
+ * on a measure, with exclusions explained rather than silently dropped.
1098
+ *
1099
+ * Construction is internal to {@link OxyClient} — call `client.peerCohort`
1100
+ * to access an instance rather than building one yourself.
1101
+ *
1102
+ * @example
1103
+ * ```typescript
1104
+ * const client = await OxyClient.create({ projectId: "...", apiKey: "..." });
1105
+ * const result = await client.peerCohort.resolve({
1106
+ * entity: "restaurant_id",
1107
+ * measure: "orders.net_revenue",
1108
+ * time_dimension: "orders.order_date",
1109
+ * period: ["2025-09-01", "2025-09-30"],
1110
+ * });
1111
+ * for (const excluded of result.excluded) {
1112
+ * console.warn(excluded.key, excluded.reason);
1113
+ * }
1114
+ * ```
1115
+ */
1116
+ var PeerCohortClient = class {
1117
+ constructor(config, request) {
1118
+ this.config = config;
1119
+ this.request = request;
1120
+ }
1121
+ path(suffix) {
1122
+ return `/${this.config.projectId}${suffix}`;
1123
+ }
1124
+ buildQuery(extra = {}) {
1125
+ const params = { ...extra };
1126
+ if (this.config.branch) params.branch = this.config.branch;
1127
+ const qs = new URLSearchParams(params).toString();
1128
+ return qs ? `?${qs}` : "";
1129
+ }
1130
+ /**
1131
+ * Resolve a subject's peer cohort and every peer's comparison against it.
1132
+ *
1133
+ * @example
1134
+ * ```typescript
1135
+ * const result = await client.peerCohort.resolve({
1136
+ * entity: "restaurant_id",
1137
+ * measure: "orders.net_revenue",
1138
+ * time_dimension: "orders.order_date",
1139
+ * period: ["2025-09-01", "2025-09-30"],
1140
+ * });
1141
+ * ```
1142
+ */
1143
+ async resolve(request) {
1144
+ const query = this.buildQuery();
1145
+ return this.request(this.path(`/semantic/cohort${query}`), {
1146
+ method: "POST",
1147
+ body: JSON.stringify(request)
1148
+ });
1149
+ }
1150
+ };
1151
+
1089
1152
  //#endregion
1090
1153
  exports.AnomaliesClient = AnomaliesClient;
1091
1154
  exports.MetricTreeClient = MetricTreeClient;
@@ -1093,6 +1156,7 @@ exports.OxyAnswer = require_react.OxyAnswer;
1093
1156
  exports.OxyApiError = require_react.OxyApiError;
1094
1157
  exports.OxyAppProvider = require_react.OxyAppProvider;
1095
1158
  exports.OxyChat = require_react.OxyChat;
1159
+ exports.PeerCohortClient = PeerCohortClient;
1096
1160
  exports.WorldModelScopeUnsupportedError = WorldModelScopeUnsupportedError;
1097
1161
  exports._resetCustomAppManifestCacheForTest = require_react._resetCustomAppManifestCacheForTest;
1098
1162
  exports.apiErrorFromResponse = require_react.apiErrorFromResponse;