@oddington/sdk 0.1.19 → 0.1.20

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,9 +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.
15
+ Hosted apps use `inbox.questions.list({ routines })` for `AnswerableQuestion[]`.
16
+ Derive `routines` from the existing app declaration with
17
+ `[...new Set(Object.values(appDeclaration.routines).map(binding => binding.routine))]`.
18
+ These inner canonical names narrow relevance only; they grant no answer or
19
+ Record-search authority. The ordinary authenticated Inbox context needs no app
20
+ identity or declaration provisioning. The filter is required; `[]` returns no
21
+ Questions after authentication. The SDK validates and privately snapshots the
22
+ filter across pagination. Local apps use `inbox.questions.list()` instead, bound
23
+ to their trusted host's declaration; any supplied filter is invalid locally.
24
+ Both use the same answer and Record-search methods, without a filter or prior list.
18
25
  `InboxItem` remains the complete first-party Inbox value. Record refs are placement
19
26
  hints only: match them against independently authorized Data, and omit ambiguous
20
27
  detail placement. Keep drafts on failed or uncertain acceptance, prevent duplicate
package/dist/browser.js CHANGED
@@ -475,7 +475,11 @@ var AppRoutineContractError = class extends Error {
475
475
  this.code = code;
476
476
  }
477
477
  code;
478
- }, propertyName = /^[a-z][A-Za-z0-9]*(?![\s\S])/, stepId = /^[a-z0-9]+(?:-[a-z0-9]+)*(?![\s\S])/, routineName = /^[a-z0-9][a-z0-9-]{0,62}(?![\s\S])/, owns = (value, key) => Object.hasOwn(value, key), invalidContract = () => {
478
+ }, propertyName = /^[a-z][A-Za-z0-9]*(?![\s\S])/, stepId = /^[a-z0-9]+(?:-[a-z0-9]+)*(?![\s\S])/, routineName = /^[a-z0-9][a-z0-9-]{0,62}(?![\s\S])/;
479
+ function isAppRoutineName(value) {
480
+ return typeof value == "string" && routineName.test(value);
481
+ }
482
+ var owns = (value, key) => Object.hasOwn(value, key), invalidContract = () => {
479
483
  throw new AppRoutineContractError("invalid_contract");
480
484
  }, invalidValue = () => {
481
485
  throw new AppRoutineContractError("invalid_value");
@@ -530,7 +534,7 @@ function parseAppRoutineContract(value) {
530
534
  return invalidContract();
531
535
  }
532
536
  }
533
- if (!record(value) || !fields2(value, ["routine", "input", "steps"], ["result"]) || typeof value.routine != "string" || !routineName.test(value.routine) || !denseArray(value.steps) || value.steps.length < 1 || value.steps.length > 64) return invalidContract();
537
+ if (!record(value) || !fields2(value, ["routine", "input", "steps"], ["result"]) || !isAppRoutineName(value.routine) || !denseArray(value.steps) || value.steps.length < 1 || value.steps.length > 64) return invalidContract();
534
538
  let input = schema2(value.input), result2 = owns(value, "result") ? schema2(value.result) : void 0, ids = /* @__PURE__ */ new Set(), steps = value.steps.map((step) => !record(step) || !fields2(step, ["id"], ["summary"]) || typeof step.id != "string" || !stepId.test(step.id) || ids.has(step.id) ? invalidContract() : (ids.add(step.id), { id: step.id, ...owns(step, "summary") ? { summary: schema2(step.summary) } : {} }));
535
539
  return { routine: value.routine, input, steps, ...result2 === void 0 ? {} : { result: result2 } };
536
540
  }
@@ -1937,14 +1941,14 @@ function response(value) {
1937
1941
  let answer = object3(value);
1938
1942
  return assert(Object.keys(answer).every((key) => ["selection", "note"].includes(key))), typeof answer.selection == "string" ? assert(text3(answer.selection) && answer.selection === answer.selection.trim()) : recordReference(answer.selection), Object.hasOwn(answer, "note") && assert(text3(answer.note)), answer;
1939
1943
  }
