@oddington/sdk 0.1.17 → 0.1.18

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
@@ -12,6 +12,16 @@ same key and content returns the original result; changed content conflicts.
12
12
  Existing Inbox-retained files remain separate from shared Files, with their
13
13
  original identities, bytes and audience. The SDK exposes no raw-credential API.
14
14
 
15
+ Focused apps use `inbox.questions.list()` for `AnswerableQuestion[]`, with relevance
16
+ bound by the trusted host to the canonical routines in the app declaration.
17
+ The same list, answer and Record-search methods work in hosted and local apps.
18
+ `InboxItem` remains the complete first-party Inbox value. Record refs are placement
19
+ hints only: match them against independently authorized Data, and omit ambiguous
20
+ detail placement. Keep drafts on failed or uncertain acceptance, prevent duplicate
21
+ interaction while submitting, and dismiss a Question only after acknowledged
22
+ acceptance. Refresh after a conflict; later effects remain Inbox work. These
23
+ interaction duties belong to the consuming app's interface.
24
+
15
25
  ```sh
16
26
  npm install @oddington/sdk
17
27
  ```
package/dist/browser.js CHANGED
@@ -1782,9 +1782,14 @@ var InboxOperationError = class extends Error {
1782
1782
  }
1783
1783
  status;
1784
1784
  reason;
1785
- }, INBOX_RESULT_BYTES = 8 * 1024 * 1024, INBOX_INPUT_BYTES = 8192, INBOX_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i, operations = {
1785
+ }, INBOX_RESULT_BYTES = 8 * 1024 * 1024, INBOX_INPUT_BYTES = 8192, INBOX_LOCAL_INPUT_BYTES = 256 * 1024, INBOX_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1786
+ function isAppQuestionOperation(operation2) {
1787
+ return operation2 === "questions.list" || operation2 === "questions.answer" || operation2 === "questions.searchRecords";
1788
+ }
1789
+ var operations = {
1786
1790
  current: [],
1787
1791
  "items.list": [],
1792
+ "questions.list": [],
1788
1793
  "items.read": ["itemId"],
1789
1794
  "items.see": ["itemId"],
1790
1795
  "items.markDigestRead": ["itemId"],
@@ -1937,7 +1942,7 @@ function parseInboxRequest(value) {
1937
1942
  let envelope = object3(value);
1938
1943
  assert(Object.keys(envelope).sort().join(",") === "input,operation"), assert(typeof envelope.operation == "string" && Object.hasOwn(operations, envelope.operation));
1939
1944
  let operation2 = envelope.operation, input = object3(envelope.input), required = operations[operation2];
1940
- assert(required.every((key) => Object.hasOwn(input, key)) && Object.keys(input).every((key) => required.includes(key) || operation2 === "questions.searchRecords" && key === "selected" || operation2 === "items.list" && key === "beforeNumber"));
1945
+ assert(required.every((key) => Object.hasOwn(input, key)) && Object.keys(input).every((key) => required.includes(key) || operation2 === "questions.searchRecords" && key === "selected" || (operation2 === "items.list" || operation2 === "questions.list") && key === "beforeNumber"));
1941
1946
  for (let key of ["itemId", "personId", "fileId"]) key in input && assert(id2(input[key]));
1942
1947
  return "revision" in input && assert(integer(input.revision)), "beforeNumber" in input && assert(integer(input.beforeNumber)), "questionId" in input && assert(text3(input.questionId) && input.questionId === input.questionId.trim()), "title" in input && assert(text3(input.title)), "lifecycle" in input && assert(input.lifecycle === "open" || input.lifecycle === "closed"), "query" in input && assert(text3(input.query, !0) && input.query.length <= 256), "selected" in input && recordReference(input.selected), "response" in input && response(input.response), assert(new TextEncoder().encode(JSON.stringify(envelope)).byteLength <= INBOX_INPUT_BYTES), { operation: operation2, input };
1943
1948
  } catch {
@@ -1990,12 +1995,32 @@ function parseInboxItem(value) {
1990
1995
  assert(id2(work.id) && text3(work.questionId) && text3(work.reason) && optionalDate(work.settledAt));
1991
1996
  }), item;
1992
1997
  }
