@superbuilders/primer-tives 4.0.5 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -4
- package/dist/client/auth/access-token.d.ts +1 -1
- package/dist/client/auth/access-token.d.ts.map +1 -1
- package/dist/client/auth/hosted-popup.d.ts +1 -1
- package/dist/client/auth/hosted-popup.d.ts.map +1 -1
- package/dist/client/auth/provider.d.ts +2 -2
- package/dist/client/auth/provider.d.ts.map +1 -1
- package/dist/client/auth-state.d.ts +2 -2
- package/dist/client/auth-state.d.ts.map +1 -1
- package/dist/client/choice-state.d.ts +2 -2
- package/dist/client/choice-state.d.ts.map +1 -1
- package/dist/client/extended-text-state.d.ts +2 -2
- package/dist/client/extended-text-state.d.ts.map +1 -1
- package/dist/client/feedback-state.d.ts +4 -4
- package/dist/client/feedback-state.d.ts.map +1 -1
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +828 -812
- package/dist/client/index.js.map +24 -24
- package/dist/client/match-state.d.ts +2 -2
- package/dist/client/match-state.d.ts.map +1 -1
- package/dist/client/observation-state.d.ts +1 -1
- package/dist/client/observation-state.d.ts.map +1 -1
- package/dist/client/order-state.d.ts +2 -2
- package/dist/client/order-state.d.ts.map +1 -1
- package/dist/client/pci-state.d.ts +2 -2
- package/dist/client/pci-state.d.ts.map +1 -1
- package/dist/client/session-context.d.ts +2 -2
- package/dist/client/session-context.d.ts.map +1 -1
- package/dist/client/session.d.ts +4 -4
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/start.d.ts +3 -3
- package/dist/client/start.d.ts.map +1 -1
- package/dist/client/text-entry-state.d.ts +2 -2
- package/dist/client/text-entry-state.d.ts.map +1 -1
- package/dist/client/transport.d.ts +7 -6
- package/dist/client/transport.d.ts.map +1 -1
- package/dist/client/types.d.ts +8 -2
- package/dist/client/types.d.ts.map +1 -1
- package/dist/contracts/index.d.ts +4 -4
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js +12 -12
- package/dist/contracts/index.js.map +6 -6
- package/dist/contracts/pci-schemas.d.ts +4 -0
- package/dist/contracts/pci-schemas.d.ts.map +1 -1
- package/dist/contracts/pci.d.ts +1 -1
- package/dist/contracts/pci.d.ts.map +1 -1
- package/dist/contracts/review.d.ts +1 -1
- package/dist/contracts/review.d.ts.map +1 -1
- package/dist/contracts/validation.d.ts +24 -2
- package/dist/contracts/validation.d.ts.map +1 -1
- package/dist/grade-level.d.ts +1 -1
- package/dist/grade-level.d.ts.map +1 -1
- package/dist/grade-level.js.map +1 -1
- package/dist/subject-pcis.d.ts +1 -1
- package/dist/subject-pcis.d.ts.map +1 -1
- package/dist/subject-pcis.js.map +2 -2
- package/dist/subject.d.ts +1 -1
- package/dist/subject.d.ts.map +1 -1
- package/dist/subject.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
package/dist/client/index.js
CHANGED
|
@@ -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,303 +6779,521 @@ function validateSubmissionForInteraction(interaction, submission) {
|
|
|
6779
6779
|
function submissionValidationMessage(result) {
|
|
6780
6780
|
return result.issues.join("; ");
|
|
6781
6781
|
}
|
|
6782
|
-
// src/client/
|
|
6783
|
-
import * as
|
|
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/
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
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
|
-
|
|
6794
|
+
const dotCount = token.split(".").length - 1;
|
|
6795
|
+
return dotCount !== 2;
|
|
6800
6796
|
}
|
|
6801
|
-
function
|
|
6802
|
-
|
|
6803
|
-
|
|
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
|
-
|
|
6806
|
-
return
|
|
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 (
|
|
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
|
|
6812
|
-
if (typeof
|
|
6825
|
+
const value = Reflect.get(payload, claim);
|
|
6826
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
6813
6827
|
return null;
|
|
6814
6828
|
}
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
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
|
|
6821
|
-
|
|
6822
|
-
|
|
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
|
|
6825
|
-
if (
|
|
6826
|
-
|
|
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
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
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
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
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
|
|
6839
|
-
if (
|
|
6840
|
-
|
|
6841
|
-
|
|
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
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
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
|
-
|
|
6854
|
-
|
|
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
|
-
|
|
6857
|
-
|
|
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
|
-
|
|
6860
|
-
|
|
6922
|
+
return field;
|
|
6923
|
+
}
|
|
6924
|
+
function readPopupMessage(event, popup, config, expectedOrigin) {
|
|
6925
|
+
if (event.source !== popup) {
|
|
6926
|
+
return { kind: "ignore" };
|
|
6861
6927
|
}
|
|
6862
|
-
if (
|
|
6863
|
-
return
|
|
6928
|
+
if (event.origin !== expectedOrigin) {
|
|
6929
|
+
return { kind: "ignore" };
|
|
6864
6930
|
}
|
|
6865
|
-
|
|
6866
|
-
|
|
6931
|
+
const data = event.data;
|
|
6932
|
+
if (!isRecord(data)) {
|
|
6933
|
+
return { kind: "ignore" };
|
|
6867
6934
|
}
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
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
|
|
6882
|
-
|
|
6883
|
-
|
|
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 (
|
|
6898
|
-
return
|
|
6943
|
+
if (state !== config.clientState) {
|
|
6944
|
+
return { kind: "error", error: ErrAuthStateMismatch };
|
|
6899
6945
|
}
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
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
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
6924
|
-
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
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
|
+
async function waitForPopupMessage(popup, config, expectedOrigin) {
|
|
6960
|
+
let __stack = [];
|
|
6961
|
+
try {
|
|
6962
|
+
const logger = config.logger;
|
|
6963
|
+
const stack = __using(__stack, new AsyncDisposableStack, 1);
|
|
6964
|
+
stack.defer(function closePopup() {
|
|
6965
|
+
popup.close();
|
|
6966
|
+
});
|
|
6967
|
+
const result = await new Promise(function waitForMessage(resolve, reject) {
|
|
6968
|
+
let settled = false;
|
|
6969
|
+
function finishWithError(error) {
|
|
6970
|
+
if (settled) {
|
|
6971
|
+
return;
|
|
6972
|
+
}
|
|
6973
|
+
settled = true;
|
|
6974
|
+
reject(error);
|
|
6940
6975
|
}
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6976
|
+
function finishWithToken(token) {
|
|
6977
|
+
if (settled) {
|
|
6978
|
+
return;
|
|
6979
|
+
}
|
|
6980
|
+
settled = true;
|
|
6981
|
+
logger.debug("hosted auth popup completed");
|
|
6982
|
+
resolve(token);
|
|
6983
|
+
}
|
|
6984
|
+
const timeoutId = globalThis.setTimeout(function timeout() {
|
|
6985
|
+
logger.error("hosted auth popup timed out");
|
|
6986
|
+
finishWithError(ErrAuthCancelled);
|
|
6987
|
+
}, DEFAULT_POPUP_TIMEOUT_MS);
|
|
6988
|
+
stack.defer(function clearPopupTimeout() {
|
|
6989
|
+
globalThis.clearTimeout(timeoutId);
|
|
6950
6990
|
});
|
|
6951
|
-
const
|
|
6952
|
-
|
|
6953
|
-
|
|
6991
|
+
const closedPollId = globalThis.setInterval(function checkClosed() {
|
|
6992
|
+
if (!popup.closed) {
|
|
6993
|
+
return;
|
|
6994
|
+
}
|
|
6995
|
+
logger.error("hosted auth popup closed");
|
|
6996
|
+
finishWithError(ErrAuthCancelled);
|
|
6997
|
+
}, POPUP_POLL_MS);
|
|
6998
|
+
stack.defer(function clearClosedPoll() {
|
|
6999
|
+
globalThis.clearInterval(closedPollId);
|
|
7000
|
+
});
|
|
7001
|
+
function handleMessage(event) {
|
|
7002
|
+
const result2 = readPopupMessage(event, popup, config, expectedOrigin);
|
|
7003
|
+
if (result2.kind === "ignore") {
|
|
7004
|
+
return;
|
|
7005
|
+
}
|
|
7006
|
+
if (result2.kind === "error") {
|
|
7007
|
+
logger.error({ error: result2.error }, "hosted auth popup failed");
|
|
7008
|
+
finishWithError(result2.error);
|
|
7009
|
+
return;
|
|
7010
|
+
}
|
|
7011
|
+
finishWithToken(result2.accessToken);
|
|
6954
7012
|
}
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
subject: body.subject
|
|
6960
|
-
}, "transport http error");
|
|
6961
|
-
return { ok: false, error: errors3.wrap(sentinel, text) };
|
|
6962
|
-
}
|
|
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 };
|
|
7013
|
+
globalThis.addEventListener("message", handleMessage);
|
|
7014
|
+
stack.defer(function removeMessageListener() {
|
|
7015
|
+
globalThis.removeEventListener("message", handleMessage);
|
|
7016
|
+
});
|
|
6967
7017
|
});
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
|
|
6973
|
-
|
|
6974
|
-
logger.debug({
|
|
6975
|
-
intentKind: body.intent.kind
|
|
6976
|
-
}, "transport success");
|
|
6977
|
-
return { ok: true, data: jsonResult.data };
|
|
7018
|
+
return result;
|
|
7019
|
+
} catch (_catch) {
|
|
7020
|
+
var _err = _catch, _hasErr = 1;
|
|
7021
|
+
} finally {
|
|
7022
|
+
var _promise = __callDispose(__stack, _err, _hasErr);
|
|
7023
|
+
_promise && await _promise;
|
|
6978
7024
|
}
|
|
6979
|
-
|
|
7025
|
+
}
|
|
7026
|
+
async function beginHostedPopup(config) {
|
|
7027
|
+
const logger = config.logger;
|
|
7028
|
+
const url = hostedAuthUrl(config);
|
|
7029
|
+
if (!URL.canParse(config.origin)) {
|
|
7030
|
+
logger.error({ origin: config.origin }, "hosted auth origin invalid");
|
|
7031
|
+
throw ErrAuthConfigInvalid;
|
|
7032
|
+
}
|
|
7033
|
+
const expectedOrigin = new URL(config.origin).origin;
|
|
7034
|
+
const popup = openAuthPopup(url, logger);
|
|
7035
|
+
return waitForPopupMessage(popup, config, expectedOrigin);
|
|
6980
7036
|
}
|
|
6981
7037
|
|
|
6982
|
-
// src/client/
|
|
6983
|
-
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
7038
|
+
// src/client/auth/storage.ts
|
|
7039
|
+
var MANAGED_AUTH_ACCESS_TOKEN_KEY_PREFIX = "primer:access-token";
|
|
7040
|
+
function managedAuthAccessTokenStorageKey(publishableKey) {
|
|
7041
|
+
return `${MANAGED_AUTH_ACCESS_TOKEN_KEY_PREFIX}:${publishableKey}`;
|
|
7042
|
+
}
|
|
7043
|
+
function managedAuthStorage(logger) {
|
|
7044
|
+
if (typeof globalThis.sessionStorage === "undefined") {
|
|
7045
|
+
logger.error("managed auth storage unavailable");
|
|
7046
|
+
throw ErrAuthUnavailable;
|
|
7047
|
+
}
|
|
7048
|
+
return globalThis.sessionStorage;
|
|
7049
|
+
}
|
|
7050
|
+
function loadManagedAuthAccessToken(storage, publishableKey) {
|
|
7051
|
+
const token = storage.getItem(managedAuthAccessTokenStorageKey(publishableKey));
|
|
7052
|
+
if (token === null || token.length === 0) {
|
|
7053
|
+
return null;
|
|
7054
|
+
}
|
|
7055
|
+
return token;
|
|
7056
|
+
}
|
|
7057
|
+
function storeManagedAuthAccessToken(storage, publishableKey, accessToken) {
|
|
7058
|
+
storage.setItem(managedAuthAccessTokenStorageKey(publishableKey), accessToken);
|
|
7059
|
+
}
|
|
7060
|
+
function clearManagedAuthAccessToken(storage, publishableKey) {
|
|
7061
|
+
storage.removeItem(managedAuthAccessTokenStorageKey(publishableKey));
|
|
6988
7062
|
}
|
|
6989
7063
|
|
|
6990
|
-
// src/client/
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
const submission = { type: "choice", selectedKeys };
|
|
6998
|
-
const key = JSON.stringify(submission);
|
|
6999
|
-
if (timeoutPending) {
|
|
7000
|
-
return Promise.resolve(ctx.errored(errors4.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
|
|
7001
|
-
}
|
|
7002
|
-
if (submitPending) {
|
|
7003
|
-
if (submitKey === key) {
|
|
7004
|
-
return submitPending;
|
|
7005
|
-
}
|
|
7006
|
-
return Promise.resolve(ctx.errored(errors4.wrap(ErrConflict, "cannot submit a different choice payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
|
|
7007
|
-
}
|
|
7008
|
-
const validation = validateSubmissionForInteraction(interaction, submission);
|
|
7009
|
-
if (!validation.ok) {
|
|
7010
|
-
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 }));
|
|
7012
|
-
}
|
|
7013
|
-
submitKey = key;
|
|
7014
|
-
submitPending = ctx.execute({ kind: "interaction", submission }, "interaction").finally(function clearPending() {
|
|
7015
|
-
submitPending = undefined;
|
|
7016
|
-
submitKey = undefined;
|
|
7017
|
-
});
|
|
7018
|
-
return submitPending;
|
|
7064
|
+
// src/client/auth/provider.ts
|
|
7065
|
+
function resolveProvidedAccessToken(token, logger) {
|
|
7066
|
+
const result = errors4.trySync(function resolveProvidedToken() {
|
|
7067
|
+
return resolveAccessToken(token, logger);
|
|
7068
|
+
});
|
|
7069
|
+
if (result.error) {
|
|
7070
|
+
return { kind: "fatal", error: result.error };
|
|
7019
7071
|
}
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7072
|
+
return { kind: "resolved", accessToken: result.data };
|
|
7073
|
+
}
|
|
7074
|
+
function resolveHostedAccessToken(token, storage, options) {
|
|
7075
|
+
const result = errors4.trySync(function resolveHostedToken() {
|
|
7076
|
+
return resolveAccessToken(token, options.logger);
|
|
7077
|
+
});
|
|
7078
|
+
if (result.error) {
|
|
7079
|
+
return { kind: "sign-in-failed", error: result.error };
|
|
7080
|
+
}
|
|
7081
|
+
storeManagedAuthAccessToken(storage, options.publishableKey, token);
|
|
7082
|
+
return {
|
|
7083
|
+
kind: "resolved",
|
|
7084
|
+
accessToken: result.data,
|
|
7085
|
+
clearCachedAccessToken: function clearCachedAccessToken() {
|
|
7086
|
+
clearManagedAuthAccessToken(storage, options.publishableKey);
|
|
7027
7087
|
}
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7088
|
+
};
|
|
7089
|
+
}
|
|
7090
|
+
function resolveStoredAccessToken(storage, options) {
|
|
7091
|
+
const stored = loadManagedAuthAccessToken(storage, options.publishableKey);
|
|
7092
|
+
if (stored === null) {
|
|
7093
|
+
return { kind: "missing" };
|
|
7094
|
+
}
|
|
7095
|
+
const result = errors4.trySync(function resolveStoredToken() {
|
|
7096
|
+
return resolveAccessToken(stored, options.logger);
|
|
7097
|
+
});
|
|
7098
|
+
if (result.error) {
|
|
7099
|
+
clearManagedAuthAccessToken(storage, options.publishableKey);
|
|
7100
|
+
return { kind: "missing" };
|
|
7032
7101
|
}
|
|
7033
7102
|
return {
|
|
7034
|
-
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
options,
|
|
7040
|
-
maxChoices,
|
|
7041
|
-
minChoices,
|
|
7042
|
-
submitChoice: beginSubmit,
|
|
7043
|
-
timeout: beginTimeout,
|
|
7044
|
-
toJSON: poisonToJSON
|
|
7103
|
+
kind: "resolved",
|
|
7104
|
+
accessToken: result.data,
|
|
7105
|
+
clearCachedAccessToken: function clearCachedAccessToken() {
|
|
7106
|
+
clearManagedAuthAccessToken(storage, options.publishableKey);
|
|
7107
|
+
}
|
|
7045
7108
|
};
|
|
7046
7109
|
}
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
7060
|
-
|
|
7061
|
-
|
|
7062
|
-
|
|
7063
|
-
|
|
7064
|
-
|
|
7065
|
-
|
|
7066
|
-
|
|
7067
|
-
|
|
7068
|
-
|
|
7069
|
-
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7074
|
-
|
|
7110
|
+
function resolveExistingAccessToken(options) {
|
|
7111
|
+
if (options.accessToken !== undefined) {
|
|
7112
|
+
return resolveProvidedAccessToken(options.accessToken, options.logger);
|
|
7113
|
+
}
|
|
7114
|
+
const storageResult = errors4.trySync(function readManagedAuthStorage() {
|
|
7115
|
+
return managedAuthStorage(options.logger);
|
|
7116
|
+
});
|
|
7117
|
+
if (storageResult.error) {
|
|
7118
|
+
return { kind: "auth-unavailable", error: storageResult.error };
|
|
7119
|
+
}
|
|
7120
|
+
return resolveStoredAccessToken(storageResult.data, options);
|
|
7121
|
+
}
|
|
7122
|
+
function authFailureResult(error) {
|
|
7123
|
+
if (errors4.is(error, ErrAuthConfigInvalid)) {
|
|
7124
|
+
return { kind: "auth-config-invalid", error };
|
|
7125
|
+
}
|
|
7126
|
+
if (errors4.is(error, ErrAuthUnavailable)) {
|
|
7127
|
+
return { kind: "auth-unavailable", error };
|
|
7128
|
+
}
|
|
7129
|
+
return { kind: "sign-in-failed", error };
|
|
7130
|
+
}
|
|
7131
|
+
async function beginHostedLogin(options) {
|
|
7132
|
+
const logger = options.logger;
|
|
7133
|
+
const contextResult = errors4.trySync(function readContext() {
|
|
7134
|
+
const storage = managedAuthStorage(logger);
|
|
7135
|
+
const url = currentUrl(logger);
|
|
7136
|
+
const clientState = randomClientState(logger);
|
|
7137
|
+
return { storage, url, clientState };
|
|
7138
|
+
});
|
|
7139
|
+
if (contextResult.error) {
|
|
7140
|
+
return authFailureResult(contextResult.error);
|
|
7141
|
+
}
|
|
7142
|
+
const context = contextResult.data;
|
|
7143
|
+
const accessTokenResult = await errors4.try(beginHostedPopup({
|
|
7144
|
+
origin: options.origin,
|
|
7145
|
+
publishableKey: options.publishableKey,
|
|
7146
|
+
currentUrl: context.url,
|
|
7147
|
+
clientState: context.clientState,
|
|
7148
|
+
logger
|
|
7149
|
+
}));
|
|
7150
|
+
if (accessTokenResult.error) {
|
|
7151
|
+
return authFailureResult(accessTokenResult.error);
|
|
7152
|
+
}
|
|
7153
|
+
return resolveHostedAccessToken(accessTokenResult.data, context.storage, options);
|
|
7154
|
+
}
|
|
7155
|
+
|
|
7156
|
+
// src/client/consumed.ts
|
|
7157
|
+
function poisonToJSON() {
|
|
7158
|
+
throw ErrNotSerializable;
|
|
7159
|
+
}
|
|
7160
|
+
|
|
7161
|
+
// src/client/auth-state.ts
|
|
7162
|
+
function pendingLogin(config) {
|
|
7163
|
+
let pending;
|
|
7164
|
+
function login() {
|
|
7165
|
+
if (pending !== undefined) {
|
|
7166
|
+
return pending;
|
|
7167
|
+
}
|
|
7168
|
+
pending = config.login().finally(function clearPending() {
|
|
7169
|
+
pending = undefined;
|
|
7170
|
+
});
|
|
7171
|
+
return pending;
|
|
7172
|
+
}
|
|
7173
|
+
return login;
|
|
7174
|
+
}
|
|
7175
|
+
function signInRequiredState(config) {
|
|
7176
|
+
return {
|
|
7177
|
+
phase: "sign-in-required",
|
|
7178
|
+
login: pendingLogin(config),
|
|
7179
|
+
toJSON: poisonToJSON
|
|
7180
|
+
};
|
|
7181
|
+
}
|
|
7182
|
+
function signInFailedState(config) {
|
|
7183
|
+
return {
|
|
7184
|
+
phase: "sign-in-failed",
|
|
7185
|
+
error: config.error,
|
|
7186
|
+
login: pendingLogin(config),
|
|
7187
|
+
toJSON: poisonToJSON
|
|
7188
|
+
};
|
|
7189
|
+
}
|
|
7190
|
+
function authUnavailableState(error) {
|
|
7191
|
+
return {
|
|
7192
|
+
phase: "auth-unavailable",
|
|
7193
|
+
error,
|
|
7194
|
+
toJSON: poisonToJSON
|
|
7195
|
+
};
|
|
7196
|
+
}
|
|
7197
|
+
function authConfigInvalidState(error) {
|
|
7198
|
+
return {
|
|
7199
|
+
phase: "auth-config-invalid",
|
|
7200
|
+
error,
|
|
7201
|
+
toJSON: poisonToJSON
|
|
7202
|
+
};
|
|
7203
|
+
}
|
|
7204
|
+
|
|
7205
|
+
// src/client/session.ts
|
|
7206
|
+
import * as errors11 from "@superbuilders/errors";
|
|
7207
|
+
|
|
7208
|
+
// src/client/choice-state.ts
|
|
7209
|
+
import * as errors5 from "@superbuilders/errors";
|
|
7210
|
+
function choiceState(ctx, body, stimulus, interaction, options, maxChoices, minChoices) {
|
|
7211
|
+
let submitPending;
|
|
7212
|
+
let submitKey;
|
|
7213
|
+
let timeoutPending;
|
|
7214
|
+
function beginSubmit(selectedKeys) {
|
|
7215
|
+
const submission = { type: "choice", selectedKeys };
|
|
7216
|
+
const key = JSON.stringify(submission);
|
|
7217
|
+
if (timeoutPending) {
|
|
7218
|
+
return Promise.resolve(ctx.errored(errors5.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
|
|
7219
|
+
}
|
|
7220
|
+
if (submitPending) {
|
|
7221
|
+
if (submitKey === key) {
|
|
7222
|
+
return submitPending;
|
|
7223
|
+
}
|
|
7224
|
+
return Promise.resolve(ctx.errored(errors5.wrap(ErrConflict, "cannot submit a different choice payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
|
|
7225
|
+
}
|
|
7226
|
+
const validation = validateSubmissionForInteraction(interaction, submission);
|
|
7227
|
+
if (!validation.ok) {
|
|
7228
|
+
ctx.logger.error({ selectedKeys, issues: validation.issues }, "choice submit invalid");
|
|
7229
|
+
return Promise.resolve(ctx.errored(errors5.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
|
|
7230
|
+
}
|
|
7231
|
+
submitKey = key;
|
|
7232
|
+
submitPending = ctx.execute({ kind: "interaction", submission }, "interaction").finally(function clearPending() {
|
|
7233
|
+
submitPending = undefined;
|
|
7234
|
+
submitKey = undefined;
|
|
7235
|
+
});
|
|
7236
|
+
return submitPending;
|
|
7237
|
+
}
|
|
7238
|
+
function beginTimeout() {
|
|
7239
|
+
const intent = { kind: "timeout" };
|
|
7240
|
+
if (submitPending) {
|
|
7241
|
+
return Promise.resolve(ctx.errored(errors5.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
|
|
7242
|
+
}
|
|
7243
|
+
if (timeoutPending) {
|
|
7244
|
+
return timeoutPending;
|
|
7245
|
+
}
|
|
7246
|
+
timeoutPending = ctx.execute(intent, "timeout").finally(function clearPending() {
|
|
7247
|
+
timeoutPending = undefined;
|
|
7248
|
+
});
|
|
7249
|
+
return timeoutPending;
|
|
7250
|
+
}
|
|
7251
|
+
return {
|
|
7252
|
+
phase: "interaction",
|
|
7253
|
+
kind: "choice",
|
|
7254
|
+
body,
|
|
7255
|
+
stimulus,
|
|
7256
|
+
interaction,
|
|
7257
|
+
options,
|
|
7258
|
+
maxChoices,
|
|
7259
|
+
minChoices,
|
|
7260
|
+
submitChoice: beginSubmit,
|
|
7261
|
+
timeout: beginTimeout,
|
|
7262
|
+
toJSON: poisonToJSON
|
|
7263
|
+
};
|
|
7264
|
+
}
|
|
7265
|
+
|
|
7266
|
+
// src/client/extended-text-state.ts
|
|
7267
|
+
import * as errors6 from "@superbuilders/errors";
|
|
7268
|
+
function extendedTextState(ctx, body, stimulus, interaction) {
|
|
7269
|
+
if (interaction.cardinality === "single") {
|
|
7270
|
+
let submitText = function(value) {
|
|
7271
|
+
const submission = { type: "extended-text", values: [value] };
|
|
7272
|
+
const key = JSON.stringify(submission);
|
|
7273
|
+
if (timeoutPending2) {
|
|
7274
|
+
return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
|
|
7275
|
+
}
|
|
7276
|
+
if (submitPending2) {
|
|
7277
|
+
if (submitKey2 === key) {
|
|
7278
|
+
return submitPending2;
|
|
7279
|
+
}
|
|
7280
|
+
return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot submit a different extended-text payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
|
|
7281
|
+
}
|
|
7282
|
+
const validation = validateSubmissionForInteraction(interaction, submission);
|
|
7283
|
+
if (!validation.ok) {
|
|
7284
|
+
ctx.logger.error({ value, issues: validation.issues }, "extended-text submit invalid");
|
|
7285
|
+
return Promise.resolve(ctx.errored(errors6.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
|
|
7286
|
+
}
|
|
7287
|
+
submitKey2 = key;
|
|
7288
|
+
submitPending2 = ctx.execute({ kind: "interaction", submission }, "interaction").finally(function clearPending() {
|
|
7289
|
+
submitPending2 = undefined;
|
|
7290
|
+
submitKey2 = undefined;
|
|
7291
|
+
});
|
|
7292
|
+
return submitPending2;
|
|
7075
7293
|
}, timeout2 = function() {
|
|
7076
7294
|
const intent = { kind: "timeout" };
|
|
7077
7295
|
if (submitPending2) {
|
|
7078
|
-
return Promise.resolve(ctx.errored(
|
|
7296
|
+
return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
|
|
7079
7297
|
}
|
|
7080
7298
|
if (timeoutPending2) {
|
|
7081
7299
|
return timeoutPending2;
|
|
@@ -7108,18 +7326,18 @@ function extendedTextState(ctx, body, stimulus, interaction) {
|
|
|
7108
7326
|
const submission = { type: "extended-text", values };
|
|
7109
7327
|
const key = JSON.stringify(submission);
|
|
7110
7328
|
if (timeoutPending) {
|
|
7111
|
-
return Promise.resolve(ctx.errored(
|
|
7329
|
+
return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
|
|
7112
7330
|
}
|
|
7113
7331
|
if (submitPending) {
|
|
7114
7332
|
if (submitKey === key) {
|
|
7115
7333
|
return submitPending;
|
|
7116
7334
|
}
|
|
7117
|
-
return Promise.resolve(ctx.errored(
|
|
7335
|
+
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
7336
|
}
|
|
7119
7337
|
const validation = validateSubmissionForInteraction(multi, submission);
|
|
7120
7338
|
if (!validation.ok) {
|
|
7121
7339
|
ctx.logger.error({ values, issues: validation.issues }, "extended-text submit invalid");
|
|
7122
|
-
return Promise.resolve(ctx.errored(
|
|
7340
|
+
return Promise.resolve(ctx.errored(errors6.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
|
|
7123
7341
|
}
|
|
7124
7342
|
submitKey = key;
|
|
7125
7343
|
submitPending = ctx.execute({ kind: "interaction", submission }, "interaction").finally(function clearPending() {
|
|
@@ -7131,7 +7349,7 @@ function extendedTextState(ctx, body, stimulus, interaction) {
|
|
|
7131
7349
|
function timeout() {
|
|
7132
7350
|
const intent = { kind: "timeout" };
|
|
7133
7351
|
if (submitPending) {
|
|
7134
|
-
return Promise.resolve(ctx.errored(
|
|
7352
|
+
return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
|
|
7135
7353
|
}
|
|
7136
7354
|
if (timeoutPending) {
|
|
7137
7355
|
return timeoutPending;
|
|
@@ -7157,7 +7375,7 @@ function extendedTextState(ctx, body, stimulus, interaction) {
|
|
|
7157
7375
|
}
|
|
7158
7376
|
|
|
7159
7377
|
// src/client/feedback-state.ts
|
|
7160
|
-
function feedbackState(ctx, body, stimulus, interaction, submission,
|
|
7378
|
+
function feedbackState(ctx, body, stimulus, interaction, submission, assessmentOutcome, feedbackContent, review) {
|
|
7161
7379
|
let pending;
|
|
7162
7380
|
return {
|
|
7163
7381
|
phase: "feedback",
|
|
@@ -7165,7 +7383,7 @@ function feedbackState(ctx, body, stimulus, interaction, submission, isCorrect,
|
|
|
7165
7383
|
stimulus,
|
|
7166
7384
|
interaction,
|
|
7167
7385
|
submission,
|
|
7168
|
-
|
|
7386
|
+
assessmentOutcome,
|
|
7169
7387
|
feedbackContent,
|
|
7170
7388
|
review,
|
|
7171
7389
|
advance: function advance() {
|
|
@@ -7180,7 +7398,7 @@ function feedbackState(ctx, body, stimulus, interaction, submission, isCorrect,
|
|
|
7180
7398
|
}
|
|
7181
7399
|
|
|
7182
7400
|
// src/client/match-state.ts
|
|
7183
|
-
import * as
|
|
7401
|
+
import * as errors7 from "@superbuilders/errors";
|
|
7184
7402
|
function matchState(ctx, body, stimulus, interaction) {
|
|
7185
7403
|
let submitPending;
|
|
7186
7404
|
let submitKey;
|
|
@@ -7189,18 +7407,18 @@ function matchState(ctx, body, stimulus, interaction) {
|
|
|
7189
7407
|
const submission = { type: "match", pairs };
|
|
7190
7408
|
const key = JSON.stringify(submission);
|
|
7191
7409
|
if (timeoutPending) {
|
|
7192
|
-
return Promise.resolve(ctx.errored(
|
|
7410
|
+
return Promise.resolve(ctx.errored(errors7.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
|
|
7193
7411
|
}
|
|
7194
7412
|
if (submitPending) {
|
|
7195
7413
|
if (submitKey === key) {
|
|
7196
7414
|
return submitPending;
|
|
7197
7415
|
}
|
|
7198
|
-
return Promise.resolve(ctx.errored(
|
|
7416
|
+
return Promise.resolve(ctx.errored(errors7.wrap(ErrConflict, "cannot submit a different match payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
|
|
7199
7417
|
}
|
|
7200
7418
|
const validation = validateSubmissionForInteraction(interaction, submission);
|
|
7201
7419
|
if (!validation.ok) {
|
|
7202
7420
|
ctx.logger.error({ pairs, issues: validation.issues }, "match submit invalid");
|
|
7203
|
-
return Promise.resolve(ctx.errored(
|
|
7421
|
+
return Promise.resolve(ctx.errored(errors7.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
|
|
7204
7422
|
}
|
|
7205
7423
|
submitKey = key;
|
|
7206
7424
|
submitPending = ctx.execute({ kind: "interaction", submission }, "interaction").finally(function clearPending() {
|
|
@@ -7212,7 +7430,7 @@ function matchState(ctx, body, stimulus, interaction) {
|
|
|
7212
7430
|
function timeout() {
|
|
7213
7431
|
const intent = { kind: "timeout" };
|
|
7214
7432
|
if (submitPending) {
|
|
7215
|
-
return Promise.resolve(ctx.errored(
|
|
7433
|
+
return Promise.resolve(ctx.errored(errors7.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
|
|
7216
7434
|
}
|
|
7217
7435
|
if (timeoutPending) {
|
|
7218
7436
|
return timeoutPending;
|
|
@@ -7257,7 +7475,7 @@ function observationState(ctx, body, stimulus) {
|
|
|
7257
7475
|
}
|
|
7258
7476
|
|
|
7259
7477
|
// src/client/order-state.ts
|
|
7260
|
-
import * as
|
|
7478
|
+
import * as errors8 from "@superbuilders/errors";
|
|
7261
7479
|
function orderState(ctx, body, stimulus, interaction) {
|
|
7262
7480
|
let submitPending;
|
|
7263
7481
|
let submitKey;
|
|
@@ -7266,18 +7484,18 @@ function orderState(ctx, body, stimulus, interaction) {
|
|
|
7266
7484
|
const submission = { type: "order", orderedKeys };
|
|
7267
7485
|
const key = JSON.stringify(submission);
|
|
7268
7486
|
if (timeoutPending) {
|
|
7269
|
-
return Promise.resolve(ctx.errored(
|
|
7487
|
+
return Promise.resolve(ctx.errored(errors8.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
|
|
7270
7488
|
}
|
|
7271
7489
|
if (submitPending) {
|
|
7272
7490
|
if (submitKey === key) {
|
|
7273
7491
|
return submitPending;
|
|
7274
7492
|
}
|
|
7275
|
-
return Promise.resolve(ctx.errored(
|
|
7493
|
+
return Promise.resolve(ctx.errored(errors8.wrap(ErrConflict, "cannot submit a different order payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
|
|
7276
7494
|
}
|
|
7277
7495
|
const validation = validateSubmissionForInteraction(interaction, submission);
|
|
7278
7496
|
if (!validation.ok) {
|
|
7279
7497
|
ctx.logger.error({ orderedKeys, issues: validation.issues }, "order submit invalid");
|
|
7280
|
-
return Promise.resolve(ctx.errored(
|
|
7498
|
+
return Promise.resolve(ctx.errored(errors8.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
|
|
7281
7499
|
}
|
|
7282
7500
|
submitKey = key;
|
|
7283
7501
|
submitPending = ctx.execute({ kind: "interaction", submission }, "interaction").finally(function clearPending() {
|
|
@@ -7289,7 +7507,7 @@ function orderState(ctx, body, stimulus, interaction) {
|
|
|
7289
7507
|
function timeout() {
|
|
7290
7508
|
const intent = { kind: "timeout" };
|
|
7291
7509
|
if (submitPending) {
|
|
7292
|
-
return Promise.resolve(ctx.errored(
|
|
7510
|
+
return Promise.resolve(ctx.errored(errors8.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
|
|
7293
7511
|
}
|
|
7294
7512
|
if (timeoutPending) {
|
|
7295
7513
|
return timeoutPending;
|
|
@@ -7315,7 +7533,7 @@ function orderState(ctx, body, stimulus, interaction) {
|
|
|
7315
7533
|
}
|
|
7316
7534
|
|
|
7317
7535
|
// src/client/pci-state.ts
|
|
7318
|
-
import * as
|
|
7536
|
+
import * as errors9 from "@superbuilders/errors";
|
|
7319
7537
|
function pciInteractionState(ctx, body, stimulus, interaction) {
|
|
7320
7538
|
let submitPending;
|
|
7321
7539
|
let submitKey;
|
|
@@ -7325,13 +7543,13 @@ function pciInteractionState(ctx, body, stimulus, interaction) {
|
|
|
7325
7543
|
const submission = { type: "portable-custom", pciId, value };
|
|
7326
7544
|
const key = JSON.stringify(submission);
|
|
7327
7545
|
if (timeoutPending) {
|
|
7328
|
-
return Promise.resolve(ctx.errored(
|
|
7546
|
+
return Promise.resolve(ctx.errored(errors9.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
|
|
7329
7547
|
}
|
|
7330
7548
|
if (submitPending) {
|
|
7331
7549
|
if (submitKey === key) {
|
|
7332
7550
|
return submitPending;
|
|
7333
7551
|
}
|
|
7334
|
-
return Promise.resolve(ctx.errored(
|
|
7552
|
+
return Promise.resolve(ctx.errored(errors9.wrap(ErrConflict, "cannot submit a different pci payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
|
|
7335
7553
|
}
|
|
7336
7554
|
ctx.logger.debug({ pciId }, "pci submit");
|
|
7337
7555
|
submitKey = key;
|
|
@@ -7344,7 +7562,7 @@ function pciInteractionState(ctx, body, stimulus, interaction) {
|
|
|
7344
7562
|
function timeout() {
|
|
7345
7563
|
const intent = { kind: "timeout" };
|
|
7346
7564
|
if (submitPending) {
|
|
7347
|
-
return Promise.resolve(ctx.errored(
|
|
7565
|
+
return Promise.resolve(ctx.errored(errors9.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
|
|
7348
7566
|
}
|
|
7349
7567
|
if (timeoutPending) {
|
|
7350
7568
|
return timeoutPending;
|
|
@@ -7370,7 +7588,7 @@ function pciInteractionState(ctx, body, stimulus, interaction) {
|
|
|
7370
7588
|
}
|
|
7371
7589
|
|
|
7372
7590
|
// src/client/text-entry-state.ts
|
|
7373
|
-
import * as
|
|
7591
|
+
import * as errors10 from "@superbuilders/errors";
|
|
7374
7592
|
function textEntryState(ctx, body, stimulus, interaction) {
|
|
7375
7593
|
let submitPending;
|
|
7376
7594
|
let submitKey;
|
|
@@ -7379,18 +7597,18 @@ function textEntryState(ctx, body, stimulus, interaction) {
|
|
|
7379
7597
|
const submission = { type: "text-entry", value };
|
|
7380
7598
|
const key = JSON.stringify(submission);
|
|
7381
7599
|
if (timeoutPending) {
|
|
7382
|
-
return Promise.resolve(ctx.errored(
|
|
7600
|
+
return Promise.resolve(ctx.errored(errors10.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
|
|
7383
7601
|
}
|
|
7384
7602
|
if (submitPending) {
|
|
7385
7603
|
if (submitKey === key) {
|
|
7386
7604
|
return submitPending;
|
|
7387
7605
|
}
|
|
7388
|
-
return Promise.resolve(ctx.errored(
|
|
7606
|
+
return Promise.resolve(ctx.errored(errors10.wrap(ErrConflict, "cannot submit a different text payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
|
|
7389
7607
|
}
|
|
7390
7608
|
const validation = validateSubmissionForInteraction(interaction, submission);
|
|
7391
7609
|
if (!validation.ok) {
|
|
7392
7610
|
ctx.logger.error({ value, issues: validation.issues }, "text-entry submit invalid");
|
|
7393
|
-
return Promise.resolve(ctx.errored(
|
|
7611
|
+
return Promise.resolve(ctx.errored(errors10.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
|
|
7394
7612
|
}
|
|
7395
7613
|
submitKey = key;
|
|
7396
7614
|
submitPending = ctx.execute({ kind: "interaction", submission }, "interaction").finally(function clearPending() {
|
|
@@ -7402,7 +7620,7 @@ function textEntryState(ctx, body, stimulus, interaction) {
|
|
|
7402
7620
|
function timeout() {
|
|
7403
7621
|
const intent = { kind: "timeout" };
|
|
7404
7622
|
if (submitPending) {
|
|
7405
|
-
return Promise.resolve(ctx.errored(
|
|
7623
|
+
return Promise.resolve(ctx.errored(errors10.wrap(ErrConflict, "cannot timeout while submission is in flight"), "timeout", intent));
|
|
7406
7624
|
}
|
|
7407
7625
|
if (timeoutPending) {
|
|
7408
7626
|
return timeoutPending;
|
|
@@ -7436,571 +7654,369 @@ var FATAL_SENTINELS = [
|
|
|
7436
7654
|
];
|
|
7437
7655
|
function isFatalError(err) {
|
|
7438
7656
|
for (const sentinel of FATAL_SENTINELS) {
|
|
7439
|
-
if (
|
|
7657
|
+
if (errors11.is(err, sentinel)) {
|
|
7440
7658
|
return true;
|
|
7441
7659
|
}
|
|
7442
7660
|
}
|
|
7443
7661
|
return false;
|
|
7444
7662
|
}
|
|
7445
|
-
function isRetriableError(err) {
|
|
7446
|
-
return !
|
|
7447
|
-
}
|
|
7448
|
-
function makeSession(sc) {
|
|
7449
|
-
const logger = sc.logger;
|
|
7450
|
-
function resolve(result) {
|
|
7451
|
-
switch (result.outcome) {
|
|
7452
|
-
case "advanced":
|
|
7453
|
-
return fromAdvanced(result.frame.body, result.frame.stimulus, result.frame.interaction);
|
|
7454
|
-
case "submitted": {
|
|
7455
|
-
const interaction = result.frame.interaction;
|
|
7456
|
-
if (interaction === null) {
|
|
7457
|
-
logger.error("submitted result without interaction");
|
|
7458
|
-
return {
|
|
7459
|
-
phase: "fatal",
|
|
7460
|
-
error:
|
|
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;
|
|
7726
|
-
}
|
|
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;
|
|
7663
|
+
function isRetriableError(err) {
|
|
7664
|
+
return !errors11.is(err, ErrInvalidSubmission);
|
|
7665
|
+
}
|
|
7666
|
+
function makeSession(sc) {
|
|
7667
|
+
const logger = sc.logger;
|
|
7668
|
+
function resolve(result) {
|
|
7669
|
+
switch (result.outcome) {
|
|
7670
|
+
case "advanced":
|
|
7671
|
+
return fromAdvanced(result.frame.body, result.frame.stimulus, result.frame.interaction);
|
|
7672
|
+
case "submitted": {
|
|
7673
|
+
const interaction = result.frame.interaction;
|
|
7674
|
+
if (interaction === null) {
|
|
7675
|
+
logger.error("submitted result without interaction");
|
|
7676
|
+
return {
|
|
7677
|
+
phase: "fatal",
|
|
7678
|
+
error: errors11.wrap(ErrBadRequest, "submitted result missing interaction"),
|
|
7679
|
+
retriable: false,
|
|
7680
|
+
toJSON: poisonToJSON
|
|
7681
|
+
};
|
|
7737
7682
|
}
|
|
7738
|
-
if (
|
|
7739
|
-
|
|
7740
|
-
finishWithError(result2.error);
|
|
7741
|
-
return;
|
|
7683
|
+
if (result.assessmentOutcome.value === "revisionRequested") {
|
|
7684
|
+
return fromAdvanced(result.frame.body, result.frame.stimulus, interaction);
|
|
7742
7685
|
}
|
|
7743
|
-
|
|
7686
|
+
return feedbackState(ctx, result.frame.body, result.frame.stimulus, interaction, result.submission, result.assessmentOutcome, result.feedbackContent, result.review);
|
|
7744
7687
|
}
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
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;
|
|
7688
|
+
case "completed":
|
|
7689
|
+
return { phase: "completed", toJSON: poisonToJSON };
|
|
7690
|
+
}
|
|
7756
7691
|
}
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
7763
|
-
|
|
7692
|
+
function errored(error, failedPhase, intent) {
|
|
7693
|
+
let pending;
|
|
7694
|
+
const retriable = isRetriableError(error);
|
|
7695
|
+
if (!retriable) {
|
|
7696
|
+
return {
|
|
7697
|
+
phase: "errored",
|
|
7698
|
+
error,
|
|
7699
|
+
retriable: false,
|
|
7700
|
+
toJSON: poisonToJSON
|
|
7701
|
+
};
|
|
7702
|
+
}
|
|
7703
|
+
const state = {
|
|
7704
|
+
phase: "errored",
|
|
7705
|
+
error,
|
|
7706
|
+
retriable: true,
|
|
7707
|
+
retry: function retry() {
|
|
7708
|
+
if (pending) {
|
|
7709
|
+
return pending;
|
|
7710
|
+
}
|
|
7711
|
+
logger.debug({ failedPhase }, "retrying from errored state");
|
|
7712
|
+
pending = execute(intent, failedPhase);
|
|
7713
|
+
return pending;
|
|
7714
|
+
},
|
|
7715
|
+
toJSON: poisonToJSON
|
|
7716
|
+
};
|
|
7717
|
+
return state;
|
|
7764
7718
|
}
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7719
|
+
async function execute(intent, phase) {
|
|
7720
|
+
logger.debug({
|
|
7721
|
+
phase,
|
|
7722
|
+
intentKind: intent.kind,
|
|
7723
|
+
subject: sc.subject
|
|
7724
|
+
}, "session execute");
|
|
7725
|
+
const body = {
|
|
7726
|
+
subject: sc.subject,
|
|
7727
|
+
intent
|
|
7728
|
+
};
|
|
7729
|
+
const result = await sc.transport(body);
|
|
7730
|
+
if (!result.ok) {
|
|
7731
|
+
if ((errors11.is(result.error, ErrTokenExpired) || errors11.is(result.error, ErrInvalidAccessToken)) && sc.reauthenticate !== null) {
|
|
7732
|
+
logger.warn({ phase, intentKind: intent.kind }, "session token rejected");
|
|
7733
|
+
return sc.reauthenticate(result.error);
|
|
7734
|
+
}
|
|
7735
|
+
if (isFatalError(result.error)) {
|
|
7736
|
+
logger.error({
|
|
7737
|
+
error: result.error,
|
|
7738
|
+
phase,
|
|
7739
|
+
intentKind: intent.kind
|
|
7740
|
+
}, "fatal transport error");
|
|
7741
|
+
return {
|
|
7742
|
+
phase: "fatal",
|
|
7743
|
+
error: result.error,
|
|
7744
|
+
retriable: false,
|
|
7745
|
+
toJSON: poisonToJSON
|
|
7746
|
+
};
|
|
7747
|
+
}
|
|
7748
|
+
logger.warn({
|
|
7749
|
+
error: result.error,
|
|
7750
|
+
phase,
|
|
7751
|
+
intentKind: intent.kind
|
|
7752
|
+
}, "retriable transport error");
|
|
7753
|
+
return errored(result.error, phase, intent);
|
|
7754
|
+
}
|
|
7755
|
+
const next = resolve(result.data);
|
|
7756
|
+
logger.debug({
|
|
7757
|
+
phase,
|
|
7758
|
+
intentKind: intent.kind,
|
|
7759
|
+
outcome: result.data.outcome,
|
|
7760
|
+
nextPhase: next.phase
|
|
7761
|
+
}, "session execute resolved");
|
|
7762
|
+
return next;
|
|
7778
7763
|
}
|
|
7779
|
-
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
return normalized;
|
|
7764
|
+
function isPciSupported(pciId) {
|
|
7765
|
+
for (const id of sc.supportedPcis) {
|
|
7766
|
+
if (id === pciId) {
|
|
7767
|
+
return true;
|
|
7768
|
+
}
|
|
7769
|
+
}
|
|
7770
|
+
return false;
|
|
7787
7771
|
}
|
|
7788
|
-
|
|
7789
|
-
|
|
7772
|
+
function fromAdvanced(body, stimulus, interaction) {
|
|
7773
|
+
if (interaction === null) {
|
|
7774
|
+
return observationState(ctx, body, stimulus);
|
|
7775
|
+
}
|
|
7776
|
+
if (interaction.type === "portable-custom") {
|
|
7777
|
+
if (!isPciSupported(interaction.pciId)) {
|
|
7778
|
+
logger.error({ pciId: interaction.pciId }, "unsupported pci in frame");
|
|
7779
|
+
return {
|
|
7780
|
+
phase: "fatal",
|
|
7781
|
+
error: errors11.wrap(ErrUnsupportedPci, `pci '${interaction.pciId}'`),
|
|
7782
|
+
retriable: false,
|
|
7783
|
+
toJSON: poisonToJSON
|
|
7784
|
+
};
|
|
7785
|
+
}
|
|
7786
|
+
}
|
|
7787
|
+
return pendingInteractionState(body, stimulus, interaction);
|
|
7790
7788
|
}
|
|
7791
|
-
|
|
7792
|
-
|
|
7789
|
+
function extendedTextInteractionState(body, stimulus, interaction) {
|
|
7790
|
+
if (!("cardinality" in interaction)) {
|
|
7791
|
+
logger.error("extended-text interaction is unsupported");
|
|
7792
|
+
return {
|
|
7793
|
+
phase: "fatal",
|
|
7794
|
+
error: errors11.wrap(ErrBadRequest, "extended-text interaction unsupported"),
|
|
7795
|
+
retriable: false,
|
|
7796
|
+
toJSON: poisonToJSON
|
|
7797
|
+
};
|
|
7798
|
+
}
|
|
7799
|
+
const extendedInteraction = interaction;
|
|
7800
|
+
return extendedTextState(ctx, body, stimulus, extendedInteraction);
|
|
7793
7801
|
}
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
|
|
7801
|
-
|
|
7802
|
-
|
|
7802
|
+
function pendingInteractionState(body, stimulus, interaction) {
|
|
7803
|
+
switch (interaction.type) {
|
|
7804
|
+
case "choice":
|
|
7805
|
+
return choiceState(ctx, body, stimulus, interaction, interaction.options, interaction.maxChoices, interaction.minChoices);
|
|
7806
|
+
case "text-entry":
|
|
7807
|
+
return textEntryState(ctx, body, stimulus, interaction);
|
|
7808
|
+
case "extended-text":
|
|
7809
|
+
return extendedTextInteractionState(body, stimulus, interaction);
|
|
7810
|
+
case "order":
|
|
7811
|
+
return orderState(ctx, body, stimulus, interaction);
|
|
7812
|
+
case "match":
|
|
7813
|
+
return matchState(ctx, body, stimulus, interaction);
|
|
7814
|
+
case "portable-custom":
|
|
7815
|
+
return pciInteractionState(ctx, body, stimulus, interaction);
|
|
7816
|
+
}
|
|
7803
7817
|
}
|
|
7804
|
-
|
|
7818
|
+
const ctx = { logger, execute, errored };
|
|
7819
|
+
return { execute };
|
|
7805
7820
|
}
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7821
|
+
|
|
7822
|
+
// src/client/transport.ts
|
|
7823
|
+
import * as errors12 from "@superbuilders/errors";
|
|
7824
|
+
|
|
7825
|
+
// src/version.ts
|
|
7826
|
+
var SDK_VERSION = "4.1.0";
|
|
7827
|
+
var NPM_PACKAGE_URL = "https://www.npmjs.com/package/@superbuilders/primer-tives";
|
|
7828
|
+
|
|
7829
|
+
// src/client/transport.ts
|
|
7830
|
+
var ADVANCE_PATH = "/api/v0/advance";
|
|
7831
|
+
function readStringField(value, key) {
|
|
7832
|
+
if (!(key in value)) {
|
|
7833
|
+
return;
|
|
7809
7834
|
}
|
|
7810
|
-
const
|
|
7811
|
-
if (typeof
|
|
7812
|
-
return
|
|
7835
|
+
const v = Reflect.get(value, key);
|
|
7836
|
+
if (typeof v !== "string") {
|
|
7837
|
+
return;
|
|
7813
7838
|
}
|
|
7814
|
-
return
|
|
7839
|
+
return v;
|
|
7815
7840
|
}
|
|
7816
|
-
function
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
if (payloadSegment === undefined || payloadSegment.length === 0) {
|
|
7820
|
-
logger.error("access token payload missing");
|
|
7821
|
-
throw errors11.wrap(ErrMalformedAccessToken, "payload missing");
|
|
7841
|
+
function parseAdvanceErrorBody(body) {
|
|
7842
|
+
if (body.length === 0) {
|
|
7843
|
+
return null;
|
|
7822
7844
|
}
|
|
7823
|
-
const
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
7845
|
+
const parsed = errors12.trySync(function parseJson() {
|
|
7846
|
+
return JSON.parse(body);
|
|
7847
|
+
});
|
|
7848
|
+
if (parsed.error) {
|
|
7849
|
+
return null;
|
|
7828
7850
|
}
|
|
7829
|
-
const
|
|
7830
|
-
if (
|
|
7831
|
-
|
|
7832
|
-
throw ErrTokenExpired;
|
|
7851
|
+
const raw = parsed.data;
|
|
7852
|
+
if (typeof raw !== "object" || raw === null) {
|
|
7853
|
+
return null;
|
|
7833
7854
|
}
|
|
7834
|
-
}
|
|
7835
|
-
|
|
7836
|
-
if (
|
|
7837
|
-
|
|
7838
|
-
throw errors11.wrap(ErrMalformedAccessToken, `token must start with '${ACCESS_TOKEN_PREFIX}' and contain two dots`);
|
|
7855
|
+
const result = {};
|
|
7856
|
+
const error = readStringField(raw, "error");
|
|
7857
|
+
if (error !== undefined) {
|
|
7858
|
+
result.error = error;
|
|
7839
7859
|
}
|
|
7840
|
-
|
|
7841
|
-
|
|
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;
|
|
7860
|
+
const detail = readStringField(raw, "detail");
|
|
7861
|
+
if (detail !== undefined) {
|
|
7862
|
+
result.detail = detail;
|
|
7853
7863
|
}
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
const token = storage.getItem(managedAuthAccessTokenStorageKey(publishableKey));
|
|
7858
|
-
if (token === null || token.length === 0) {
|
|
7859
|
-
return null;
|
|
7864
|
+
const minimumSdkVersion = readStringField(raw, "minimumSdkVersion");
|
|
7865
|
+
if (minimumSdkVersion !== undefined) {
|
|
7866
|
+
result.minimumSdkVersion = minimumSdkVersion;
|
|
7860
7867
|
}
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
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 };
|
|
7868
|
+
const receivedSdkVersion = readStringField(raw, "receivedSdkVersion");
|
|
7869
|
+
if (receivedSdkVersion !== undefined) {
|
|
7870
|
+
result.receivedSdkVersion = receivedSdkVersion;
|
|
7877
7871
|
}
|
|
7878
|
-
|
|
7872
|
+
const upgradeUrl = readStringField(raw, "upgradeUrl");
|
|
7873
|
+
if (upgradeUrl !== undefined) {
|
|
7874
|
+
result.upgradeUrl = upgradeUrl;
|
|
7875
|
+
}
|
|
7876
|
+
return result;
|
|
7879
7877
|
}
|
|
7880
|
-
function
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
|
|
7884
|
-
|
|
7885
|
-
|
|
7878
|
+
function httpSentinel(status, body) {
|
|
7879
|
+
if (status === 400) {
|
|
7880
|
+
const parsed = parseAdvanceErrorBody(body);
|
|
7881
|
+
if (parsed?.error === "sdk_upgrade_required") {
|
|
7882
|
+
return ErrSdkUpgradeRequired;
|
|
7883
|
+
}
|
|
7884
|
+
return ErrBadRequest;
|
|
7886
7885
|
}
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
|
|
7891
|
-
clearCachedAccessToken: function clearCachedAccessToken() {
|
|
7892
|
-
clearManagedAuthAccessToken(storage, options.publishableKey);
|
|
7886
|
+
if (status === 401) {
|
|
7887
|
+
const parsed = parseAdvanceErrorBody(body);
|
|
7888
|
+
if (parsed?.detail === "token_expired") {
|
|
7889
|
+
return ErrTokenExpired;
|
|
7893
7890
|
}
|
|
7894
|
-
|
|
7895
|
-
}
|
|
7896
|
-
function resolveStoredAccessToken(storage, options) {
|
|
7897
|
-
const stored = loadManagedAuthAccessToken(storage, options.publishableKey);
|
|
7898
|
-
if (stored === null) {
|
|
7899
|
-
return { kind: "missing" };
|
|
7891
|
+
return ErrInvalidAccessToken;
|
|
7900
7892
|
}
|
|
7901
|
-
|
|
7902
|
-
return
|
|
7903
|
-
});
|
|
7904
|
-
if (result.error) {
|
|
7905
|
-
clearManagedAuthAccessToken(storage, options.publishableKey);
|
|
7906
|
-
return { kind: "missing" };
|
|
7893
|
+
if (status === 403) {
|
|
7894
|
+
return ErrForbidden;
|
|
7907
7895
|
}
|
|
7908
|
-
|
|
7909
|
-
|
|
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);
|
|
7896
|
+
if (status === 404) {
|
|
7897
|
+
return ErrNotFound;
|
|
7919
7898
|
}
|
|
7920
|
-
|
|
7921
|
-
return
|
|
7922
|
-
});
|
|
7923
|
-
if (storageResult.error) {
|
|
7924
|
-
return { kind: "auth-unavailable", error: storageResult.error };
|
|
7899
|
+
if (status === 409) {
|
|
7900
|
+
return ErrConflict;
|
|
7925
7901
|
}
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
function authFailureResult(error) {
|
|
7929
|
-
if (errors12.is(error, ErrAuthConfigInvalid)) {
|
|
7930
|
-
return { kind: "auth-config-invalid", error };
|
|
7902
|
+
if (status === 429) {
|
|
7903
|
+
return ErrRateLimited;
|
|
7931
7904
|
}
|
|
7932
|
-
if (
|
|
7933
|
-
return
|
|
7905
|
+
if (status === 502 || status === 503 || status === 504) {
|
|
7906
|
+
return ErrServiceUnavailable;
|
|
7934
7907
|
}
|
|
7935
|
-
return
|
|
7908
|
+
return ErrServerError;
|
|
7936
7909
|
}
|
|
7937
|
-
|
|
7938
|
-
const
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
return
|
|
7910
|
+
function buildSdkUpgradeRequiredError(sentinel, text, logger) {
|
|
7911
|
+
const parsed = parseAdvanceErrorBody(text);
|
|
7912
|
+
if (parsed === null) {
|
|
7913
|
+
logger.error({
|
|
7914
|
+
receivedSdkVersion: null,
|
|
7915
|
+
minimumSdkVersion: "<unknown>",
|
|
7916
|
+
upgradeUrl: NPM_PACKAGE_URL
|
|
7917
|
+
}, "sdk upgrade required");
|
|
7918
|
+
const message2 = `<missing> < <unknown>; bump @superbuilders/primer-tives at ${NPM_PACKAGE_URL}`;
|
|
7919
|
+
return errors12.wrap(sentinel, message2);
|
|
7947
7920
|
}
|
|
7948
|
-
const
|
|
7949
|
-
const
|
|
7950
|
-
|
|
7951
|
-
|
|
7952
|
-
|
|
7953
|
-
|
|
7954
|
-
|
|
7955
|
-
|
|
7956
|
-
|
|
7957
|
-
|
|
7921
|
+
const minimumSdkVersion = parsed.minimumSdkVersion === undefined ? "<unknown>" : parsed.minimumSdkVersion;
|
|
7922
|
+
const receivedSdkVersion = parsed.receivedSdkVersion === undefined ? null : parsed.receivedSdkVersion;
|
|
7923
|
+
const receivedDisplay = receivedSdkVersion === null ? "<missing>" : receivedSdkVersion;
|
|
7924
|
+
const upgradeUrl = parsed.upgradeUrl === undefined ? NPM_PACKAGE_URL : parsed.upgradeUrl;
|
|
7925
|
+
logger.error({
|
|
7926
|
+
receivedSdkVersion,
|
|
7927
|
+
minimumSdkVersion,
|
|
7928
|
+
upgradeUrl
|
|
7929
|
+
}, "sdk upgrade required");
|
|
7930
|
+
const message = `${receivedDisplay} < ${minimumSdkVersion}; bump @superbuilders/primer-tives at ${upgradeUrl}`;
|
|
7931
|
+
return errors12.wrap(sentinel, message);
|
|
7932
|
+
}
|
|
7933
|
+
function isAbortError(err) {
|
|
7934
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
7935
|
+
return true;
|
|
7958
7936
|
}
|
|
7959
|
-
|
|
7937
|
+
if (err instanceof DOMException && err.name === "TimeoutError") {
|
|
7938
|
+
return true;
|
|
7939
|
+
}
|
|
7940
|
+
return false;
|
|
7960
7941
|
}
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
function
|
|
7966
|
-
if (
|
|
7967
|
-
return
|
|
7942
|
+
function createTransport(tc) {
|
|
7943
|
+
const fetchFn = tc.fetch ? tc.fetch : globalThis.fetch;
|
|
7944
|
+
const logger = tc.logger;
|
|
7945
|
+
const advanceUrl = new URL(ADVANCE_PATH, tc.origin).toString();
|
|
7946
|
+
function transportSignal() {
|
|
7947
|
+
if (tc.abort) {
|
|
7948
|
+
return tc.abort.signal;
|
|
7968
7949
|
}
|
|
7969
|
-
|
|
7970
|
-
|
|
7950
|
+
return;
|
|
7951
|
+
}
|
|
7952
|
+
async function transport(body) {
|
|
7953
|
+
logger.debug({
|
|
7954
|
+
intentKind: body.intent.kind,
|
|
7955
|
+
subject: body.subject
|
|
7956
|
+
}, "transport request");
|
|
7957
|
+
const fetchResult = await fetchFn(advanceUrl, {
|
|
7958
|
+
method: "POST",
|
|
7959
|
+
mode: "cors",
|
|
7960
|
+
credentials: "omit",
|
|
7961
|
+
headers: {
|
|
7962
|
+
"Content-Type": "application/json",
|
|
7963
|
+
Authorization: `Bearer ${tc.accessToken.value}`,
|
|
7964
|
+
"X-Primer-Publishable-Key": tc.publishableKey,
|
|
7965
|
+
"X-Primer-SDK-Version": SDK_VERSION
|
|
7966
|
+
},
|
|
7967
|
+
body: JSON.stringify(body),
|
|
7968
|
+
signal: transportSignal()
|
|
7969
|
+
}).then(function ok(r) {
|
|
7970
|
+
return { ok: true, response: r };
|
|
7971
|
+
}, function fail(err) {
|
|
7972
|
+
return { ok: false, error: err };
|
|
7971
7973
|
});
|
|
7972
|
-
|
|
7974
|
+
if (!fetchResult.ok) {
|
|
7975
|
+
if (isAbortError(fetchResult.error)) {
|
|
7976
|
+
logger.error({
|
|
7977
|
+
intentKind: body.intent.kind
|
|
7978
|
+
}, "transport timeout");
|
|
7979
|
+
return { ok: false, error: errors12.wrap(ErrTimeout, fetchResult.error.message) };
|
|
7980
|
+
}
|
|
7981
|
+
logger.error({
|
|
7982
|
+
error: fetchResult.error
|
|
7983
|
+
}, "transport network error");
|
|
7984
|
+
return { ok: false, error: errors12.wrap(ErrNetwork, fetchResult.error.message) };
|
|
7985
|
+
}
|
|
7986
|
+
const res = fetchResult.response;
|
|
7987
|
+
if (!res.ok) {
|
|
7988
|
+
const text = await res.text().catch(function fallback() {
|
|
7989
|
+
return "";
|
|
7990
|
+
});
|
|
7991
|
+
const sentinel = httpSentinel(res.status, text);
|
|
7992
|
+
if (errors12.is(sentinel, ErrSdkUpgradeRequired)) {
|
|
7993
|
+
return { ok: false, error: buildSdkUpgradeRequiredError(sentinel, text, logger) };
|
|
7994
|
+
}
|
|
7995
|
+
logger.error({
|
|
7996
|
+
status: res.status,
|
|
7997
|
+
body: text,
|
|
7998
|
+
intentKind: body.intent.kind,
|
|
7999
|
+
subject: body.subject
|
|
8000
|
+
}, "transport http error");
|
|
8001
|
+
return { ok: false, error: errors12.wrap(sentinel, text) };
|
|
8002
|
+
}
|
|
8003
|
+
const jsonResult = await res.json().then(function ok(data) {
|
|
8004
|
+
return { ok: true, data };
|
|
8005
|
+
}, function fail(err) {
|
|
8006
|
+
return { ok: false, error: err };
|
|
8007
|
+
});
|
|
8008
|
+
if (!jsonResult.ok) {
|
|
8009
|
+
logger.error({
|
|
8010
|
+
error: jsonResult.error
|
|
8011
|
+
}, "transport json parse failed");
|
|
8012
|
+
return { ok: false, error: errors12.wrap(ErrJsonParse, jsonResult.error.message) };
|
|
8013
|
+
}
|
|
8014
|
+
logger.debug({
|
|
8015
|
+
intentKind: body.intent.kind
|
|
8016
|
+
}, "transport success");
|
|
8017
|
+
return { ok: true, data: jsonResult.data };
|
|
7973
8018
|
}
|
|
7974
|
-
return
|
|
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
|
-
};
|
|
8019
|
+
return transport;
|
|
8004
8020
|
}
|
|
8005
8021
|
|
|
8006
8022
|
// src/client/start.ts
|
|
@@ -8123,4 +8139,4 @@ export {
|
|
|
8123
8139
|
start
|
|
8124
8140
|
};
|
|
8125
8141
|
|
|
8126
|
-
//# debugId=
|
|
8142
|
+
//# debugId=8BB175F09E303E4A64756E2164756E21
|