1940
- function parseInboxRequest(value) {
1944
+ function parseInboxRequest(value, questionListContext = "local") {
1941
1945
  try {
1942
1946
  let envelope = object3(value);
1943
1947
  assert(Object.keys(envelope).sort().join(",") === "input,operation"), assert(typeof envelope.operation == "string" && Object.hasOwn(operations, envelope.operation));
1944
- let operation2 = envelope.operation, input = object3(envelope.input), required = operations[operation2];
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"));
1948
+ let operation2 = envelope.operation, input = { ...object3(envelope.input) }, required = operations[operation2];
1949
+ 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" || operation2 === "questions.list" && questionListContext === "hosted" && key === "routines")), operation2 === "questions.list" && questionListContext === "hosted" && (assert(Object.hasOwn(input, "routines")), assert(array2(input.routines).every(isAppRoutineName)));
1946
1950
  for (let key of ["itemId", "personId", "fileId"]) key in input && assert(id2(input[key]));
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 };
1951
+ 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), "routines" in input && (input.routines = [...new Set(input.routines)]), { operation: operation2, input };
1948
1952
  } catch {
1949
1953
  throw new InboxOperationError(400, "invalid_input");
1950
1954
  }
@@ -2493,13 +2497,13 @@ function hostFailure(error, context2) {
2493
2497
  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();
2494
2498
  }
2495
2499
  async function send2(operation2, input) {
2496
- let envelope = parseInboxRequest({ operation: operation2, input }), context2;
2500
+ let envelope = operation2 === "questions.list" ? void 0 : parseInboxRequest({ operation: operation2, input }), context2;
2497
2501
  try {
2498
2502
  context2 = await resolveAppContext();
2499
2503
  } catch {
2500
2504
  throw stale();
2501
2505
  }
2502
- if (context2.kind === "local") {
2506
+ if (envelope ??= parseInboxRequest({ operation: operation2, input }, context2.kind === "local" ? "local" : "hosted"), context2.kind === "local") {
2503
2507
  if (!isAppQuestionOperation(operation2)) throw new InboxOperationError(403, "access_denied");
2504
2508
  let check2 = () => {
2505
2509
  if (!context2.isCurrent() || context2.signal.aborted || context2.expiresAt <= Date.now()) throw stale();
@@ -2636,13 +2640,17 @@ var inbox = {
2636
2640
  addRecipient: async (input) => await send2("items.addRecipient", input)
2637
2641
  },
2638
2642
  questions: {
2639
- async list() {
2640
- let questions = [], beforeNumber, initial;
2643
+ async list(input) {
2644
+ let supplied = input === void 0 ? void 0 : parseInboxRequest({ operation: "questions.list", input }, "hosted").input;
2645
+ if (supplied && Object.hasOwn(supplied, "beforeNumber")) throw new InboxOperationError(400, "invalid_input");
2646
+ let routines = supplied ? Object.freeze(supplied.routines) : void 0, questions = [], beforeNumber, initial;
2641
2647
  try {
2642
2648
  initial = await resolveAppContext();
2643
2649
  } catch {
2644
2650
  throw stale();
2645
2651
  }
2652
+ if (initial.kind === "local" ? input !== void 0 : routines === void 0)
2653
+ throw new InboxOperationError(400, "invalid_input");
2646
2654
  let host;
2647
2655
  try {
2648
2656
  host = initial.kind !== "local" && await resolveHostedInboxContext(initial);
@@ -2655,8 +2663,11 @@ var inbox = {
2655
2663
  };
2656
2664
  do {
2657
2665
  check();
2658
- let page = await send2("questions.list", beforeNumber === void 0 ? {} : { beforeNumber });
2659
- 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();
2666
+ let page = await send2("questions.list", {
2667
+ ...routines === void 0 ? {} : { routines },
2668
+ ...beforeNumber === void 0 ? {} : { beforeNumber }
2669
+ });
2670
+ if (check(), page.questions.some((question2) => question2.item.href !== `https://${initial.workspace}.oddington.ai/inbox/${question2.item.id}` || routines !== void 0 && !routines.includes(question2.origin.routine)) || beforeNumber !== void 0 && (page.questions.some((question2) => question2.item.number >= beforeNumber) || page.nextBeforeNumber !== null && page.nextBeforeNumber >= beforeNumber)) throw unavailable2();
2660
2671
  if (questions.push(...page.questions), page.nextBeforeNumber === null) return questions;
2661
2672
  beforeNumber = page.nextBeforeNumber;
2662
2673
  } while (!0);