1998
+ function parseAnswerableQuestion(value) {
1999
+ let entry = object3(json2(value));
2000
+ keys3(entry, ["item", "origin", "question", "recordRefs"]);
2001
+ let item = object3(entry.item);
2002
+ keys3(item, ["id", "number", "revision", "title", "href"]), assert(id2(item.id) && integer(item.number) && integer(item.revision) && text3(item.title) && text3(item.href));
2003
+ let link = new URL(item.href), slug = link.hostname.slice(0, -13);
2004
+ assert(link.hostname.endsWith(".oddington.ai") && slug.length <= 63 && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug) && !["auth", "www", "api", "os"].includes(slug) && item.href === `https://${slug}.oddington.ai/inbox/${item.id}`);
2005
+ let origin = object3(entry.origin);
2006
+ keys3(origin, ["routine"]), assert(text3(origin.routine) && origin.routine === origin.routine.trim());
2007
+ let question2 = object3(entry.question);
2008
+ keys3(question2, ["id", "prompt", "instructions", "response"]), assert(text3(question2.id) && question2.id === question2.id.trim() && text3(question2.prompt) && (question2.instructions === null || text3(question2.instructions, !0))), responseDefinition(question2.response);
2009
+ let refs = array2(entry.recordRefs);
2010
+ return assert(refs.every((ref) => text3(ref) && ref === ref.trim()) && new Set(refs).size === refs.length), entry;
2011
+ }
1993
2012
  function parseInboxAcceptance(value) {
1994
2013
  let accepted = object3(value);
1995
2014
  return assert(id2(accepted.responseId) && date(accepted.acceptedAt)), "executionId" in accepted && assert(id2(accepted.executionId)), "acceptanceOrder" in accepted && assert(integer(accepted.acceptanceOrder)), "label" in accepted && assert(text3(accepted.label)), accepted;
1996
2015
  }
