@superbuilders/primer-tives 4.0.5 → 5.0.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 (62) hide show
  1. package/README.md +36 -8
  2. package/dist/client/auth/access-token.d.ts +1 -1
  3. package/dist/client/auth/access-token.d.ts.map +1 -1
  4. package/dist/client/auth/hosted-popup.d.ts +1 -1
  5. package/dist/client/auth/hosted-popup.d.ts.map +1 -1
  6. package/dist/client/auth/provider.d.ts +2 -2
  7. package/dist/client/auth/provider.d.ts.map +1 -1
  8. package/dist/client/auth-state.d.ts +2 -2
  9. package/dist/client/auth-state.d.ts.map +1 -1
  10. package/dist/client/choice-state.d.ts +3 -3
  11. package/dist/client/choice-state.d.ts.map +1 -1
  12. package/dist/client/extended-text-state.d.ts +3 -3
  13. package/dist/client/extended-text-state.d.ts.map +1 -1
  14. package/dist/client/feedback-state.d.ts +6 -5
  15. package/dist/client/feedback-state.d.ts.map +1 -1
  16. package/dist/client/index.d.ts +2 -2
  17. package/dist/client/index.d.ts.map +1 -1
  18. package/dist/client/index.js +883 -737
  19. package/dist/client/index.js.map +24 -24
  20. package/dist/client/match-state.d.ts +3 -3
  21. package/dist/client/match-state.d.ts.map +1 -1
  22. package/dist/client/observation-state.d.ts +1 -1
  23. package/dist/client/observation-state.d.ts.map +1 -1
  24. package/dist/client/order-state.d.ts +3 -3
  25. package/dist/client/order-state.d.ts.map +1 -1
  26. package/dist/client/pci-state.d.ts +3 -3
  27. package/dist/client/pci-state.d.ts.map +1 -1
  28. package/dist/client/session-context.d.ts +2 -2
  29. package/dist/client/session-context.d.ts.map +1 -1
  30. package/dist/client/session.d.ts +4 -4
  31. package/dist/client/session.d.ts.map +1 -1
  32. package/dist/client/start.d.ts +4 -3
  33. package/dist/client/start.d.ts.map +1 -1
  34. package/dist/client/text-entry-state.d.ts +3 -3
  35. package/dist/client/text-entry-state.d.ts.map +1 -1
  36. package/dist/client/transport.d.ts +12 -6
  37. package/dist/client/transport.d.ts.map +1 -1
  38. package/dist/client/types.d.ts +32 -3
  39. package/dist/client/types.d.ts.map +1 -1
  40. package/dist/contracts/index.d.ts +4 -4
  41. package/dist/contracts/index.d.ts.map +1 -1
  42. package/dist/contracts/index.js +12 -12
  43. package/dist/contracts/index.js.map +6 -6
  44. package/dist/contracts/pci-schemas.d.ts +4 -0
  45. package/dist/contracts/pci-schemas.d.ts.map +1 -1
  46. package/dist/contracts/pci.d.ts +1 -1
  47. package/dist/contracts/pci.d.ts.map +1 -1
  48. package/dist/contracts/review.d.ts +1 -1
  49. package/dist/contracts/review.d.ts.map +1 -1
  50. package/dist/contracts/validation.d.ts +24 -2
  51. package/dist/contracts/validation.d.ts.map +1 -1
  52. package/dist/grade-level.d.ts +1 -1
  53. package/dist/grade-level.d.ts.map +1 -1
  54. package/dist/grade-level.js.map +1 -1
  55. package/dist/subject-pcis.d.ts +1 -1
  56. package/dist/subject-pcis.d.ts.map +1 -1
  57. package/dist/subject-pcis.js.map +2 -2
  58. package/dist/subject.d.ts +1 -1
  59. package/dist/subject.d.ts.map +1 -1
  60. package/dist/subject.js.map +1 -1
  61. package/dist/version.d.ts +1 -1
  62. package/package.json +1 -1
@@ -6461,14 +6461,14 @@ var FractionInputSubmissionDraft07Schema = {
6461
6461
  type: "object",
6462
6462
  additionalProperties: false,
6463
6463
  required: ["form", "whole"],
6464
- properties: { form: { const: "whole" }, whole: { type: "string" } }
6464
+ properties: { form: { type: "string", const: "whole" }, whole: { type: "string" } }
6465
6465
  },
6466
6466
  {
6467
6467
  type: "object",
6468
6468
  additionalProperties: false,
6469
6469
  required: ["form", "numerator", "denominator"],
6470
6470
  properties: {
6471
- form: { const: "proper" },
6471
+ form: { type: "string", const: "proper" },
6472
6472
  numerator: { type: "string" },
6473
6473
  denominator: { type: "string" }
6474
6474
  }
@@ -6478,7 +6478,7 @@ var FractionInputSubmissionDraft07Schema = {
6478
6478
  additionalProperties: false,
6479
6479
  required: ["form", "numerator", "denominator"],
6480
6480
  properties: {
6481
- form: { const: "improper" },
6481
+ form: { type: "string", const: "improper" },
6482
6482
  numerator: { type: "string" },
6483
6483
  denominator: { type: "string" }
6484
6484
  }
@@ -6488,7 +6488,7 @@ var FractionInputSubmissionDraft07Schema = {
6488
6488
  additionalProperties: false,
6489
6489
  required: ["form", "whole", "numerator", "denominator"],
6490
6490
  properties: {
6491
- form: { const: "mixed" },
6491
+ form: { type: "string", const: "mixed" },
6492
6492
  whole: { type: "string" },
6493
6493
  numerator: { type: "string" },
6494
6494
  denominator: { type: "string" }
@@ -6510,7 +6510,7 @@ var ChoiceSubmissionSchema = {
6510
6510
  additionalProperties: false,
6511
6511
  required: ["type", "selectedKeys"],
6512
6512
  properties: {
6513
- type: { const: "choice" },
6513
+ type: { type: "string", const: "choice" },
6514
6514
  selectedKeys: { type: "array", items: { type: "string" } }
6515
6515
  }
6516
6516
  };
@@ -6518,14 +6518,14 @@ var TextEntrySubmissionSchema = {
6518
6518
  type: "object",
6519
6519
  additionalProperties: false,
6520
6520
  required: ["type", "value"],
6521
- properties: { type: { const: "text-entry" }, value: { type: "string" } }
6521
+ properties: { type: { type: "string", const: "text-entry" }, value: { type: "string" } }
6522
6522
  };
6523
6523
  var ExtendedTextSubmissionSchema = {
6524
6524
  type: "object",
6525
6525
  additionalProperties: false,
6526
6526
  required: ["type", "values"],
6527
6527
  properties: {
6528
- type: { const: "extended-text" },
6528
+ type: { type: "string", const: "extended-text" },
6529
6529
  values: { type: "array", minItems: 1, items: { type: "string" } }
6530
6530
  }
6531
6531
  };
@@ -6534,7 +6534,7 @@ var OrderSubmissionSchema = {
6534
6534
  additionalProperties: false,
6535
6535
  required: ["type", "orderedKeys"],
6536
6536
  properties: {
6537
- type: { const: "order" },
6537
+ type: { type: "string", const: "order" },
6538
6538
  orderedKeys: { type: "array", items: { type: "string" } }
6539
6539
  }
6540
6540
  };
@@ -6543,7 +6543,7 @@ var MatchSubmissionSchema = {
6543
6543
  additionalProperties: false,
6544
6544
  required: ["type", "pairs"],
6545
6545
  properties: {
6546
- type: { const: "match" },
6546
+ type: { type: "string", const: "match" },
6547
6547
  pairs: { type: "array", items: MatchPairSchema }
6548
6548
  }
6549
6549
  };
@@ -6552,8 +6552,8 @@ var FractionInputPciSubmissionSchema = {
6552
6552
  additionalProperties: false,
6553
6553
  required: ["type", "pciId", "value"],
6554
6554
  properties: {
6555
- type: { const: "portable-custom" },
6556
- pciId: { const: "urn:primer:pci:fraction-input" },
6555
+ type: { type: "string", const: "portable-custom" },
6556
+ pciId: { type: "string", const: "urn:primer:pci:fraction-input" },
6557
6557
  value: FractionInputSubmissionDraft07Schema
6558
6558
  }
6559
6559
  };
@@ -6779,217 +6779,518 @@ function validateSubmissionForInteraction(interaction, submission) {
6779
6779
  function submissionValidationMessage(result) {
6780
6780
  return result.issues.join("; ");
6781
6781
  }
6782
- // src/client/transport.ts
6783
- import * as errors3 from "@superbuilders/errors";
6784
-
6785
- // src/version.ts
6786
- var SDK_VERSION = "4.0.5";
6787
- var NPM_PACKAGE_URL = "https://www.npmjs.com/package/@superbuilders/primer-tives";
6782
+ // src/client/auth/provider.ts
6783
+ import * as errors4 from "@superbuilders/errors";
6788
6784
 
6789
- // src/client/transport.ts
6790
- var ADVANCE_PATH = "/api/v0/advance";
6791
- function readStringField(value, key) {
6792
- if (!(key in value)) {
6793
- return;
6794
- }
6795
- const v = Reflect.get(value, key);
6796
- if (typeof v !== "string") {
6797
- return;
6785
+ // src/client/auth/access-token.ts
6786
+ import * as errors3 from "@superbuilders/errors";
6787
+ var ACCESS_TOKEN_PREFIX = "eyJ";
6788
+ var TOKEN_EXPIRY_SKEW_MS = 60000;
6789
+ var resolvedAccessTokenBrand = Symbol("primer resolved access token");
6790
+ function isMalformedJws(token) {
6791
+ if (!token.startsWith(ACCESS_TOKEN_PREFIX)) {
6792
+ return true;
6798
6793
  }
6799
- return v;
6794
+ const dotCount = token.split(".").length - 1;
6795
+ return dotCount !== 2;
6800
6796
  }
6801
- function parseAdvanceErrorBody(body) {
6802
- if (body.length === 0) {
6803
- return null;
6797
+ function paddedBase64(base64Url) {
6798
+ const normalized = base64Url.replaceAll("-", "+").replaceAll("_", "/");
6799
+ const remainder = normalized.length % 4;
6800
+ if (remainder === 0) {
6801
+ return normalized;
6804
6802
  }
6805
- const parsed = errors3.trySync(function parseJson() {
6806
- return JSON.parse(body);
6803
+ if (remainder === 2) {
6804
+ return `${normalized}==`;
6805
+ }
6806
+ if (remainder === 3) {
6807
+ return `${normalized}=`;
6808
+ }
6809
+ throw errors3.wrap(ErrMalformedAccessToken, "payload base64");
6810
+ }
6811
+ function decodeBase64UrlJson(segment) {
6812
+ const decoded = errors3.trySync(function decodePayload() {
6813
+ const json = globalThis.atob(paddedBase64(segment));
6814
+ return JSON.parse(json);
6807
6815
  });
6808
- if (parsed.error) {
6816
+ if (decoded.error) {
6817
+ throw errors3.wrap(ErrMalformedAccessToken, "payload decode");
6818
+ }
6819
+ return decoded.data;
6820
+ }
6821
+ function numericClaim(payload, claim) {
6822
+ if (typeof payload !== "object" || payload === null) {
6809
6823
  return null;
6810
6824
  }
6811
- const raw = parsed.data;
6812
- if (typeof raw !== "object" || raw === null) {
6825
+ const value = Reflect.get(payload, claim);
6826
+ if (typeof value !== "number" || !Number.isFinite(value)) {
6813
6827
  return null;
6814
6828
  }
6815
- const result = {};
6816
- const error = readStringField(raw, "error");
6817
- if (error !== undefined) {
6818
- result.error = error;
6829
+ return value;
6830
+ }
6831
+ function validateAccessTokenTimestamp(token, logger) {
6832
+ const segments = token.split(".");
6833
+ const payloadSegment = segments[1];
6834
+ if (payloadSegment === undefined || payloadSegment.length === 0) {
6835
+ logger.error("access token payload missing");
6836
+ throw errors3.wrap(ErrMalformedAccessToken, "payload missing");
6819
6837
  }
6820
- const detail = readStringField(raw, "detail");
6821
- if (detail !== undefined) {
6822
- result.detail = detail;
6838
+ const payload = decodeBase64UrlJson(payloadSegment);
6839
+ const exp = numericClaim(payload, "exp");
6840
+ if (exp === null) {
6841
+ logger.error("access token exp missing");
6842
+ throw errors3.wrap(ErrMalformedAccessToken, "exp missing");
6823
6843
  }
6824
- const minimumSdkVersion = readStringField(raw, "minimumSdkVersion");
6825
- if (minimumSdkVersion !== undefined) {
6826
- result.minimumSdkVersion = minimumSdkVersion;
6844
+ const expiresAtMs = exp * 1000;
6845
+ if (expiresAtMs <= Date.now() + TOKEN_EXPIRY_SKEW_MS) {
6846
+ logger.error("access token expired");
6847
+ throw ErrTokenExpired;
6827
6848
  }
6828
- const receivedSdkVersion = readStringField(raw, "receivedSdkVersion");
6829
- if (receivedSdkVersion !== undefined) {
6830
- result.receivedSdkVersion = receivedSdkVersion;
6849
+ }
6850
+ function resolveAccessToken(token, logger) {
6851
+ if (isMalformedJws(token)) {
6852
+ logger.error({ prefix: ACCESS_TOKEN_PREFIX }, "malformed access token");
6853
+ throw errors3.wrap(ErrMalformedAccessToken, `token must start with '${ACCESS_TOKEN_PREFIX}' and contain two dots`);
6831
6854
  }
6832
- const upgradeUrl = readStringField(raw, "upgradeUrl");
6833
- if (upgradeUrl !== undefined) {
6834
- result.upgradeUrl = upgradeUrl;
6855
+ validateAccessTokenTimestamp(token, logger);
6856
+ return { value: token, [resolvedAccessTokenBrand]: true };
6857
+ }
6858
+
6859
+ // src/client/auth/browser.ts
6860
+ var POPUP_TARGET = "primer-auth";
6861
+ var POPUP_FEATURES = "popup,width=480,height=720";
6862
+ function currentUrl(logger) {
6863
+ if (typeof globalThis.location === "undefined") {
6864
+ logger.error("auth location unavailable");
6865
+ throw ErrAuthUnavailable;
6866
+ }
6867
+ return new URL(globalThis.location.href);
6868
+ }
6869
+ function redirectUri(url) {
6870
+ return `${url.origin}${url.pathname}${url.search}`;
6871
+ }
6872
+ function randomClientState(logger) {
6873
+ if (typeof globalThis.crypto === "undefined") {
6874
+ logger.error("auth crypto unavailable");
6875
+ throw ErrAuthUnavailable;
6876
+ }
6877
+ const bytes = new Uint8Array(24);
6878
+ globalThis.crypto.getRandomValues(bytes);
6879
+ let result = "";
6880
+ for (const byte of bytes) {
6881
+ result += byte.toString(16).padStart(2, "0");
6835
6882
  }
6836
6883
  return result;
6837
6884
  }
6838
- function httpSentinel(status, body) {
6839
- if (status === 400) {
6840
- const parsed = parseAdvanceErrorBody(body);
6841
- if (parsed?.error === "sdk_upgrade_required") {
6842
- return ErrSdkUpgradeRequired;
6843
- }
6844
- return ErrBadRequest;
6885
+ function openAuthPopup(url, logger) {
6886
+ if (typeof globalThis.open === "undefined") {
6887
+ logger.error("auth popup api unavailable");
6888
+ throw ErrAuthUnavailable;
6845
6889
  }
6846
- if (status === 401) {
6847
- const parsed = parseAdvanceErrorBody(body);
6848
- if (parsed?.detail === "token_expired") {
6849
- return ErrTokenExpired;
6850
- }
6851
- return ErrInvalidAccessToken;
6890
+ const popup = globalThis.open(url, POPUP_TARGET, POPUP_FEATURES);
6891
+ if (popup === null) {
6892
+ logger.error("auth popup blocked");
6893
+ throw ErrAuthPopupBlocked;
6852
6894
  }
6853
- if (status === 403) {
6854
- return ErrForbidden;
6895
+ return popup;
6896
+ }
6897
+
6898
+ // src/client/auth/hosted-popup.ts
6899
+ var DEFAULT_POPUP_TIMEOUT_MS = 10 * 60 * 1000;
6900
+ var POPUP_POLL_MS = 250;
6901
+ var AUTH_MESSAGE_TYPE = "primer-tives.auth.result.v1";
6902
+ function hostedAuthUrl(config) {
6903
+ const logger = config.logger;
6904
+ if (!URL.canParse(config.origin)) {
6905
+ logger.error({ origin: config.origin }, "hosted auth origin invalid");
6906
+ throw ErrAuthConfigInvalid;
6855
6907
  }
6856
- if (status === 404) {
6857
- return ErrNotFound;
6908
+ const authUrl = new URL("/api/auth/timeback/start", config.origin);
6909
+ authUrl.searchParams.set("publishableKey", config.publishableKey);
6910
+ authUrl.searchParams.set("redirectUri", redirectUri(config.currentUrl));
6911
+ authUrl.searchParams.set("state", config.clientState);
6912
+ return authUrl.toString();
6913
+ }
6914
+ function isRecord(value) {
6915
+ return typeof value === "object" && value !== null;
6916
+ }
6917
+ function stringField(value, key) {
6918
+ const field = value[key];
6919
+ if (typeof field !== "string" || field.length === 0) {
6920
+ return null;
6858
6921
  }
6859
- if (status === 409) {
6860
- return ErrConflict;
6922
+ return field;
6923
+ }
6924
+ function readPopupMessage(event, popup, config, expectedOrigin) {
6925
+ if (event.source !== popup) {
6926
+ return { kind: "ignore" };
6861
6927
  }
6862
- if (status === 429) {
6863
- return ErrRateLimited;
6928
+ if (event.origin !== expectedOrigin) {
6929
+ return { kind: "ignore" };
6864
6930
  }
6865
- if (status === 502 || status === 503 || status === 504) {
6866
- return ErrServiceUnavailable;
6931
+ const data = event.data;
6932
+ if (!isRecord(data)) {
6933
+ return { kind: "ignore" };
6867
6934
  }
6868
- return ErrServerError;
6869
- }
6870
- function buildSdkUpgradeRequiredError(sentinel, text, logger) {
6871
- const parsed = parseAdvanceErrorBody(text);
6872
- if (parsed === null) {
6873
- logger.error({
6874
- receivedSdkVersion: null,
6875
- minimumSdkVersion: "<unknown>",
6876
- upgradeUrl: NPM_PACKAGE_URL
6877
- }, "sdk upgrade required");
6878
- const message2 = `<missing> < <unknown>; bump @superbuilders/primer-tives at ${NPM_PACKAGE_URL}`;
6879
- return errors3.wrap(sentinel, message2);
6935
+ const messageType = stringField(data, "type");
6936
+ if (messageType !== AUTH_MESSAGE_TYPE) {
6937
+ return { kind: "ignore" };
6880
6938
  }
6881
- const minimumSdkVersion = parsed.minimumSdkVersion === undefined ? "<unknown>" : parsed.minimumSdkVersion;
6882
- const receivedSdkVersion = parsed.receivedSdkVersion === undefined ? null : parsed.receivedSdkVersion;
6883
- const receivedDisplay = receivedSdkVersion === null ? "<missing>" : receivedSdkVersion;
6884
- const upgradeUrl = parsed.upgradeUrl === undefined ? NPM_PACKAGE_URL : parsed.upgradeUrl;
6885
- logger.error({
6886
- receivedSdkVersion,
6887
- minimumSdkVersion,
6888
- upgradeUrl
6889
- }, "sdk upgrade required");
6890
- const message = `${receivedDisplay} < ${minimumSdkVersion}; bump @superbuilders/primer-tives at ${upgradeUrl}`;
6891
- return errors3.wrap(sentinel, message);
6892
- }
6893
- function isAbortError(err) {
6894
- if (err instanceof DOMException && err.name === "AbortError") {
6895
- return true;
6939
+ const state = stringField(data, "state");
6940
+ if (state === null) {
6941
+ return { kind: "error", error: ErrAuthCallbackInvalid };
6896
6942
  }
6897
- if (err instanceof DOMException && err.name === "TimeoutError") {
6898
- return true;
6943
+ if (state !== config.clientState) {
6944
+ return { kind: "error", error: ErrAuthStateMismatch };
6899
6945
  }
6900
- return false;
6901
- }
6902
- function createTransport(tc) {
6903
- const fetchFn = tc.fetch ? tc.fetch : globalThis.fetch;
6904
- const logger = tc.logger;
6905
- const advanceUrl = new URL(ADVANCE_PATH, tc.origin).toString();
6906
- function transportSignal() {
6907
- if (tc.abort) {
6908
- return tc.abort.signal;
6909
- }
6910
- return;
6946
+ const status = stringField(data, "status");
6947
+ if (status === "error") {
6948
+ return { kind: "error", error: ErrAuthCallbackInvalid };
6911
6949
  }
6912
- async function transport(body) {
6913
- logger.debug({
6914
- intentKind: body.intent.kind,
6915
- subject: body.subject
6916
- }, "transport request");
6917
- const fetchResult = await fetchFn(advanceUrl, {
6918
- method: "POST",
6919
- mode: "cors",
6920
- credentials: "omit",
6921
- headers: {
6922
- "Content-Type": "application/json",
6923
- Authorization: `Bearer ${tc.accessToken.value}`,
6924
- "X-Primer-Publishable-Key": tc.publishableKey,
6925
- "X-Primer-SDK-Version": SDK_VERSION
6926
- },
6927
- body: JSON.stringify(body),
6928
- signal: transportSignal()
6929
- }).then(function ok(r) {
6930
- return { ok: true, response: r };
6931
- }, function fail(err) {
6932
- return { ok: false, error: err };
6933
- });
6934
- if (!fetchResult.ok) {
6935
- if (isAbortError(fetchResult.error)) {
6936
- logger.error({
6937
- intentKind: body.intent.kind
6938
- }, "transport timeout");
6939
- return { ok: false, error: errors3.wrap(ErrTimeout, fetchResult.error.message) };
6950
+ if (status !== "success") {
6951
+ return { kind: "error", error: ErrAuthCallbackInvalid };
6952
+ }
6953
+ const accessToken = stringField(data, "accessToken");
6954
+ if (accessToken === null) {
6955
+ return { kind: "error", error: ErrAuthCallbackInvalid };
6956
+ }
6957
+ return { kind: "success", accessToken };
6958
+ }
6959
+ function supportsAsyncDisposableStack() {
6960
+ return typeof Reflect.get(globalThis, "AsyncDisposableStack") === "function";
6961
+ }
6962
+ async function waitForPopupMessageWithAsyncDisposableStack(popup, config, expectedOrigin) {
6963
+ let __stack = [];
6964
+ try {
6965
+ const logger = config.logger;
6966
+ const stack = __using(__stack, new AsyncDisposableStack, 1);
6967
+ stack.defer(function closePopup() {
6968
+ popup.close();
6969
+ });
6970
+ const result = await new Promise(function waitForMessage(resolve, reject) {
6971
+ let settled = false;
6972
+ function finishWithError(error) {
6973
+ if (settled) {
6974
+ return;
6975
+ }
6976
+ settled = true;
6977
+ reject(error);
6940
6978
  }
6941
- logger.error({
6942
- error: fetchResult.error
6943
- }, "transport network error");
6944
- return { ok: false, error: errors3.wrap(ErrNetwork, fetchResult.error.message) };
6945
- }
6946
- const res = fetchResult.response;
6947
- if (!res.ok) {
6948
- const text = await res.text().catch(function fallback() {
6949
- return "";
6979
+ function finishWithToken(token) {
6980
+ if (settled) {
6981
+ return;
6982
+ }
6983
+ settled = true;
6984
+ logger.debug("hosted auth popup completed");
6985
+ resolve(token);
6986
+ }
6987
+ const timeoutId = globalThis.setTimeout(function timeout() {
6988
+ logger.error("hosted auth popup timed out");
6989
+ finishWithError(ErrAuthCancelled);
6990
+ }, DEFAULT_POPUP_TIMEOUT_MS);
6991
+ stack.defer(function clearPopupTimeout() {
6992
+ globalThis.clearTimeout(timeoutId);
6950
6993
  });
6951
- const sentinel = httpSentinel(res.status, text);
6952
- if (errors3.is(sentinel, ErrSdkUpgradeRequired)) {
6953
- return { ok: false, error: buildSdkUpgradeRequiredError(sentinel, text, logger) };
6994
+ const closedPollId = globalThis.setInterval(function checkClosed() {
6995
+ if (!popup.closed) {
6996
+ return;
6997
+ }
6998
+ logger.error("hosted auth popup closed");
6999
+ finishWithError(ErrAuthCancelled);
7000
+ }, POPUP_POLL_MS);
7001
+ stack.defer(function clearClosedPoll() {
7002
+ globalThis.clearInterval(closedPollId);
7003
+ });
7004
+ function handleMessage(event) {
7005
+ const result2 = readPopupMessage(event, popup, config, expectedOrigin);
7006
+ if (result2.kind === "ignore") {
7007
+ return;
7008
+ }
7009
+ if (result2.kind === "error") {
7010
+ logger.error({ error: result2.error }, "hosted auth popup failed");
7011
+ finishWithError(result2.error);
7012
+ return;
7013
+ }
7014
+ finishWithToken(result2.accessToken);
6954
7015
  }
6955
- logger.error({
6956
- status: res.status,
6957
- body: text,
6958
- intentKind: body.intent.kind,
6959
- subject: body.subject
6960
- }, "transport http error");
6961
- return { ok: false, error: errors3.wrap(sentinel, text) };
7016
+ globalThis.addEventListener("message", handleMessage);
7017
+ stack.defer(function removeMessageListener() {
7018
+ globalThis.removeEventListener("message", handleMessage);
7019
+ });
7020
+ });
7021
+ return result;
7022
+ } catch (_catch) {
7023
+ var _err = _catch, _hasErr = 1;
7024
+ } finally {
7025
+ var _promise = __callDispose(__stack, _err, _hasErr);
7026
+ _promise && await _promise;
7027
+ }
7028
+ }
7029
+ function cleanupManualPopupWait(cleanups) {
7030
+ for (let index = cleanups.length - 1;index >= 0; index -= 1) {
7031
+ const cleanup = cleanups[index];
7032
+ if (cleanup === undefined) {
7033
+ continue;
6962
7034
  }
6963
- const jsonResult = await res.json().then(function ok(data) {
6964
- return { ok: true, data };
6965
- }, function fail(err) {
6966
- return { ok: false, error: err };
7035
+ cleanup();
7036
+ }
7037
+ }
7038
+ async function waitForPopupMessageWithManualCleanup(popup, config, expectedOrigin) {
7039
+ const logger = config.logger;
7040
+ const cleanups = [];
7041
+ cleanups.push(function closePopup() {
7042
+ popup.close();
7043
+ });
7044
+ const pending = new Promise(function waitForMessage(resolve, reject) {
7045
+ let settled = false;
7046
+ function finishWithError(error) {
7047
+ if (settled) {
7048
+ return;
7049
+ }
7050
+ settled = true;
7051
+ reject(error);
7052
+ }
7053
+ function finishWithToken(token) {
7054
+ if (settled) {
7055
+ return;
7056
+ }
7057
+ settled = true;
7058
+ logger.debug("hosted auth popup completed");
7059
+ resolve(token);
7060
+ }
7061
+ const timeoutId = globalThis.setTimeout(function timeout() {
7062
+ logger.error("hosted auth popup timed out");
7063
+ finishWithError(ErrAuthCancelled);
7064
+ }, DEFAULT_POPUP_TIMEOUT_MS);
7065
+ cleanups.push(function clearPopupTimeout() {
7066
+ globalThis.clearTimeout(timeoutId);
6967
7067
  });
6968
- if (!jsonResult.ok) {
6969
- logger.error({
6970
- error: jsonResult.error
6971
- }, "transport json parse failed");
6972
- return { ok: false, error: errors3.wrap(ErrJsonParse, jsonResult.error.message) };
7068
+ const closedPollId = globalThis.setInterval(function checkClosed() {
7069
+ if (!popup.closed) {
7070
+ return;
7071
+ }
7072
+ logger.error("hosted auth popup closed");
7073
+ finishWithError(ErrAuthCancelled);
7074
+ }, POPUP_POLL_MS);
7075
+ cleanups.push(function clearClosedPoll() {
7076
+ globalThis.clearInterval(closedPollId);
7077
+ });
7078
+ function handleMessage(event) {
7079
+ const result = readPopupMessage(event, popup, config, expectedOrigin);
7080
+ if (result.kind === "ignore") {
7081
+ return;
7082
+ }
7083
+ if (result.kind === "error") {
7084
+ logger.error({ error: result.error }, "hosted auth popup failed");
7085
+ finishWithError(result.error);
7086
+ return;
7087
+ }
7088
+ finishWithToken(result.accessToken);
6973
7089
  }
6974
- logger.debug({
6975
- intentKind: body.intent.kind
6976
- }, "transport success");
6977
- return { ok: true, data: jsonResult.data };
7090
+ globalThis.addEventListener("message", handleMessage);
7091
+ cleanups.push(function removeMessageListener() {
7092
+ globalThis.removeEventListener("message", handleMessage);
7093
+ });
7094
+ });
7095
+ return pending.then(function cleanupSuccess(token) {
7096
+ cleanupManualPopupWait(cleanups);
7097
+ return token;
7098
+ }, function cleanupFailure(error) {
7099
+ cleanupManualPopupWait(cleanups);
7100
+ throw error;
7101
+ });
7102
+ }
7103
+ async function waitForPopupMessage(popup, config, expectedOrigin) {
7104
+ if (supportsAsyncDisposableStack()) {
7105
+ return waitForPopupMessageWithAsyncDisposableStack(popup, config, expectedOrigin);
6978
7106
  }
6979
- return transport;
7107
+ return waitForPopupMessageWithManualCleanup(popup, config, expectedOrigin);
7108
+ }
7109
+ async function beginHostedPopup(config) {
7110
+ const logger = config.logger;
7111
+ const url = hostedAuthUrl(config);
7112
+ if (!URL.canParse(config.origin)) {
7113
+ logger.error({ origin: config.origin }, "hosted auth origin invalid");
7114
+ throw ErrAuthConfigInvalid;
7115
+ }
7116
+ const expectedOrigin = new URL(config.origin).origin;
7117
+ const popup = openAuthPopup(url, logger);
7118
+ return waitForPopupMessage(popup, config, expectedOrigin);
6980
7119
  }
6981
7120
 
6982
- // src/client/session.ts
6983
- import * as errors10 from "@superbuilders/errors";
7121
+ // src/client/auth/storage.ts
7122
+ var MANAGED_AUTH_ACCESS_TOKEN_KEY_PREFIX = "primer:access-token";
7123
+ function managedAuthAccessTokenStorageKey(publishableKey) {
7124
+ return `${MANAGED_AUTH_ACCESS_TOKEN_KEY_PREFIX}:${publishableKey}`;
7125
+ }
7126
+ function managedAuthStorage(logger) {
7127
+ if (typeof globalThis.sessionStorage === "undefined") {
7128
+ logger.error("managed auth storage unavailable");
7129
+ throw ErrAuthUnavailable;
7130
+ }
7131
+ return globalThis.sessionStorage;
7132
+ }
7133
+ function loadManagedAuthAccessToken(storage, publishableKey) {
7134
+ const token = storage.getItem(managedAuthAccessTokenStorageKey(publishableKey));
7135
+ if (token === null || token.length === 0) {
7136
+ return null;
7137
+ }
7138
+ return token;
7139
+ }
7140
+ function storeManagedAuthAccessToken(storage, publishableKey, accessToken) {
7141
+ storage.setItem(managedAuthAccessTokenStorageKey(publishableKey), accessToken);
7142
+ }
7143
+ function clearManagedAuthAccessToken(storage, publishableKey) {
7144
+ storage.removeItem(managedAuthAccessTokenStorageKey(publishableKey));
7145
+ }
7146
+
7147
+ // src/client/auth/provider.ts
7148
+ function resolveProvidedAccessToken(token, logger) {
7149
+ const result = errors4.trySync(function resolveProvidedToken() {
7150
+ return resolveAccessToken(token, logger);
7151
+ });
7152
+ if (result.error) {
7153
+ return { kind: "fatal", error: result.error };
7154
+ }
7155
+ return { kind: "resolved", accessToken: result.data };
7156
+ }
7157
+ function resolveHostedAccessToken(token, storage, options) {
7158
+ const result = errors4.trySync(function resolveHostedToken() {
7159
+ return resolveAccessToken(token, options.logger);
7160
+ });
7161
+ if (result.error) {
7162
+ return { kind: "sign-in-failed", error: result.error };
7163
+ }
7164
+ storeManagedAuthAccessToken(storage, options.publishableKey, token);
7165
+ return {
7166
+ kind: "resolved",
7167
+ accessToken: result.data,
7168
+ clearCachedAccessToken: function clearCachedAccessToken() {
7169
+ clearManagedAuthAccessToken(storage, options.publishableKey);
7170
+ }
7171
+ };
7172
+ }
7173
+ function resolveStoredAccessToken(storage, options) {
7174
+ const stored = loadManagedAuthAccessToken(storage, options.publishableKey);
7175
+ if (stored === null) {
7176
+ return { kind: "missing" };
7177
+ }
7178
+ const result = errors4.trySync(function resolveStoredToken() {
7179
+ return resolveAccessToken(stored, options.logger);
7180
+ });
7181
+ if (result.error) {
7182
+ clearManagedAuthAccessToken(storage, options.publishableKey);
7183
+ return { kind: "missing" };
7184
+ }
7185
+ return {
7186
+ kind: "resolved",
7187
+ accessToken: result.data,
7188
+ clearCachedAccessToken: function clearCachedAccessToken() {
7189
+ clearManagedAuthAccessToken(storage, options.publishableKey);
7190
+ }
7191
+ };
7192
+ }
7193
+ function resolveExistingAccessToken(options) {
7194
+ if (options.accessToken !== undefined) {
7195
+ return resolveProvidedAccessToken(options.accessToken, options.logger);
7196
+ }
7197
+ const storageResult = errors4.trySync(function readManagedAuthStorage() {
7198
+ return managedAuthStorage(options.logger);
7199
+ });
7200
+ if (storageResult.error) {
7201
+ return { kind: "auth-unavailable", error: storageResult.error };
7202
+ }
7203
+ return resolveStoredAccessToken(storageResult.data, options);
7204
+ }
7205
+ function authFailureResult(error) {
7206
+ if (errors4.is(error, ErrAuthConfigInvalid)) {
7207
+ return { kind: "auth-config-invalid", error };
7208
+ }
7209
+ if (errors4.is(error, ErrAuthUnavailable)) {
7210
+ return { kind: "auth-unavailable", error };
7211
+ }
7212
+ return { kind: "sign-in-failed", error };
7213
+ }
7214
+ async function beginHostedLogin(options) {
7215
+ const logger = options.logger;
7216
+ const contextResult = errors4.trySync(function readContext() {
7217
+ const storage = managedAuthStorage(logger);
7218
+ const url = currentUrl(logger);
7219
+ const clientState = randomClientState(logger);
7220
+ return { storage, url, clientState };
7221
+ });
7222
+ if (contextResult.error) {
7223
+ return authFailureResult(contextResult.error);
7224
+ }
7225
+ const context = contextResult.data;
7226
+ const accessTokenResult = await errors4.try(beginHostedPopup({
7227
+ origin: options.origin,
7228
+ publishableKey: options.publishableKey,
7229
+ currentUrl: context.url,
7230
+ clientState: context.clientState,
7231
+ logger
7232
+ }));
7233
+ if (accessTokenResult.error) {
7234
+ return authFailureResult(accessTokenResult.error);
7235
+ }
7236
+ return resolveHostedAccessToken(accessTokenResult.data, context.storage, options);
7237
+ }
6984
7238
 
6985
7239
  // src/client/consumed.ts
6986
7240
  function poisonToJSON() {
6987
7241
  throw ErrNotSerializable;
6988
7242
  }
6989
7243
 
7244
+ // src/client/auth-state.ts
7245
+ function pendingLogin(config) {
7246
+ let pending;
7247
+ function login() {
7248
+ if (pending !== undefined) {
7249
+ return pending;
7250
+ }
7251
+ pending = config.login().finally(function clearPending() {
7252
+ pending = undefined;
7253
+ });
7254
+ return pending;
7255
+ }
7256
+ return login;
7257
+ }
7258
+ function signInRequiredState(config) {
7259
+ return {
7260
+ phase: "sign-in-required",
7261
+ login: pendingLogin(config),
7262
+ toJSON: poisonToJSON
7263
+ };
7264
+ }
7265
+ function signInFailedState(config) {
7266
+ return {
7267
+ phase: "sign-in-failed",
7268
+ error: config.error,
7269
+ login: pendingLogin(config),
7270
+ toJSON: poisonToJSON
7271
+ };
7272
+ }
7273
+ function authUnavailableState(error) {
7274
+ return {
7275
+ phase: "auth-unavailable",
7276
+ error,
7277
+ toJSON: poisonToJSON
7278
+ };
7279
+ }
7280
+ function authConfigInvalidState(error) {
7281
+ return {
7282
+ phase: "auth-config-invalid",
7283
+ error,
7284
+ toJSON: poisonToJSON
7285
+ };
7286
+ }
7287
+
7288
+ // src/client/session.ts
7289
+ import * as errors11 from "@superbuilders/errors";
7290
+
6990
7291
  // src/client/choice-state.ts
6991
- import * as errors4 from "@superbuilders/errors";
6992
- function choiceState(ctx, body, stimulus, interaction, options, maxChoices, minChoices) {
7292
+ import * as errors5 from "@superbuilders/errors";
7293
+ function choiceState(ctx, body, stimulus, interaction, options, maxChoices, minChoices, feedback) {
6993
7294
  let submitPending;
6994
7295
  let submitKey;
6995
7296
  let timeoutPending;
@@ -6997,18 +7298,18 @@ function choiceState(ctx, body, stimulus, interaction, options, maxChoices, minC
6997
7298
  const submission = { type: "choice", selectedKeys };
6998
7299
  const key = JSON.stringify(submission);
6999
7300
  if (timeoutPending) {
7000
- return Promise.resolve(ctx.errored(errors4.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
7301
+ return Promise.resolve(ctx.errored(errors5.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
7001
7302
  }
7002
7303
  if (submitPending) {
7003
7304
  if (submitKey === key) {
7004
7305
  return submitPending;
7005
7306
  }
7006
- return Promise.resolve(ctx.errored(errors4.wrap(ErrConflict, "cannot submit a different choice payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
7307
+ return Promise.resolve(ctx.errored(errors5.wrap(ErrConflict, "cannot submit a different choice payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
7007
7308
  }
7008
7309
  const validation = validateSubmissionForInteraction(interaction, submission);
7009
7310
  if (!validation.ok) {
7010
7311
  ctx.logger.error({ selectedKeys, issues: validation.issues }, "choice submit invalid");
7011
- return Promise.resolve(ctx.errored(errors4.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
7312
+ return Promise.resolve(ctx.errored(errors5.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
7012
7313
  }
7013
7314
  submitKey = key;
7014
7315
  submitPending = ctx.execute({ kind: "interaction", submission }, "interaction").finally(function clearPending() {
@@ -7020,7 +7321,7 @@ function choiceState(ctx, body, stimulus, interaction, options, maxChoices, minC
7020
7321
  function beginTimeout() {
7021
7322
  const intent = { kind: "timeout" };
7022
7323
  if (submitPending) {
7023
- return Promise.resolve(ctx.errored(errors4.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
7324
+ return Promise.resolve(ctx.errored(errors5.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
7024
7325
  }
7025
7326
  if (timeoutPending) {
7026
7327
  return timeoutPending;
@@ -7036,6 +7337,7 @@ function choiceState(ctx, body, stimulus, interaction, options, maxChoices, minC
7036
7337
  body,
7037
7338
  stimulus,
7038
7339
  interaction,
7340
+ feedback,
7039
7341
  options,
7040
7342
  maxChoices,
7041
7343
  minChoices,
@@ -7046,25 +7348,25 @@ function choiceState(ctx, body, stimulus, interaction, options, maxChoices, minC
7046
7348
  }
7047
7349
 
7048
7350
  // src/client/extended-text-state.ts
7049
- import * as errors5 from "@superbuilders/errors";
7050
- function extendedTextState(ctx, body, stimulus, interaction) {
7351
+ import * as errors6 from "@superbuilders/errors";
7352
+ function extendedTextState(ctx, body, stimulus, interaction, feedback) {
7051
7353
  if (interaction.cardinality === "single") {
7052
7354
  let submitText = function(value) {
7053
7355
  const submission = { type: "extended-text", values: [value] };
7054
7356
  const key = JSON.stringify(submission);
7055
7357
  if (timeoutPending2) {
7056
- return Promise.resolve(ctx.errored(errors5.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
7358
+ return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
7057
7359
  }
7058
7360
  if (submitPending2) {
7059
7361
  if (submitKey2 === key) {
7060
7362
  return submitPending2;
7061
7363
  }
7062
- return Promise.resolve(ctx.errored(errors5.wrap(ErrConflict, "cannot submit a different extended-text payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
7364
+ return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot submit a different extended-text payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
7063
7365
  }
7064
7366
  const validation = validateSubmissionForInteraction(interaction, submission);
7065
7367
  if (!validation.ok) {
7066
7368
  ctx.logger.error({ value, issues: validation.issues }, "extended-text submit invalid");
7067
- return Promise.resolve(ctx.errored(errors5.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
7369
+ return Promise.resolve(ctx.errored(errors6.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
7068
7370
  }
7069
7371
  submitKey2 = key;
7070
7372
  submitPending2 = ctx.execute({ kind: "interaction", submission }, "interaction").finally(function clearPending() {
@@ -7075,7 +7377,7 @@ function extendedTextState(ctx, body, stimulus, interaction) {
7075
7377
  }, timeout2 = function() {
7076
7378
  const intent = { kind: "timeout" };
7077
7379
  if (submitPending2) {
7078
- return Promise.resolve(ctx.errored(errors5.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
7380
+ return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
7079
7381
  }
7080
7382
  if (timeoutPending2) {
7081
7383
  return timeoutPending2;
@@ -7095,6 +7397,7 @@ function extendedTextState(ctx, body, stimulus, interaction) {
7095
7397
  body,
7096
7398
  stimulus,
7097
7399
  interaction,
7400
+ feedback,
7098
7401
  submitText,
7099
7402
  timeout: timeout2,
7100
7403
  toJSON: poisonToJSON
@@ -7108,18 +7411,18 @@ function extendedTextState(ctx, body, stimulus, interaction) {
7108
7411
  const submission = { type: "extended-text", values };
7109
7412
  const key = JSON.stringify(submission);
7110
7413
  if (timeoutPending) {
7111
- return Promise.resolve(ctx.errored(errors5.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
7414
+ return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
7112
7415
  }
7113
7416
  if (submitPending) {
7114
7417
  if (submitKey === key) {
7115
7418
  return submitPending;
7116
7419
  }
7117
- return Promise.resolve(ctx.errored(errors5.wrap(ErrConflict, "cannot submit a different extended-text payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
7420
+ return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot submit a different extended-text payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
7118
7421
  }
7119
7422
  const validation = validateSubmissionForInteraction(multi, submission);
7120
7423
  if (!validation.ok) {
7121
7424
  ctx.logger.error({ values, issues: validation.issues }, "extended-text submit invalid");
7122
- return Promise.resolve(ctx.errored(errors5.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
7425
+ return Promise.resolve(ctx.errored(errors6.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
7123
7426
  }
7124
7427
  submitKey = key;
7125
7428
  submitPending = ctx.execute({ kind: "interaction", submission }, "interaction").finally(function clearPending() {
@@ -7131,7 +7434,7 @@ function extendedTextState(ctx, body, stimulus, interaction) {
7131
7434
  function timeout() {
7132
7435
  const intent = { kind: "timeout" };
7133
7436
  if (submitPending) {
7134
- return Promise.resolve(ctx.errored(errors5.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
7437
+ return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
7135
7438
  }
7136
7439
  if (timeoutPending) {
7137
7440
  return timeoutPending;
@@ -7148,6 +7451,7 @@ function extendedTextState(ctx, body, stimulus, interaction) {
7148
7451
  body,
7149
7452
  stimulus,
7150
7453
  interaction: multi,
7454
+ feedback,
7151
7455
  maxStrings: multi.maxStrings,
7152
7456
  minStrings: multi.minStrings,
7153
7457
  submitTexts,
@@ -7157,31 +7461,47 @@ function extendedTextState(ctx, body, stimulus, interaction) {
7157
7461
  }
7158
7462
 
7159
7463
  // src/client/feedback-state.ts
7160
- function feedbackState(ctx, body, stimulus, interaction, submission, isCorrect, feedbackContent, review) {
7464
+ function createAdvance(ctx) {
7161
7465
  let pending;
7466
+ return function advance() {
7467
+ if (pending) {
7468
+ return pending;
7469
+ }
7470
+ pending = ctx.execute({ kind: "observation" }, "observation");
7471
+ return pending;
7472
+ };
7473
+ }
7474
+ function submittedFeedbackState(ctx, body, stimulus, interaction, submission, assessmentOutcome, feedbackContent, review) {
7162
7475
  return {
7163
7476
  phase: "feedback",
7477
+ feedbackKind: "submitted",
7164
7478
  body,
7165
7479
  stimulus,
7166
7480
  interaction,
7167
7481
  submission,
7168
- isCorrect,
7482
+ assessmentOutcome,
7169
7483
  feedbackContent,
7170
7484
  review,
7171
- advance: function advance() {
7172
- if (pending) {
7173
- return pending;
7174
- }
7175
- pending = ctx.execute({ kind: "observation" }, "observation");
7176
- return pending;
7177
- },
7485
+ advance: createAdvance(ctx),
7486
+ toJSON: poisonToJSON
7487
+ };
7488
+ }
7489
+ function timedOutFeedbackState(ctx, body, stimulus, interaction, feedbackContent) {
7490
+ return {
7491
+ phase: "feedback",
7492
+ feedbackKind: "timedOut",
7493
+ body,
7494
+ stimulus,
7495
+ interaction,
7496
+ feedbackContent,
7497
+ advance: createAdvance(ctx),
7178
7498
  toJSON: poisonToJSON
7179
7499
  };
7180
7500
  }
7181
7501
 
7182
7502
  // src/client/match-state.ts
7183
- import * as errors6 from "@superbuilders/errors";
7184
- function matchState(ctx, body, stimulus, interaction) {
7503
+ import * as errors7 from "@superbuilders/errors";
7504
+ function matchState(ctx, body, stimulus, interaction, feedback) {
7185
7505
  let submitPending;
7186
7506
  let submitKey;
7187
7507
  let timeoutPending;
@@ -7189,18 +7509,18 @@ function matchState(ctx, body, stimulus, interaction) {
7189
7509
  const submission = { type: "match", pairs };
7190
7510
  const key = JSON.stringify(submission);
7191
7511
  if (timeoutPending) {
7192
- return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
7512
+ return Promise.resolve(ctx.errored(errors7.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
7193
7513
  }
7194
7514
  if (submitPending) {
7195
7515
  if (submitKey === key) {
7196
7516
  return submitPending;
7197
7517
  }
7198
- return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot submit a different match payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
7518
+ return Promise.resolve(ctx.errored(errors7.wrap(ErrConflict, "cannot submit a different match payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
7199
7519
  }
7200
7520
  const validation = validateSubmissionForInteraction(interaction, submission);
7201
7521
  if (!validation.ok) {
7202
7522
  ctx.logger.error({ pairs, issues: validation.issues }, "match submit invalid");
7203
- return Promise.resolve(ctx.errored(errors6.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
7523
+ return Promise.resolve(ctx.errored(errors7.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
7204
7524
  }
7205
7525
  submitKey = key;
7206
7526
  submitPending = ctx.execute({ kind: "interaction", submission }, "interaction").finally(function clearPending() {
@@ -7212,7 +7532,7 @@ function matchState(ctx, body, stimulus, interaction) {
7212
7532
  function timeout() {
7213
7533
  const intent = { kind: "timeout" };
7214
7534
  if (submitPending) {
7215
- return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
7535
+ return Promise.resolve(ctx.errored(errors7.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
7216
7536
  }
7217
7537
  if (timeoutPending) {
7218
7538
  return timeoutPending;
@@ -7228,6 +7548,7 @@ function matchState(ctx, body, stimulus, interaction) {
7228
7548
  body,
7229
7549
  stimulus,
7230
7550
  interaction,
7551
+ feedback,
7231
7552
  sourceChoices: interaction.sourceChoices,
7232
7553
  targetChoices: interaction.targetChoices,
7233
7554
  minAssociations: interaction.minAssociations,
@@ -7257,8 +7578,8 @@ function observationState(ctx, body, stimulus) {
7257
7578
  }
7258
7579
 
7259
7580
  // src/client/order-state.ts
7260
- import * as errors7 from "@superbuilders/errors";
7261
- function orderState(ctx, body, stimulus, interaction) {
7581
+ import * as errors8 from "@superbuilders/errors";
7582
+ function orderState(ctx, body, stimulus, interaction, feedback) {
7262
7583
  let submitPending;
7263
7584
  let submitKey;
7264
7585
  let timeoutPending;
@@ -7266,18 +7587,18 @@ function orderState(ctx, body, stimulus, interaction) {
7266
7587
  const submission = { type: "order", orderedKeys };
7267
7588
  const key = JSON.stringify(submission);
7268
7589
  if (timeoutPending) {
7269
- return Promise.resolve(ctx.errored(errors7.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
7590
+ return Promise.resolve(ctx.errored(errors8.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
7270
7591
  }
7271
7592
  if (submitPending) {
7272
7593
  if (submitKey === key) {
7273
7594
  return submitPending;
7274
7595
  }
7275
- return Promise.resolve(ctx.errored(errors7.wrap(ErrConflict, "cannot submit a different order payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
7596
+ return Promise.resolve(ctx.errored(errors8.wrap(ErrConflict, "cannot submit a different order payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
7276
7597
  }
7277
7598
  const validation = validateSubmissionForInteraction(interaction, submission);
7278
7599
  if (!validation.ok) {
7279
7600
  ctx.logger.error({ orderedKeys, issues: validation.issues }, "order submit invalid");
7280
- return Promise.resolve(ctx.errored(errors7.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
7601
+ return Promise.resolve(ctx.errored(errors8.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
7281
7602
  }
7282
7603
  submitKey = key;
7283
7604
  submitPending = ctx.execute({ kind: "interaction", submission }, "interaction").finally(function clearPending() {
@@ -7289,7 +7610,7 @@ function orderState(ctx, body, stimulus, interaction) {
7289
7610
  function timeout() {
7290
7611
  const intent = { kind: "timeout" };
7291
7612
  if (submitPending) {
7292
- return Promise.resolve(ctx.errored(errors7.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
7613
+ return Promise.resolve(ctx.errored(errors8.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
7293
7614
  }
7294
7615
  if (timeoutPending) {
7295
7616
  return timeoutPending;
@@ -7305,6 +7626,7 @@ function orderState(ctx, body, stimulus, interaction) {
7305
7626
  body,
7306
7627
  stimulus,
7307
7628
  interaction,
7629
+ feedback,
7308
7630
  choices: interaction.choices,
7309
7631
  minChoices: interaction.minChoices,
7310
7632
  maxChoices: interaction.maxChoices,
@@ -7315,8 +7637,8 @@ function orderState(ctx, body, stimulus, interaction) {
7315
7637
  }
7316
7638
 
7317
7639
  // src/client/pci-state.ts
7318
- import * as errors8 from "@superbuilders/errors";
7319
- function pciInteractionState(ctx, body, stimulus, interaction) {
7640
+ import * as errors9 from "@superbuilders/errors";
7641
+ function pciInteractionState(ctx, body, stimulus, interaction, feedback) {
7320
7642
  let submitPending;
7321
7643
  let submitKey;
7322
7644
  let timeoutPending;
@@ -7325,13 +7647,13 @@ function pciInteractionState(ctx, body, stimulus, interaction) {
7325
7647
  const submission = { type: "portable-custom", pciId, value };
7326
7648
  const key = JSON.stringify(submission);
7327
7649
  if (timeoutPending) {
7328
- return Promise.resolve(ctx.errored(errors8.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
7650
+ return Promise.resolve(ctx.errored(errors9.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
7329
7651
  }
7330
7652
  if (submitPending) {
7331
7653
  if (submitKey === key) {
7332
7654
  return submitPending;
7333
7655
  }
7334
- return Promise.resolve(ctx.errored(errors8.wrap(ErrConflict, "cannot submit a different pci payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
7656
+ return Promise.resolve(ctx.errored(errors9.wrap(ErrConflict, "cannot submit a different pci payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
7335
7657
  }
7336
7658
  ctx.logger.debug({ pciId }, "pci submit");
7337
7659
  submitKey = key;
@@ -7344,7 +7666,7 @@ function pciInteractionState(ctx, body, stimulus, interaction) {
7344
7666
  function timeout() {
7345
7667
  const intent = { kind: "timeout" };
7346
7668
  if (submitPending) {
7347
- return Promise.resolve(ctx.errored(errors8.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
7669
+ return Promise.resolve(ctx.errored(errors9.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
7348
7670
  }
7349
7671
  if (timeoutPending) {
7350
7672
  return timeoutPending;
@@ -7361,6 +7683,7 @@ function pciInteractionState(ctx, body, stimulus, interaction) {
7361
7683
  body,
7362
7684
  stimulus,
7363
7685
  interaction,
7686
+ feedback,
7364
7687
  pciId,
7365
7688
  properties,
7366
7689
  submit,
@@ -7370,8 +7693,8 @@ function pciInteractionState(ctx, body, stimulus, interaction) {
7370
7693
  }
7371
7694
 
7372
7695
  // src/client/text-entry-state.ts
7373
- import * as errors9 from "@superbuilders/errors";
7374
- function textEntryState(ctx, body, stimulus, interaction) {
7696
+ import * as errors10 from "@superbuilders/errors";
7697
+ function textEntryState(ctx, body, stimulus, interaction, feedback) {
7375
7698
  let submitPending;
7376
7699
  let submitKey;
7377
7700
  let timeoutPending;
@@ -7379,18 +7702,18 @@ function textEntryState(ctx, body, stimulus, interaction) {
7379
7702
  const submission = { type: "text-entry", value };
7380
7703
  const key = JSON.stringify(submission);
7381
7704
  if (timeoutPending) {
7382
- return Promise.resolve(ctx.errored(errors9.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
7705
+ return Promise.resolve(ctx.errored(errors10.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
7383
7706
  }
7384
7707
  if (submitPending) {
7385
7708
  if (submitKey === key) {
7386
7709
  return submitPending;
7387
7710
  }
7388
- return Promise.resolve(ctx.errored(errors9.wrap(ErrConflict, "cannot submit a different text payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
7711
+ return Promise.resolve(ctx.errored(errors10.wrap(ErrConflict, "cannot submit a different text payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
7389
7712
  }
7390
7713
  const validation = validateSubmissionForInteraction(interaction, submission);
7391
7714
  if (!validation.ok) {
7392
7715
  ctx.logger.error({ value, issues: validation.issues }, "text-entry submit invalid");
7393
- return Promise.resolve(ctx.errored(errors9.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
7716
+ return Promise.resolve(ctx.errored(errors10.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
7394
7717
  }
7395
7718
  submitKey = key;
7396
7719
  submitPending = ctx.execute({ kind: "interaction", submission }, "interaction").finally(function clearPending() {
@@ -7402,7 +7725,7 @@ function textEntryState(ctx, body, stimulus, interaction) {
7402
7725
  function timeout() {
7403
7726
  const intent = { kind: "timeout" };
7404
7727
  if (submitPending) {
7405
- return Promise.resolve(ctx.errored(errors9.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
7728
+ return Promise.resolve(ctx.errored(errors10.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
7406
7729
  }
7407
7730
  if (timeoutPending) {
7408
7731
  return timeoutPending;
@@ -7418,6 +7741,7 @@ function textEntryState(ctx, body, stimulus, interaction) {
7418
7741
  body,
7419
7742
  stimulus,
7420
7743
  interaction,
7744
+ feedback,
7421
7745
  submitText,
7422
7746
  timeout,
7423
7747
  toJSON: poisonToJSON
@@ -7436,571 +7760,386 @@ var FATAL_SENTINELS = [
7436
7760
  ];
7437
7761
  function isFatalError(err) {
7438
7762
  for (const sentinel of FATAL_SENTINELS) {
7439
- if (errors10.is(err, sentinel)) {
7763
+ if (errors11.is(err, sentinel)) {
7440
7764
  return true;
7441
7765
  }
7442
7766
  }
7443
7767
  return false;
7444
7768
  }
7445
7769
  function isRetriableError(err) {
7446
- return !errors10.is(err, ErrInvalidSubmission);
7770
+ return !errors11.is(err, ErrInvalidSubmission);
7447
7771
  }
7448
7772
  function makeSession(sc) {
7449
7773
  const logger = sc.logger;
7450
7774
  function resolve(result) {
7451
7775
  switch (result.outcome) {
7452
7776
  case "advanced":
7453
- return fromAdvanced(result.frame.body, result.frame.stimulus, result.frame.interaction);
7777
+ return fromAdvanced(result.frame.body, result.frame.stimulus, result.frame.interaction, result.frame.feedback);
7454
7778
  case "submitted": {
7455
7779
  const interaction = result.frame.interaction;
7456
7780
  if (interaction === null) {
7457
7781
  logger.error("submitted result without interaction");
7458
- return {
7459
- phase: "fatal",
7460
- error: errors10.wrap(ErrBadRequest, "submitted result missing interaction"),
7461
- retriable: false,
7462
- toJSON: poisonToJSON
7463
- };
7464
- }
7465
- return feedbackState(ctx, result.frame.body, result.frame.stimulus, interaction, result.submission, result.isCorrect, result.feedbackContent, result.review);
7466
- }
7467
- case "completed":
7468
- return { phase: "completed", toJSON: poisonToJSON };
7469
- }
7470
- }
7471
- function errored(error, failedPhase, intent) {
7472
- let pending;
7473
- const retriable = isRetriableError(error);
7474
- if (!retriable) {
7475
- return {
7476
- phase: "errored",
7477
- error,
7478
- retriable: false,
7479
- toJSON: poisonToJSON
7480
- };
7481
- }
7482
- const state = {
7483
- phase: "errored",
7484
- error,
7485
- retriable: true,
7486
- retry: function retry() {
7487
- if (pending) {
7488
- return pending;
7489
- }
7490
- logger.debug({ failedPhase }, "retrying from errored state");
7491
- pending = execute(intent, failedPhase);
7492
- return pending;
7493
- },
7494
- toJSON: poisonToJSON
7495
- };
7496
- return state;
7497
- }
7498
- async function execute(intent, phase) {
7499
- logger.debug({
7500
- phase,
7501
- intentKind: intent.kind,
7502
- subject: sc.subject
7503
- }, "session execute");
7504
- const body = {
7505
- subject: sc.subject,
7506
- intent
7507
- };
7508
- const result = await sc.transport(body);
7509
- if (!result.ok) {
7510
- if ((errors10.is(result.error, ErrTokenExpired) || errors10.is(result.error, ErrInvalidAccessToken)) && sc.reauthenticate !== null) {
7511
- logger.warn({ phase, intentKind: intent.kind }, "session token rejected");
7512
- return sc.reauthenticate(result.error);
7513
- }
7514
- if (isFatalError(result.error)) {
7515
- logger.error({
7516
- error: result.error,
7517
- phase,
7518
- intentKind: intent.kind
7519
- }, "fatal transport error");
7520
- return {
7521
- phase: "fatal",
7522
- error: result.error,
7523
- retriable: false,
7524
- toJSON: poisonToJSON
7525
- };
7526
- }
7527
- logger.warn({
7528
- error: result.error,
7529
- phase,
7530
- intentKind: intent.kind
7531
- }, "retriable transport error");
7532
- return errored(result.error, phase, intent);
7533
- }
7534
- const next = resolve(result.data);
7535
- logger.debug({
7536
- phase,
7537
- intentKind: intent.kind,
7538
- outcome: result.data.outcome,
7539
- nextPhase: next.phase
7540
- }, "session execute resolved");
7541
- return next;
7542
- }
7543
- function isPciSupported(pciId) {
7544
- for (const id of sc.supportedPcis) {
7545
- if (id === pciId) {
7546
- return true;
7547
- }
7548
- }
7549
- return false;
7550
- }
7551
- function fromAdvanced(body, stimulus, interaction) {
7552
- if (interaction === null) {
7553
- return observationState(ctx, body, stimulus);
7554
- }
7555
- if (interaction.type === "portable-custom") {
7556
- if (!isPciSupported(interaction.pciId)) {
7557
- logger.error({ pciId: interaction.pciId }, "unsupported pci in frame");
7558
- return {
7559
- phase: "fatal",
7560
- error: errors10.wrap(ErrUnsupportedPci, `pci '${interaction.pciId}'`),
7561
- retriable: false,
7562
- toJSON: poisonToJSON
7563
- };
7564
- }
7565
- }
7566
- return pendingInteractionState(body, stimulus, interaction);
7567
- }
7568
- function pendingInteractionState(body, stimulus, interaction) {
7569
- switch (interaction.type) {
7570
- case "choice":
7571
- return choiceState(ctx, body, stimulus, interaction, interaction.options, interaction.maxChoices, interaction.minChoices);
7572
- case "text-entry":
7573
- return textEntryState(ctx, body, stimulus, interaction);
7574
- case "extended-text":
7575
- return extendedTextState(ctx, body, stimulus, interaction);
7576
- case "order":
7577
- return orderState(ctx, body, stimulus, interaction);
7578
- case "match":
7579
- return matchState(ctx, body, stimulus, interaction);
7580
- case "portable-custom":
7581
- return pciInteractionState(ctx, body, stimulus, interaction);
7582
- }
7583
- }
7584
- const ctx = { logger, execute, errored };
7585
- return { execute };
7586
- }
7587
-
7588
- // src/client/auth/provider.ts
7589
- import * as errors12 from "@superbuilders/errors";
7590
-
7591
- // src/client/auth/browser.ts
7592
- var POPUP_TARGET = "primer-auth";
7593
- var POPUP_FEATURES = "popup,width=480,height=720";
7594
- function currentUrl(logger) {
7595
- if (typeof globalThis.location === "undefined") {
7596
- logger.error("auth location unavailable");
7597
- throw ErrAuthUnavailable;
7598
- }
7599
- return new URL(globalThis.location.href);
7600
- }
7601
- function redirectUri(url) {
7602
- return `${url.origin}${url.pathname}${url.search}`;
7603
- }
7604
- function randomClientState(logger) {
7605
- if (typeof globalThis.crypto === "undefined") {
7606
- logger.error("auth crypto unavailable");
7607
- throw ErrAuthUnavailable;
7608
- }
7609
- const bytes = new Uint8Array(24);
7610
- globalThis.crypto.getRandomValues(bytes);
7611
- let result = "";
7612
- for (const byte of bytes) {
7613
- result += byte.toString(16).padStart(2, "0");
7614
- }
7615
- return result;
7616
- }
7617
- function openAuthPopup(url, logger) {
7618
- if (typeof globalThis.open === "undefined") {
7619
- logger.error("auth popup api unavailable");
7620
- throw ErrAuthUnavailable;
7621
- }
7622
- const popup = globalThis.open(url, POPUP_TARGET, POPUP_FEATURES);
7623
- if (popup === null) {
7624
- logger.error("auth popup blocked");
7625
- throw ErrAuthPopupBlocked;
7626
- }
7627
- return popup;
7628
- }
7629
-
7630
- // src/client/auth/hosted-popup.ts
7631
- var DEFAULT_POPUP_TIMEOUT_MS = 10 * 60 * 1000;
7632
- var POPUP_POLL_MS = 250;
7633
- var AUTH_MESSAGE_TYPE = "primer-tives.auth.result.v1";
7634
- function hostedAuthUrl(config) {
7635
- const logger = config.logger;
7636
- if (!URL.canParse(config.origin)) {
7637
- logger.error({ origin: config.origin }, "hosted auth origin invalid");
7638
- throw ErrAuthConfigInvalid;
7639
- }
7640
- const authUrl = new URL("/api/auth/timeback/start", config.origin);
7641
- authUrl.searchParams.set("publishableKey", config.publishableKey);
7642
- authUrl.searchParams.set("redirectUri", redirectUri(config.currentUrl));
7643
- authUrl.searchParams.set("state", config.clientState);
7644
- return authUrl.toString();
7645
- }
7646
- function isRecord(value) {
7647
- return typeof value === "object" && value !== null;
7648
- }
7649
- function stringField(value, key) {
7650
- const field = value[key];
7651
- if (typeof field !== "string" || field.length === 0) {
7652
- return null;
7653
- }
7654
- return field;
7655
- }
7656
- function readPopupMessage(event, popup, config, expectedOrigin) {
7657
- if (event.source !== popup) {
7658
- return { kind: "ignore" };
7659
- }
7660
- if (event.origin !== expectedOrigin) {
7661
- return { kind: "ignore" };
7662
- }
7663
- const data = event.data;
7664
- if (!isRecord(data)) {
7665
- return { kind: "ignore" };
7666
- }
7667
- const messageType = stringField(data, "type");
7668
- if (messageType !== AUTH_MESSAGE_TYPE) {
7669
- return { kind: "ignore" };
7670
- }
7671
- const state = stringField(data, "state");
7672
- if (state === null) {
7673
- return { kind: "error", error: ErrAuthCallbackInvalid };
7674
- }
7675
- if (state !== config.clientState) {
7676
- return { kind: "error", error: ErrAuthStateMismatch };
7677
- }
7678
- const status = stringField(data, "status");
7679
- if (status === "error") {
7680
- return { kind: "error", error: ErrAuthCallbackInvalid };
7681
- }
7682
- if (status !== "success") {
7683
- return { kind: "error", error: ErrAuthCallbackInvalid };
7684
- }
7685
- const accessToken = stringField(data, "accessToken");
7686
- if (accessToken === null) {
7687
- return { kind: "error", error: ErrAuthCallbackInvalid };
7688
- }
7689
- return { kind: "success", accessToken };
7690
- }
7691
- async function waitForPopupMessage(popup, config, expectedOrigin) {
7692
- let __stack = [];
7693
- try {
7694
- const logger = config.logger;
7695
- const stack = __using(__stack, new AsyncDisposableStack, 1);
7696
- stack.defer(function closePopup() {
7697
- popup.close();
7698
- });
7699
- const result = await new Promise(function waitForMessage(resolve, reject) {
7700
- let settled = false;
7701
- function finishWithError(error) {
7702
- if (settled) {
7703
- return;
7704
- }
7705
- settled = true;
7706
- reject(error);
7707
- }
7708
- function finishWithToken(token) {
7709
- if (settled) {
7710
- return;
7711
- }
7712
- settled = true;
7713
- logger.debug("hosted auth popup completed");
7714
- resolve(token);
7715
- }
7716
- const timeoutId = globalThis.setTimeout(function timeout() {
7717
- logger.error("hosted auth popup timed out");
7718
- finishWithError(ErrAuthCancelled);
7719
- }, DEFAULT_POPUP_TIMEOUT_MS);
7720
- stack.defer(function clearPopupTimeout() {
7721
- globalThis.clearTimeout(timeoutId);
7722
- });
7723
- const closedPollId = globalThis.setInterval(function checkClosed() {
7724
- if (!popup.closed) {
7725
- return;
7782
+ return {
7783
+ phase: "fatal",
7784
+ error: errors11.wrap(ErrBadRequest, "submitted result missing interaction"),
7785
+ retriable: false,
7786
+ toJSON: poisonToJSON
7787
+ };
7726
7788
  }
7727
- logger.error("hosted auth popup closed");
7728
- finishWithError(ErrAuthCancelled);
7729
- }, POPUP_POLL_MS);
7730
- stack.defer(function clearClosedPoll() {
7731
- globalThis.clearInterval(closedPollId);
7732
- });
7733
- function handleMessage(event) {
7734
- const result2 = readPopupMessage(event, popup, config, expectedOrigin);
7735
- if (result2.kind === "ignore") {
7736
- return;
7789
+ if (result.assessmentOutcome.value === "revisionRequested") {
7790
+ const feedback = {
7791
+ assessmentOutcome: result.assessmentOutcome,
7792
+ feedbackContent: result.feedbackContent
7793
+ };
7794
+ return fromAdvanced(result.frame.body, result.frame.stimulus, interaction, feedback);
7737
7795
  }
7738
- if (result2.kind === "error") {
7739
- logger.error({ error: result2.error }, "hosted auth popup failed");
7740
- finishWithError(result2.error);
7741
- return;
7796
+ return submittedFeedbackState(ctx, result.frame.body, result.frame.stimulus, interaction, result.submission, result.assessmentOutcome, result.feedbackContent, result.review);
7797
+ }
7798
+ case "timedOut": {
7799
+ const interaction = result.frame.interaction;
7800
+ if (interaction === null) {
7801
+ logger.error("timed out result without interaction");
7802
+ return {
7803
+ phase: "fatal",
7804
+ error: errors11.wrap(ErrBadRequest, "timed out result missing interaction"),
7805
+ retriable: false,
7806
+ toJSON: poisonToJSON
7807
+ };
7742
7808
  }
7743
- finishWithToken(result2.accessToken);
7809
+ return timedOutFeedbackState(ctx, result.frame.body, result.frame.stimulus, interaction, result.feedbackContent);
7744
7810
  }
7745
- globalThis.addEventListener("message", handleMessage);
7746
- stack.defer(function removeMessageListener() {
7747
- globalThis.removeEventListener("message", handleMessage);
7748
- });
7749
- });
7750
- return result;
7751
- } catch (_catch) {
7752
- var _err = _catch, _hasErr = 1;
7753
- } finally {
7754
- var _promise = __callDispose(__stack, _err, _hasErr);
7755
- _promise && await _promise;
7811
+ case "completed":
7812
+ return { phase: "completed", toJSON: poisonToJSON };
7813
+ }
7756
7814
  }
7757
- }
7758
- async function beginHostedPopup(config) {
7759
- const logger = config.logger;
7760
- const url = hostedAuthUrl(config);
7761
- if (!URL.canParse(config.origin)) {
7762
- logger.error({ origin: config.origin }, "hosted auth origin invalid");
7763
- throw ErrAuthConfigInvalid;
7815
+ function errored(error, failedPhase, intent) {
7816
+ let pending;
7817
+ const retriable = isRetriableError(error);
7818
+ if (!retriable) {
7819
+ return {
7820
+ phase: "errored",
7821
+ error,
7822
+ retriable: false,
7823
+ toJSON: poisonToJSON
7824
+ };
7825
+ }
7826
+ const state = {
7827
+ phase: "errored",
7828
+ error,
7829
+ retriable: true,
7830
+ retry: function retry() {
7831
+ if (pending) {
7832
+ return pending;
7833
+ }
7834
+ logger.debug({ failedPhase }, "retrying from errored state");
7835
+ pending = execute(intent, failedPhase);
7836
+ return pending;
7837
+ },
7838
+ toJSON: poisonToJSON
7839
+ };
7840
+ return state;
7764
7841
  }
7765
- const expectedOrigin = new URL(config.origin).origin;
7766
- const popup = openAuthPopup(url, logger);
7767
- return waitForPopupMessage(popup, config, expectedOrigin);
7768
- }
7769
-
7770
- // src/client/auth/access-token.ts
7771
- import * as errors11 from "@superbuilders/errors";
7772
- var ACCESS_TOKEN_PREFIX = "eyJ";
7773
- var TOKEN_EXPIRY_SKEW_MS = 60000;
7774
- var resolvedAccessTokenBrand = Symbol("primer resolved access token");
7775
- function isMalformedJws(token) {
7776
- if (!token.startsWith(ACCESS_TOKEN_PREFIX)) {
7777
- return true;
7842
+ async function execute(intent, phase) {
7843
+ logger.debug({
7844
+ phase,
7845
+ intentKind: intent.kind,
7846
+ subject: sc.subject
7847
+ }, "session execute");
7848
+ const body = {
7849
+ subject: sc.subject,
7850
+ intent
7851
+ };
7852
+ const result = await sc.transport(body);
7853
+ if (!result.ok) {
7854
+ if ((errors11.is(result.error, ErrTokenExpired) || errors11.is(result.error, ErrInvalidAccessToken)) && sc.reauthenticate !== null) {
7855
+ logger.warn({ phase, intentKind: intent.kind }, "session token rejected");
7856
+ return sc.reauthenticate(result.error);
7857
+ }
7858
+ if (isFatalError(result.error)) {
7859
+ logger.error({
7860
+ error: result.error,
7861
+ phase,
7862
+ intentKind: intent.kind
7863
+ }, "fatal transport error");
7864
+ return {
7865
+ phase: "fatal",
7866
+ error: result.error,
7867
+ retriable: false,
7868
+ toJSON: poisonToJSON
7869
+ };
7870
+ }
7871
+ logger.warn({
7872
+ error: result.error,
7873
+ phase,
7874
+ intentKind: intent.kind
7875
+ }, "retriable transport error");
7876
+ return errored(result.error, phase, intent);
7877
+ }
7878
+ const next = resolve(result.data);
7879
+ logger.debug({
7880
+ phase,
7881
+ intentKind: intent.kind,
7882
+ outcome: result.data.outcome,
7883
+ nextPhase: next.phase
7884
+ }, "session execute resolved");
7885
+ return next;
7778
7886
  }
7779
- const dotCount = token.split(".").length - 1;
7780
- return dotCount !== 2;
7781
- }
7782
- function paddedBase64(base64Url) {
7783
- const normalized = base64Url.replaceAll("-", "+").replaceAll("_", "/");
7784
- const remainder = normalized.length % 4;
7785
- if (remainder === 0) {
7786
- return normalized;
7887
+ function isPciSupported(pciId) {
7888
+ for (const id of sc.supportedPcis) {
7889
+ if (id === pciId) {
7890
+ return true;
7891
+ }
7892
+ }
7893
+ return false;
7787
7894
  }
7788
- if (remainder === 2) {
7789
- return `${normalized}==`;
7895
+ function fromAdvanced(body, stimulus, interaction, feedback) {
7896
+ if (interaction === null) {
7897
+ return observationState(ctx, body, stimulus);
7898
+ }
7899
+ if (interaction.type === "portable-custom") {
7900
+ if (!isPciSupported(interaction.pciId)) {
7901
+ logger.error({ pciId: interaction.pciId }, "unsupported pci in frame");
7902
+ return {
7903
+ phase: "fatal",
7904
+ error: errors11.wrap(ErrUnsupportedPci, `pci '${interaction.pciId}'`),
7905
+ retriable: false,
7906
+ toJSON: poisonToJSON
7907
+ };
7908
+ }
7909
+ }
7910
+ return pendingInteractionState(body, stimulus, interaction, feedback);
7790
7911
  }
7791
- if (remainder === 3) {
7792
- return `${normalized}=`;
7912
+ function extendedTextInteractionState(body, stimulus, interaction, feedback) {
7913
+ if (!("cardinality" in interaction)) {
7914
+ logger.error("extended-text interaction is unsupported");
7915
+ return {
7916
+ phase: "fatal",
7917
+ error: errors11.wrap(ErrBadRequest, "extended-text interaction unsupported"),
7918
+ retriable: false,
7919
+ toJSON: poisonToJSON
7920
+ };
7921
+ }
7922
+ const extendedInteraction = interaction;
7923
+ return extendedTextState(ctx, body, stimulus, extendedInteraction, feedback);
7924
+ }
7925
+ function pendingInteractionState(body, stimulus, interaction, feedback) {
7926
+ switch (interaction.type) {
7927
+ case "choice":
7928
+ return choiceState(ctx, body, stimulus, interaction, interaction.options, interaction.maxChoices, interaction.minChoices, feedback);
7929
+ case "text-entry":
7930
+ return textEntryState(ctx, body, stimulus, interaction, feedback);
7931
+ case "extended-text":
7932
+ return extendedTextInteractionState(body, stimulus, interaction, feedback);
7933
+ case "order":
7934
+ return orderState(ctx, body, stimulus, interaction, feedback);
7935
+ case "match":
7936
+ return matchState(ctx, body, stimulus, interaction, feedback);
7937
+ case "portable-custom":
7938
+ return pciInteractionState(ctx, body, stimulus, interaction, feedback);
7939
+ }
7793
7940
  }
7794
- throw errors11.wrap(ErrMalformedAccessToken, "payload base64");
7941
+ const ctx = { logger, execute, errored };
7942
+ return { execute };
7795
7943
  }
7796
- function decodeBase64UrlJson(segment) {
7797
- const decoded = errors11.trySync(function decodePayload() {
7798
- const json = globalThis.atob(paddedBase64(segment));
7799
- return JSON.parse(json);
7800
- });
7801
- if (decoded.error) {
7802
- throw errors11.wrap(ErrMalformedAccessToken, "payload decode");
7944
+
7945
+ // src/client/transport.ts
7946
+ import * as errors12 from "@superbuilders/errors";
7947
+
7948
+ // src/version.ts
7949
+ var SDK_VERSION = "5.0.0";
7950
+ var NPM_PACKAGE_URL = "https://www.npmjs.com/package/@superbuilders/primer-tives";
7951
+
7952
+ // src/client/transport.ts
7953
+ var ADVANCE_PATH = "/api/v0/advance";
7954
+ function readStringField(value, key) {
7955
+ if (!(key in value)) {
7956
+ return;
7803
7957
  }
7804
- return decoded.data;
7958
+ const v = Reflect.get(value, key);
7959
+ if (typeof v !== "string") {
7960
+ return;
7961
+ }
7962
+ return v;
7805
7963
  }
7806
- function numericClaim(payload, claim) {
7807
- if (typeof payload !== "object" || payload === null) {
7964
+ function parseAdvanceErrorBody(body) {
7965
+ if (body.length === 0) {
7808
7966
  return null;
7809
7967
  }
7810
- const value = Reflect.get(payload, claim);
7811
- if (typeof value !== "number" || !Number.isFinite(value)) {
7968
+ const parsed = errors12.trySync(function parseJson() {
7969
+ return JSON.parse(body);
7970
+ });
7971
+ if (parsed.error) {
7812
7972
  return null;
7813
7973
  }
7814
- return value;
7815
- }
7816
- function validateAccessTokenTimestamp(token, logger) {
7817
- const segments = token.split(".");
7818
- const payloadSegment = segments[1];
7819
- if (payloadSegment === undefined || payloadSegment.length === 0) {
7820
- logger.error("access token payload missing");
7821
- throw errors11.wrap(ErrMalformedAccessToken, "payload missing");
7822
- }
7823
- const payload = decodeBase64UrlJson(payloadSegment);
7824
- const exp = numericClaim(payload, "exp");
7825
- if (exp === null) {
7826
- logger.error("access token exp missing");
7827
- throw errors11.wrap(ErrMalformedAccessToken, "exp missing");
7974
+ const raw = parsed.data;
7975
+ if (typeof raw !== "object" || raw === null) {
7976
+ return null;
7828
7977
  }
7829
- const expiresAtMs = exp * 1000;
7830
- if (expiresAtMs <= Date.now() + TOKEN_EXPIRY_SKEW_MS) {
7831
- logger.error("access token expired");
7832
- throw ErrTokenExpired;
7978
+ const result = {};
7979
+ const error = readStringField(raw, "error");
7980
+ if (error !== undefined) {
7981
+ result.error = error;
7833
7982
  }
7834
- }
7835
- function resolveAccessToken(token, logger) {
7836
- if (isMalformedJws(token)) {
7837
- logger.error({ prefix: ACCESS_TOKEN_PREFIX }, "malformed access token");
7838
- throw errors11.wrap(ErrMalformedAccessToken, `token must start with '${ACCESS_TOKEN_PREFIX}' and contain two dots`);
7983
+ const detail = readStringField(raw, "detail");
7984
+ if (detail !== undefined) {
7985
+ result.detail = detail;
7839
7986
  }
7840
- validateAccessTokenTimestamp(token, logger);
7841
- return { value: token, [resolvedAccessTokenBrand]: true };
7842
- }
7843
-
7844
- // src/client/auth/storage.ts
7845
- var MANAGED_AUTH_ACCESS_TOKEN_KEY_PREFIX = "primer:access-token";
7846
- function managedAuthAccessTokenStorageKey(publishableKey) {
7847
- return `${MANAGED_AUTH_ACCESS_TOKEN_KEY_PREFIX}:${publishableKey}`;
7848
- }
7849
- function managedAuthStorage(logger) {
7850
- if (typeof globalThis.sessionStorage === "undefined") {
7851
- logger.error("managed auth storage unavailable");
7852
- throw ErrAuthUnavailable;
7987
+ const minimumSdkVersion = readStringField(raw, "minimumSdkVersion");
7988
+ if (minimumSdkVersion !== undefined) {
7989
+ result.minimumSdkVersion = minimumSdkVersion;
7853
7990
  }
7854
- return globalThis.sessionStorage;
7855
- }
7856
- function loadManagedAuthAccessToken(storage, publishableKey) {
7857
- const token = storage.getItem(managedAuthAccessTokenStorageKey(publishableKey));
7858
- if (token === null || token.length === 0) {
7859
- return null;
7991
+ const receivedSdkVersion = readStringField(raw, "receivedSdkVersion");
7992
+ if (receivedSdkVersion !== undefined) {
7993
+ result.receivedSdkVersion = receivedSdkVersion;
7860
7994
  }
7861
- return token;
7862
- }
7863
- function storeManagedAuthAccessToken(storage, publishableKey, accessToken) {
7864
- storage.setItem(managedAuthAccessTokenStorageKey(publishableKey), accessToken);
7865
- }
7866
- function clearManagedAuthAccessToken(storage, publishableKey) {
7867
- storage.removeItem(managedAuthAccessTokenStorageKey(publishableKey));
7868
- }
7869
-
7870
- // src/client/auth/provider.ts
7871
- function resolveProvidedAccessToken(token, logger) {
7872
- const result = errors12.trySync(function resolveProvidedToken() {
7873
- return resolveAccessToken(token, logger);
7874
- });
7875
- if (result.error) {
7876
- return { kind: "fatal", error: result.error };
7995
+ const upgradeUrl = readStringField(raw, "upgradeUrl");
7996
+ if (upgradeUrl !== undefined) {
7997
+ result.upgradeUrl = upgradeUrl;
7877
7998
  }
7878
- return { kind: "resolved", accessToken: result.data };
7999
+ return result;
7879
8000
  }
7880
- function resolveHostedAccessToken(token, storage, options) {
7881
- const result = errors12.trySync(function resolveHostedToken() {
7882
- return resolveAccessToken(token, options.logger);
7883
- });
7884
- if (result.error) {
7885
- return { kind: "sign-in-failed", error: result.error };
8001
+ function httpSentinel(status, body) {
8002
+ if (status === 400) {
8003
+ const parsed = parseAdvanceErrorBody(body);
8004
+ if (parsed?.error === "sdk_upgrade_required") {
8005
+ return ErrSdkUpgradeRequired;
8006
+ }
8007
+ return ErrBadRequest;
7886
8008
  }
7887
- storeManagedAuthAccessToken(storage, options.publishableKey, token);
7888
- return {
7889
- kind: "resolved",
7890
- accessToken: result.data,
7891
- clearCachedAccessToken: function clearCachedAccessToken() {
7892
- clearManagedAuthAccessToken(storage, options.publishableKey);
8009
+ if (status === 401) {
8010
+ const parsed = parseAdvanceErrorBody(body);
8011
+ if (parsed?.detail === "token_expired") {
8012
+ return ErrTokenExpired;
7893
8013
  }
7894
- };
7895
- }
7896
- function resolveStoredAccessToken(storage, options) {
7897
- const stored = loadManagedAuthAccessToken(storage, options.publishableKey);
7898
- if (stored === null) {
7899
- return { kind: "missing" };
8014
+ return ErrInvalidAccessToken;
7900
8015
  }
7901
- const result = errors12.trySync(function resolveStoredToken() {
7902
- return resolveAccessToken(stored, options.logger);
7903
- });
7904
- if (result.error) {
7905
- clearManagedAuthAccessToken(storage, options.publishableKey);
7906
- return { kind: "missing" };
8016
+ if (status === 403) {
8017
+ return ErrForbidden;
7907
8018
  }
7908
- return {
7909
- kind: "resolved",
7910
- accessToken: result.data,
7911
- clearCachedAccessToken: function clearCachedAccessToken() {
7912
- clearManagedAuthAccessToken(storage, options.publishableKey);
7913
- }
7914
- };
7915
- }
7916
- function resolveExistingAccessToken(options) {
7917
- if (options.accessToken !== undefined) {
7918
- return resolveProvidedAccessToken(options.accessToken, options.logger);
8019
+ if (status === 404) {
8020
+ return ErrNotFound;
7919
8021
  }
7920
- const storageResult = errors12.trySync(function readManagedAuthStorage() {
7921
- return managedAuthStorage(options.logger);
7922
- });
7923
- if (storageResult.error) {
7924
- return { kind: "auth-unavailable", error: storageResult.error };
8022
+ if (status === 409) {
8023
+ return ErrConflict;
7925
8024
  }
7926
- return resolveStoredAccessToken(storageResult.data, options);
7927
- }
7928
- function authFailureResult(error) {
7929
- if (errors12.is(error, ErrAuthConfigInvalid)) {
7930
- return { kind: "auth-config-invalid", error };
8025
+ if (status === 429) {
8026
+ return ErrRateLimited;
7931
8027
  }
7932
- if (errors12.is(error, ErrAuthUnavailable)) {
7933
- return { kind: "auth-unavailable", error };
8028
+ if (status === 502 || status === 503 || status === 504) {
8029
+ return ErrServiceUnavailable;
7934
8030
  }
7935
- return { kind: "sign-in-failed", error };
8031
+ return ErrServerError;
7936
8032
  }
7937
- async function beginHostedLogin(options) {
7938
- const logger = options.logger;
7939
- const contextResult = errors12.trySync(function readContext() {
7940
- const storage = managedAuthStorage(logger);
7941
- const url = currentUrl(logger);
7942
- const clientState = randomClientState(logger);
7943
- return { storage, url, clientState };
7944
- });
7945
- if (contextResult.error) {
7946
- return authFailureResult(contextResult.error);
8033
+ function buildSdkUpgradeRequiredError(sentinel, text, logger) {
8034
+ const parsed = parseAdvanceErrorBody(text);
8035
+ if (parsed === null) {
8036
+ logger.error({
8037
+ receivedSdkVersion: null,
8038
+ minimumSdkVersion: "<unknown>",
8039
+ upgradeUrl: NPM_PACKAGE_URL
8040
+ }, "sdk upgrade required");
8041
+ const message2 = `<missing> < <unknown>; bump @superbuilders/primer-tives at ${NPM_PACKAGE_URL}`;
8042
+ return errors12.wrap(sentinel, message2);
7947
8043
  }
7948
- const context = contextResult.data;
7949
- const accessTokenResult = await errors12.try(beginHostedPopup({
7950
- origin: options.origin,
7951
- publishableKey: options.publishableKey,
7952
- currentUrl: context.url,
7953
- clientState: context.clientState,
7954
- logger
7955
- }));
7956
- if (accessTokenResult.error) {
7957
- return authFailureResult(accessTokenResult.error);
8044
+ const minimumSdkVersion = parsed.minimumSdkVersion === undefined ? "<unknown>" : parsed.minimumSdkVersion;
8045
+ const receivedSdkVersion = parsed.receivedSdkVersion === undefined ? null : parsed.receivedSdkVersion;
8046
+ const receivedDisplay = receivedSdkVersion === null ? "<missing>" : receivedSdkVersion;
8047
+ const upgradeUrl = parsed.upgradeUrl === undefined ? NPM_PACKAGE_URL : parsed.upgradeUrl;
8048
+ logger.error({
8049
+ receivedSdkVersion,
8050
+ minimumSdkVersion,
8051
+ upgradeUrl
8052
+ }, "sdk upgrade required");
8053
+ const message = `${receivedDisplay} < ${minimumSdkVersion}; bump @superbuilders/primer-tives at ${upgradeUrl}`;
8054
+ return errors12.wrap(sentinel, message);
8055
+ }
8056
+ function isAbortError(err) {
8057
+ if (err instanceof DOMException && err.name === "AbortError") {
8058
+ return true;
7958
8059
  }
7959
- return resolveHostedAccessToken(accessTokenResult.data, context.storage, options);
8060
+ if (err instanceof DOMException && err.name === "TimeoutError") {
8061
+ return true;
8062
+ }
8063
+ return false;
7960
8064
  }
7961
-
7962
- // src/client/auth-state.ts
7963
- function pendingLogin(config) {
7964
- let pending;
7965
- function login() {
7966
- if (pending !== undefined) {
7967
- return pending;
8065
+ function createTransport(tc) {
8066
+ const fetchFn = tc.fetch ? tc.fetch : globalThis.fetch;
8067
+ const logger = tc.logger;
8068
+ const advanceUrl = new URL(ADVANCE_PATH, tc.origin).toString();
8069
+ function transportSignal() {
8070
+ if (tc.abort) {
8071
+ return tc.abort.signal;
7968
8072
  }
7969
- pending = config.login().finally(function clearPending() {
7970
- pending = undefined;
8073
+ return;
8074
+ }
8075
+ async function transport(body) {
8076
+ logger.debug({
8077
+ intentKind: body.intent.kind,
8078
+ subject: body.subject
8079
+ }, "transport request");
8080
+ const fetchResult = await fetchFn(advanceUrl, {
8081
+ method: "POST",
8082
+ mode: "cors",
8083
+ credentials: "omit",
8084
+ headers: {
8085
+ "Content-Type": "application/json",
8086
+ Authorization: `Bearer ${tc.accessToken.value}`,
8087
+ "X-Primer-Publishable-Key": tc.publishableKey,
8088
+ "X-Primer-SDK-Version": SDK_VERSION
8089
+ },
8090
+ body: JSON.stringify(body),
8091
+ signal: transportSignal()
8092
+ }).then(function ok(r) {
8093
+ return { ok: true, response: r };
8094
+ }, function fail(err) {
8095
+ return { ok: false, error: err };
7971
8096
  });
7972
- return pending;
8097
+ if (!fetchResult.ok) {
8098
+ if (isAbortError(fetchResult.error)) {
8099
+ logger.error({
8100
+ intentKind: body.intent.kind
8101
+ }, "transport timeout");
8102
+ return { ok: false, error: errors12.wrap(ErrTimeout, fetchResult.error.message) };
8103
+ }
8104
+ logger.error({
8105
+ error: fetchResult.error
8106
+ }, "transport network error");
8107
+ return { ok: false, error: errors12.wrap(ErrNetwork, fetchResult.error.message) };
8108
+ }
8109
+ const res = fetchResult.response;
8110
+ if (!res.ok) {
8111
+ const text = await res.text().catch(function fallback() {
8112
+ return "";
8113
+ });
8114
+ const sentinel = httpSentinel(res.status, text);
8115
+ if (errors12.is(sentinel, ErrSdkUpgradeRequired)) {
8116
+ return { ok: false, error: buildSdkUpgradeRequiredError(sentinel, text, logger) };
8117
+ }
8118
+ logger.error({
8119
+ status: res.status,
8120
+ body: text,
8121
+ intentKind: body.intent.kind,
8122
+ subject: body.subject
8123
+ }, "transport http error");
8124
+ return { ok: false, error: errors12.wrap(sentinel, text) };
8125
+ }
8126
+ const jsonResult = await res.json().then(function ok(data) {
8127
+ return { ok: true, data };
8128
+ }, function fail(err) {
8129
+ return { ok: false, error: err };
8130
+ });
8131
+ if (!jsonResult.ok) {
8132
+ logger.error({
8133
+ error: jsonResult.error
8134
+ }, "transport json parse failed");
8135
+ return { ok: false, error: errors12.wrap(ErrJsonParse, jsonResult.error.message) };
8136
+ }
8137
+ logger.debug({
8138
+ intentKind: body.intent.kind
8139
+ }, "transport success");
8140
+ return { ok: true, data: jsonResult.data };
7973
8141
  }
7974
- return login;
7975
- }
7976
- function signInRequiredState(config) {
7977
- return {
7978
- phase: "sign-in-required",
7979
- login: pendingLogin(config),
7980
- toJSON: poisonToJSON
7981
- };
7982
- }
7983
- function signInFailedState(config) {
7984
- return {
7985
- phase: "sign-in-failed",
7986
- error: config.error,
7987
- login: pendingLogin(config),
7988
- toJSON: poisonToJSON
7989
- };
7990
- }
7991
- function authUnavailableState(error) {
7992
- return {
7993
- phase: "auth-unavailable",
7994
- error,
7995
- toJSON: poisonToJSON
7996
- };
7997
- }
7998
- function authConfigInvalidState(error) {
7999
- return {
8000
- phase: "auth-config-invalid",
8001
- error,
8002
- toJSON: poisonToJSON
8003
- };
8142
+ return transport;
8004
8143
  }
8005
8144
 
8006
8145
  // src/client/start.ts
@@ -8023,6 +8162,12 @@ function primerOrigin(origin) {
8023
8162
  }
8024
8163
  return DEFAULT_PRIMER_ORIGIN;
8025
8164
  }
8165
+ function primerAuthOrigin(authOrigin, origin) {
8166
+ if (authOrigin !== undefined) {
8167
+ return authOrigin;
8168
+ }
8169
+ return origin;
8170
+ }
8026
8171
  async function startRuntime(config, resolved) {
8027
8172
  let reauthenticate = null;
8028
8173
  if (resolved.clearCachedAccessToken !== undefined) {
@@ -8066,7 +8211,7 @@ function makeSignInFailedState(config, error) {
8066
8211
  }
8067
8212
  async function loginAndStart(config) {
8068
8213
  const result = await beginHostedLogin({
8069
- origin: config.origin,
8214
+ origin: config.authOrigin,
8070
8215
  publishableKey: config.publishableKey,
8071
8216
  logger: config.logger
8072
8217
  });
@@ -8091,6 +8236,7 @@ async function start(options) {
8091
8236
  const config = {
8092
8237
  publishableKey: options.publishableKey,
8093
8238
  origin,
8239
+ authOrigin: primerAuthOrigin(options.authOrigin, origin),
8094
8240
  fetch: options.fetch,
8095
8241
  abort: options.abort,
8096
8242
  logger,
@@ -8123,4 +8269,4 @@ export {
8123
8269
  start
8124
8270
  };
8125
8271
 
8126
- //# debugId=D2887B68B961EF6364756E2164756E21
8272
+ //# debugId=9849F045A465657964756E2164756E21