1997
2016
  function parseInboxResult(operation2, value) {
1998
2017
  if (operation2 === "current") return parseInboxCurrent(value);
2018
+ if (operation2 === "questions.list") {
2019
+ let page = object3(value);
2020
+ keys3(page, ["questions", "nextBeforeNumber"]);
2021
+ let questions = array2(page.questions).map(parseAnswerableQuestion);
2022
+ return assert(page.nextBeforeNumber === null || integer(page.nextBeforeNumber)), { questions, nextBeforeNumber: page.nextBeforeNumber };
2023
+ }
1999
2024
  if (operation2 === "items.read") return parseInboxItem(value);
2000
2025
  if (operation2 === "items.list") {
2001
2026
  let page = object3(value), items = array2(page.items).map(parseInboxItem);
@@ -2063,128 +2088,61 @@ async function limitedBody(request, limit, timeoutMs) {
2063
2088
  }
2064
2089
  }
2065
2090
 
2066
- var unavailable2 = () => new InboxOperationError(503, "unavailable"), stale = () => new InboxOperationError(401, "authority_unavailable");
2067
- async function send2(operation2, input) {
2068
- let envelope = parseInboxRequest({ operation: operation2, input }), context2;
2069
- try {
2070
- context2 = await resolveAppContext();
2071
- } catch {
2072
- throw stale();
2091
+ var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
2092
+ function parseInboxHostContext(value) {
2093
+ if (!value || typeof value != "object" || Array.isArray(value)) throw new Error("Inbox context unavailable");
2094
+ let v = value;
2095
+ if (v.kind !== "inbox" && v.kind !== "focused" || Object.keys(v).sort().join(",") !== (v.kind === "inbox" ? "generation,kind,memberId,workspaceId" : "appId,generation,kind,memberId,workspaceId") || typeof v.workspaceId != "string" || !UUID.test(v.workspaceId) || typeof v.memberId != "string" || !UUID.test(v.memberId) || typeof v.generation != "string" || !UUID.test(v.generation) || v.kind === "focused" && (typeof v.appId != "string" || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(v.appId)))
2096
+ throw new Error("Inbox context unavailable");
2097
+ let base = { workspaceId: v.workspaceId, memberId: v.memberId, generation: v.generation };
2098
+ return Object.freeze(v.kind === "inbox" ? { ...base, kind: "inbox" } : { ...base, kind: "focused", appId: v.appId });
2099
+ }
2100
+
2101
+ var InboxContextError = class extends Error {
2102
+ constructor(status) {
2103
+ super("Inbox context unavailable");
2104
+ this.status = status;
2073
2105
  }
2074
- if (context2.kind === "local" || context2.endpoint !== `https://${context2.workspace}.oddington.ai` || ["auth", "www", "api", "os"].includes(context2.workspace)) throw stale();
2075
- let check = () => {
2076
- if (!context2.isCurrent() || !Number.isFinite(context2.expiresAt) || context2.expiresAt <= Date.now()) throw stale();
2077
- };
2078
- check();
2079
- try {
2080
- let response2 = await fetch(`${context2.endpoint}/api/inbox`, {
2081
- method: "POST",
2082
- headers: { authorization: `Bearer ${context2.accessToken}`, "content-type": "application/json" },
2083
- body: JSON.stringify(envelope),
2106
+ status;
2107
+ }, contexts = /* @__PURE__ */ new WeakMap();
2108
+ function resolveHostedInboxContext(host) {
2109
+ if (!host.binding || !host.memberId) throw new Error("Inbox context unavailable");
2110
+ let resolve = contexts.get(host.binding);
2111
+ return resolve || (resolve = hostedInboxContext(), contexts.set(host.binding, resolve)), resolve({ ...host, memberId: host.memberId });
2112
+ }
2113
+ function hostedInboxContext() {
2114
+ let installed2, revision = 0, issued = 0, settled = 0;
2115
+ return async (host) => {
2116
+ let started = ++issued, check = () => {
2117
+ if (!host.isCurrent() || host.expiresAt <= Date.now()) throw new Error("Inbox context unavailable");
2118
+ };
2119
+ check();
2120
+ let response2 = await fetch(`${host.endpoint}/api/inbox/context`, {
2121
+ method: "GET",
2122
+ headers: { authorization: `Bearer ${host.accessToken}` },
2123
+ credentials: "omit",
2084
2124
  redirect: "error",
2085
2125
  cache: "no-store",
2086
- signal: AbortSignal.timeout(3e4)
2126
+ signal: AbortSignal.timeout(15e3)
2087
2127
  });
2088
2128
  try {
2089
- check();
2090
- } catch (error) {
2091
- throw response2.body?.cancel().catch(() => {
2092
- }), error;
2093
- }
2094
- if (!response2.ok)
2095
- throw response2.body?.cancel().catch(() => {
2096
- }), new InboxOperationError(
2097
- [400, 401, 403, 409].includes(response2.status) ? response2.status : 503,
2098
- response2.status === 400 ? "invalid_input" : response2.status === 401 ? "authority_unavailable" : response2.status === 403 ? "access_denied" : response2.status === 409 ? "conflict" : "unavailable"
2099
- );
2100
- if (response2.status !== 200)
2101
- throw response2.body?.cancel().catch(() => {
2102
- }), unavailable2();
2103
- if (operation2 === "files.read") {
2104
- if (!response2.body) throw unavailable2();
2105
- let source = response2.body, reader, size = 0, cancel = async (reason) => {
2106
- try {
2107
- await (reader ? reader.cancel(reason) : source.cancel(reason));
2108
- } finally {
2109
- reader?.releaseLock();
2110
- }
2111
- }, body2 = new ReadableStream({
2112
- async pull(controller) {
2113
- try {
2114
- check(), reader ??= source.getReader();
2115
- let next = await reader.read();
2116
- if (check(), next.done) {
2117
- reader.releaseLock(), controller.close();
2118
- return;
2119
- }
2120
- if (size += next.value.byteLength, size > 25 * 1024 * 1024) throw unavailable2();
2121
- controller.enqueue(next.value);
2122
- } catch (error) {
2123
- controller.error(error), await cancel(error).catch(() => {
2124
- });
2125
- }
2126
- },
2127
- cancel
2128
- }, { highWaterMark: 0 });
2129
- return new Response(body2, { status: response2.status, headers: response2.headers });
2130
- }
2131
- let bytes = await limitedBody(response2, INBOX_RESULT_BYTES);
2132
- if (check(), !bytes) throw unavailable2();
2133
- let value = JSON.parse(new TextDecoder("utf-8", { fatal: !0 }).decode(bytes)), parsed = parseInboxResult(operation2, value);
2134
- if (check(), operation2 === "current" && parsed.workspace.slug !== context2.workspace) throw unavailable2();
2135
- if ("itemId" in envelope.input) {
2136
- let item = operation2 === "items.read" ? parsed : operation2 === "items.rename" || operation2 === "items.setLifecycle" || operation2 === "items.addRecipient" ? parsed.item : void 0;
2137
- if (item && (item.id !== envelope.input.itemId || "revision" in envelope.input && item.revision !== envelope.input.revision) || operation2 === "items.addRecipient" && parsed.addedPersonId !== envelope.input.personId || operation2 === "questions.answer" && typeof envelope.input.response.selection == "object" && !parsed.label?.trim()) throw unavailable2();
2138
- }
2139
- return parsed;
2140
- } catch (error) {
2141
- throw check(), error instanceof InboxOperationError ? error : unavailable2();
2142
- }
2143
- }
2144
- var inbox = {
2145
- current: async () => await send2("current", {}),
2146
- items: {
2147
- async list() {
2148
- let items = [], beforeNumber, initial;
2149
- try {
2150
- initial = await resolveAppContext();
2151
- } catch {
2152
- throw stale();
2129
+ if (check(), (response2.status === 401 || response2.status === 403) && (installed2 = void 0, revision++, settled = Math.max(settled, started)), !response2.ok || response2.status !== 200) throw new InboxContextError(response2.status);
2130
+ let bytes = await limitedBody(response2, 4096);
2131
+ if (check(), !bytes) throw new Error("Inbox context unavailable");
2132
+ let context2 = parseInboxHostContext(JSON.parse(new TextDecoder("utf-8", { fatal: !0 }).decode(bytes)));
2133
+ if (context2.memberId !== host.memberId) throw new Error("Inbox context unavailable");
2134
+ if (JSON.stringify(installed2) !== JSON.stringify(context2)) {
2135
+ if (started < settled) throw new Error("Inbox context unavailable");
2136
+ installed2 = context2, revision++;
2153
2137
  }
2154
- let check = () => {
2155
- if (!initial.isCurrent() || initial.expiresAt <= Date.now()) throw stale();
2156
- };
2157
- do {
2158
- check();
2159
- let page = await send2("items.list", beforeNumber === void 0 ? {} : { beforeNumber });
2160
- if (check(), beforeNumber !== void 0 && (page.items.some((item) => item.number >= beforeNumber) || page.nextBeforeNumber !== null && page.nextBeforeNumber >= beforeNumber)) throw unavailable2();
2161
- if (items.push(...page.items), page.nextBeforeNumber === null) return items;
2162
- beforeNumber = page.nextBeforeNumber;
2163
- } while (!0);
2164
- },
2165
- read: async (itemId) => await send2("items.read", { itemId }),
2166
- see: async (itemId) => {
2167
- await send2("items.see", { itemId });
2168
- },
2169
- markDigestRead: async (itemId) => await send2("items.markDigestRead", { itemId }),
2170
- rename: async (input) => await send2("items.rename", input),
2171
- setLifecycle: async (input) => await send2("items.setLifecycle", input),
2172
- listRecipientCandidates: async (itemId) => await send2("items.listRecipientCandidates", { itemId }),
2173
- addRecipient: async (input) => await send2("items.addRecipient", input)
2174
- },
2175
- questions: {
2176
- answer: async (input) => await send2("questions.answer", input),
2177
- searchRecords: async (input) => await send2("questions.searchRecords", input)
2178
- },
2179
- files: { read: async (input) => await send2("files.read", input) }
2180
- };
2181
-
2182
- var sources = /* @__PURE__ */ new WeakMap();
2183
- function registerBrowserAuth(handle2, source) {
2184
- sources.set(handle2, source);
2185
- }
2186
- function browserAuthSource(handle2) {
2187
- return sources.get(handle2);
2138
+ settled = Math.max(settled, started);
2139
+ let current2 = revision;
2140
+ return Object.freeze({ context: context2, isCurrent: () => host.isCurrent() && host.expiresAt > Date.now() && current2 === revision });
2141
+ } finally {
2142
+ response2.body?.cancel().catch(() => {
2143
+ });
2144
+ }
2145
+ };
2188
2146
  }
2189
2147
 
2190
2148
  var sessionKey = "_os.app.session", responseChecks = /* @__PURE__ */ new WeakMap(), AppResponseReadError = class extends Error {
@@ -2426,10 +2384,17 @@ function appContext(origin, active2) {
2426
2384
  throw error instanceof DataOperationError ? error : !current2() || controller.signal.aborted ? new DataOperationError("context_unavailable") : new DataOperationError("unavailable", !0);
2427
2385
  }
2428
2386
  }
2387
+ async function requestInbox(operation2, input) {
2388
+ if (!["questions.list", "questions.answer", "questions.searchRecords"].includes(operation2)) throw new Error("Inbox operation unavailable");
2389
+ return requestChecked(`${origin}/_os/app/inbox`, { method: "POST", body: JSON.stringify({ operation: operation2, input }) }, !0);
2390
+ }
2429
2391
  async function requestRoutine(key, suffix, init2 = {}) {
2392
+ return requestChecked(`${origin}/_os/app/routines/${encodeURIComponent(key)}/${suffix}`, init2);
2393
+ }
2394
+ async function requestChecked(url, init2, inbox2 = !1) {
2430
2395
  if (!current2())
2431
2396
  throw invalidate(), new Error("Local app context unavailable.");
2432
- let url = `${origin}/_os/app/routines/${encodeURIComponent(key)}/${suffix}`, admitted = credential, response2 = await fetch(url, {
2397
+ let admitted = credential, response2 = await fetch(url, {
2433
2398
  ...init2,
2434
2399
  credentials: "omit",
2435
2400
  redirect: "error",
@@ -2441,7 +2406,7 @@ function appContext(origin, active2) {
2441
2406
  for (await synchronize(); alive() && stored() !== credential; ) await synchronize();
2442
2407
  if (!current2() || response2.redirected || response2.url && response2.url !== url)
2443
2408
  throw invalidate(), new Error("Local app context unavailable.");
2444
- return (response2.status === 401 || response2.status === 403) && admitted === credential && invalidate(), responseChecks.set(response2, async (value) => {
2409
+ return (response2.status === 401 || !inbox2 && response2.status === 403) && admitted === credential && invalidate(), responseChecks.set(response2, async (value) => {
2445
2410
  for (await synchronize(); alive() && stored() !== credential; ) await synchronize();
2446
2411
  admitted === credential && value && typeof value == "object" && "error" in value && value.error === "authority_unavailable" && invalidate();
2447
2412
  }), response2;
@@ -2461,6 +2426,7 @@ function appContext(origin, active2) {
2461
2426
  },
2462
2427
  requestRoutine,
2463
2428
  requestData,
2429
+ requestInbox,
2464
2430
  requestAvailability: (key) => requestRoutine(key, "availability", { method: "GET" })
2465
2431
  };
2466
2432
  }
@@ -2503,6 +2469,193 @@ function validBinding(value) {
2503
2469
  return typeof binding.appId == "string" && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(binding.appId) && typeof binding.workspace == "string" && /^[a-z0-9][a-z0-9-]{0,62}$/.test(binding.workspace) && typeof binding.memberId == "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(binding.memberId) && typeof binding.sessionId == "string" && /^[A-Za-z0-9_-]{43}$/.test(binding.sessionId) && typeof binding.expiresAt == "number" && Number.isFinite(binding.expiresAt);
2504
2470
  }
2505
2471
 
2472
+ var unavailable2 = () => new InboxOperationError(503, "unavailable"), stale = () => new InboxOperationError(401, "authority_unavailable");
2473
+ function hostFailure(error, context2) {
2474
+ return !context2.isCurrent() || context2.expiresAt <= Date.now() || error instanceof InboxContextError && error.status === 401 ? stale() : error instanceof InboxContextError && error.status === 403 ? new InboxOperationError(403, "access_denied") : unavailable2();
2475
+ }
2476
+ async function send2(operation2, input) {
2477
+ let envelope = parseInboxRequest({ operation: operation2, input }), context2;
2478
+ try {
2479
+ context2 = await resolveAppContext();
2480
+ } catch {
2481
+ throw stale();
2482
+ }
2483
+ if (context2.kind === "local") {
2484
+ if (!isAppQuestionOperation(operation2)) throw new InboxOperationError(403, "access_denied");
2485
+ let check2 = () => {
2486
+ if (!context2.isCurrent() || context2.signal.aborted || context2.expiresAt <= Date.now()) throw stale();
2487
+ };
2488
+ try {
2489
+ let response2 = await context2.requestInbox(operation2, envelope.input), value = await appResponse(response2, INBOX_RESULT_BYTES, context2.signal);
2490
+ if (check2(), !response2.ok) throw new InboxOperationError(
2491
+ [400, 401, 403, 409].includes(response2.status) ? response2.status : 503,
2492
+ response2.status === 400 ? "invalid_input" : response2.status === 401 ? "authority_unavailable" : response2.status === 403 ? "access_denied" : response2.status === 409 ? "conflict" : "unavailable"
2493
+ );
2494
+ if (response2.status !== 200) throw unavailable2();
2495
+ let parsed = parseInboxResult(operation2, value);
2496
+ if (check2(), operation2 === "questions.answer" && typeof envelope.input.response.selection == "object" && !parsed.label?.trim()) throw unavailable2();
2497
+ return parsed;
2498
+ } catch (error) {
2499
+ throw check2(), error instanceof InboxOperationError ? error : unavailable2();
2500
+ }
2501
+ }
2502
+ if (context2.endpoint !== `https://${context2.workspace}.oddington.ai` || ["auth", "www", "api", "os"].includes(context2.workspace)) throw stale();
2503
+ let host;
2504
+ try {
2505
+ host = await resolveHostedInboxContext(context2);
2506
+ } catch (error) {
2507
+ throw hostFailure(error, context2);
2508
+ }
2509
+ if (!host) throw unavailable2();
2510
+ let check = () => {
2511
+ if (!context2.isCurrent() || !host.isCurrent() || !Number.isFinite(context2.expiresAt) || context2.expiresAt <= Date.now()) throw stale();
2512
+ };
2513
+ if (check(), host.context.kind === "focused" && !isAppQuestionOperation(operation2))
2514
+ throw new InboxOperationError(403, "access_denied");
2515
+ try {
2516
+ let response2 = await fetch(`${context2.endpoint}/api/inbox`, {
2517
+ method: "POST",
2518
+ headers: { authorization: `Bearer ${context2.accessToken}`, "content-type": "application/json" },
2519
+ body: JSON.stringify(envelope),
2520
+ redirect: "error",
2521
+ cache: "no-store",
2522
+ signal: AbortSignal.timeout(3e4)
2523
+ });
2524
+ try {
2525
+ check();
2526
+ } catch (error) {
2527
+ throw response2.body?.cancel().catch(() => {
2528
+ }), error;
2529
+ }
2530
+ if (!response2.ok)
2531
+ throw response2.body?.cancel().catch(() => {
2532
+ }), new InboxOperationError(
2533
+ [400, 401, 403, 409].includes(response2.status) ? response2.status : 503,
2534
+ response2.status === 400 ? "invalid_input" : response2.status === 401 ? "authority_unavailable" : response2.status === 403 ? "access_denied" : response2.status === 409 ? "conflict" : "unavailable"
2535
+ );
2536
+ if (response2.status !== 200)
2537
+ throw response2.body?.cancel().catch(() => {
2538
+ }), unavailable2();
2539
+ if (operation2 === "files.read") {
2540
+ if (!response2.body) throw unavailable2();
2541
+ let source = response2.body, reader, size = 0, cancel = async (reason) => {
2542
+ try {
2543
+ await (reader ? reader.cancel(reason) : source.cancel(reason));
2544
+ } finally {
2545
+ reader?.releaseLock();
2546
+ }
2547
+ }, body2 = new ReadableStream({
2548
+ async pull(controller) {
2549
+ try {
2550
+ check(), reader ??= source.getReader();
2551
+ let next = await reader.read();
2552
+ if (check(), next.done) {
2553
+ reader.releaseLock(), controller.close();
2554
+ return;
2555
+ }
2556
+ if (size += next.value.byteLength, size > 25 * 1024 * 1024) throw unavailable2();
2557
+ controller.enqueue(next.value);
2558
+ } catch (error) {
2559
+ controller.error(error), await cancel(error).catch(() => {
2560
+ });
2561
+ }
2562
+ },
2563
+ cancel
2564
+ }, { highWaterMark: 0 });
2565
+ return new Response(body2, { status: response2.status, headers: response2.headers });
2566
+ }
2567
+ let bytes = await limitedBody(response2, INBOX_RESULT_BYTES);
2568
+ if (check(), !bytes) throw unavailable2();
2569
+ let value = JSON.parse(new TextDecoder("utf-8", { fatal: !0 }).decode(bytes)), parsed = parseInboxResult(operation2, value);
2570
+ if (check(), operation2 === "current" && parsed.workspace.slug !== context2.workspace) throw unavailable2();
2571
+ if ("itemId" in envelope.input) {
2572
+ let item = operation2 === "items.read" ? parsed : operation2 === "items.rename" || operation2 === "items.setLifecycle" || operation2 === "items.addRecipient" ? parsed.item : void 0;
2573
+ if (item && (item.id !== envelope.input.itemId || "revision" in envelope.input && item.revision !== envelope.input.revision) || operation2 === "items.addRecipient" && parsed.addedPersonId !== envelope.input.personId || operation2 === "questions.answer" && typeof envelope.input.response.selection == "object" && !parsed.label?.trim()) throw unavailable2();
2574
+ }
2575
+ return parsed;
2576
+ } catch (error) {
2577
+ throw check(), error instanceof InboxOperationError ? error : unavailable2();
2578
+ }
2579
+ }
2580
+ var inbox = {
2581
+ current: async () => await send2("current", {}),
2582
+ items: {
2583
+ async list() {
2584
+ let items = [], beforeNumber, initial;
2585
+ try {
2586
+ initial = await resolveAppContext();
2587
+ } catch {
2588
+ throw stale();
2589
+ }
2590
+ let host;
2591
+ try {
2592
+ host = initial.kind !== "local" && await resolveHostedInboxContext(initial);
2593
+ } catch (error) {
2594
+ throw hostFailure(error, initial);
2595
+ }
2596
+ if (initial.kind === "local") throw new InboxOperationError(403, "access_denied");
2597
+ if (!host) throw unavailable2();
2598
+ let check = () => {
2599
+ if (!initial.isCurrent() || !host.isCurrent() || initial.expiresAt <= Date.now()) throw stale();
2600
+ };
2601
+ do {
2602
+ check();
2603
+ let page = await send2("items.list", beforeNumber === void 0 ? {} : { beforeNumber });
2604
+ if (check(), beforeNumber !== void 0 && (page.items.some((item) => item.number >= beforeNumber) || page.nextBeforeNumber !== null && page.nextBeforeNumber >= beforeNumber)) throw unavailable2();
2605
+ if (items.push(...page.items), page.nextBeforeNumber === null) return items;
2606
+ beforeNumber = page.nextBeforeNumber;
2607
+ } while (!0);
2608
+ },
2609
+ read: async (itemId) => await send2("items.read", { itemId }),
2610
+ see: async (itemId) => {
2611
+ await send2("items.see", { itemId });
2612
+ },
2613
+ markDigestRead: async (itemId) => await send2("items.markDigestRead", { itemId }),
2614
+ rename: async (input) => await send2("items.rename", input),
2615
+ setLifecycle: async (input) => await send2("items.setLifecycle", input),
2616
+ listRecipientCandidates: async (itemId) => await send2("items.listRecipientCandidates", { itemId }),
2617
+ addRecipient: async (input) => await send2("items.addRecipient", input)
2618
+ },
2619
+ questions: {
2620
+ async list() {
2621
+ let questions = [], beforeNumber, initial;
2622
+ try {
2623
+ initial = await resolveAppContext();
2624
+ } catch {
2625
+ throw stale();
2626
+ }
2627
+ let host;
2628
+ try {
2629
+ host = initial.kind !== "local" && await resolveHostedInboxContext(initial);
2630
+ } catch (error) {
2631
+ throw hostFailure(error, initial);
2632
+ }
2633
+ if (initial.kind !== "local" && !host) throw unavailable2();
2634
+ let check = () => {
2635
+ if (!initial.isCurrent() || host && !host.isCurrent() || initial.expiresAt <= Date.now()) throw stale();
2636
+ };
2637
+ do {
2638
+ check();
2639
+ let page = await send2("questions.list", beforeNumber === void 0 ? {} : { beforeNumber });
2640
+ if (check(), page.questions.some((question2) => question2.item.href !== `https://${initial.workspace}.oddington.ai/inbox/${question2.item.id}`) || beforeNumber !== void 0 && (page.questions.some((question2) => question2.item.number >= beforeNumber) || page.nextBeforeNumber !== null && page.nextBeforeNumber >= beforeNumber)) throw unavailable2();
2641
+ if (questions.push(...page.questions), page.nextBeforeNumber === null) return questions;
2642
+ beforeNumber = page.nextBeforeNumber;
2643
+ } while (!0);
2644
+ },
2645
+ answer: async (input) => await send2("questions.answer", input),
2646
+ searchRecords: async (input) => await send2("questions.searchRecords", input)
2647
+ },
2648
+ files: { read: async (input) => await send2("files.read", input) }
2649
+ };
2650
+
2651
+ var sources = /* @__PURE__ */ new WeakMap();
2652
+ function registerBrowserAuth(handle2, source) {
2653
+ sources.set(handle2, source);
2654
+ }
2655
+ function browserAuthSource(handle2) {
2656
+ return sources.get(handle2);
2657
+ }
2658
+
2506
2659
  var installedDispose;
2507
2660
  function init({ auth: input }) {
2508
2661
  installedDispose?.();
@@ -2521,7 +2674,7 @@ function init({ auth: input }) {
2521
2674
  url.protocol === "https:" && !url.port && !url.username && !url.password && !url.search && !url.hash && url.pathname === "/" && /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(slug) && slug !== "auth" && (endpoint = url.origin, workspace = slug);
2522
2675
  } catch {
2523
2676
  }
2524
- let active2 = !0, revision = 0, person, token, local = localOrigin ? appContext(localOrigin, () => active2) : void 0, subscription, stopDisposal, uninstall = installMemberContext(async () => {
2677
+ let active2 = !0, revision = 0, person, token, binding = {}, local = localOrigin ? appContext(localOrigin, () => active2) : void 0, subscription, stopDisposal, uninstall = installMemberContext(async () => {
2525
2678
  if (local) return local.resolve();
2526
2679
  if (!active2 || !endpoint || !workspace || globalThis.location.origin !== endpoint) throw new Error("Hosted Data context unavailable.");
2527
2680
  let started = revision, { data: { session }, error } = await auth2.getSession();
@@ -2530,12 +2683,16 @@ function init({ auth: input }) {
2530
2683
  let expiresAt = (session.expires_at ?? 0) * 1e3;
2531
2684
  if (!Number.isFinite(expiresAt) || expiresAt <= Date.now() || !session.user.id)
2532
2685
  throw new Error("Hosted Data context unavailable.");
2533
- return person = session.user.id, {
2686
+ person = session.user.id;
2687
+ let isCurrent = () => active2 && globalThis.location.origin === endpoint && revision === started && person === session.user.id;
2688
+ return {
2534
2689
  workspace,
2535
2690
  endpoint,
2536
2691
  accessToken: session.access_token,
2537
2692
  expiresAt,
2538
- isCurrent: () => active2 && globalThis.location.origin === endpoint && revision === started && person === session.user.id
2693
+ isCurrent,
2694
+ memberId: session.user.id,
2695
+ binding
2539
2696
  };
2540
2697
  }), dispose = () => {
2541
2698
  active2 && (active2 = !1, local?.dispose(), revision++, subscription?.unsubscribe(), stopDisposal?.(), uninstall(), installedDispose === dispose && (installedDispose = void 0));