@superbuilders/primer-tives 4.0.3 → 4.0.5
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 +219 -102
- package/dist/client/auth/browser.d.ts +4 -14
- package/dist/client/auth/browser.d.ts.map +1 -1
- package/dist/client/auth/hosted-popup.d.ts +0 -2
- package/dist/client/auth/hosted-popup.d.ts.map +1 -1
- package/dist/client/auth/provider.d.ts +18 -8
- package/dist/client/auth/provider.d.ts.map +1 -1
- package/dist/client/auth/storage.d.ts +7 -9
- package/dist/client/auth/storage.d.ts.map +1 -1
- package/dist/client/auth-state.d.ts +14 -0
- package/dist/client/auth-state.d.ts.map +1 -0
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +154 -159
- package/dist/client/index.js.map +11 -11
- package/dist/client/session.d.ts +1 -1
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/start.d.ts +10 -4
- package/dist/client/start.d.ts.map +1 -1
- package/dist/client/types.d.ts +29 -8
- package/dist/client/types.d.ts.map +1 -1
- package/dist/contracts/validation.d.ts +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/dist/client/auth/callback.d.ts +0 -10
- package/dist/client/auth/callback.d.ts.map +0 -1
- package/dist/client/unauthenticated-state.d.ts +0 -10
- package/dist/client/unauthenticated-state.d.ts.map +0 -1
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
import type { PrimerLogger } from "../../logger";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
readonly storage?: Storage;
|
|
5
|
-
readonly currentUrl?: string;
|
|
6
|
-
readonly popupTarget?: string;
|
|
7
|
-
readonly popupFeatures?: string;
|
|
8
|
-
readonly popupTimeoutMs?: number;
|
|
9
|
-
};
|
|
10
|
-
declare function browserStorage(options: HostedAuthOptions | undefined, logger: PrimerLogger): Storage;
|
|
11
|
-
declare function currentUrl(options: HostedAuthOptions | undefined, logger: PrimerLogger): URL;
|
|
12
|
-
declare function redirectUri(options: HostedAuthOptions | undefined, url: URL, logger: PrimerLogger): string;
|
|
2
|
+
declare function currentUrl(logger: PrimerLogger): URL;
|
|
3
|
+
declare function redirectUri(url: URL): string;
|
|
13
4
|
declare function randomClientState(logger: PrimerLogger): string;
|
|
14
|
-
declare function openAuthPopup(url: string,
|
|
15
|
-
export {
|
|
16
|
-
export type { HostedAuthOptions };
|
|
5
|
+
declare function openAuthPopup(url: string, logger: PrimerLogger): Window;
|
|
6
|
+
export { currentUrl, openAuthPopup, randomClientState, redirectUri };
|
|
17
7
|
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../src/client/auth/browser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../src/client/auth/browser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AAKtE,iBAAS,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,GAAG,CAM7C;AAED,iBAAS,WAAW,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAErC;AAED,iBAAS,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAYvD;AAED,iBAAS,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CAWhE;AAED,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,iBAAiB,EAAE,WAAW,EAAE,CAAA"}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import type { PrimerLogger } from "../../logger";
|
|
2
|
-
import { type HostedAuthOptions } from "./browser";
|
|
3
2
|
type HostedPopupConfig = {
|
|
4
3
|
readonly origin: string;
|
|
5
4
|
readonly publishableKey: string;
|
|
6
5
|
readonly currentUrl: URL;
|
|
7
6
|
readonly clientState: string;
|
|
8
|
-
readonly options?: HostedAuthOptions;
|
|
9
7
|
readonly logger: PrimerLogger;
|
|
10
8
|
};
|
|
11
9
|
declare function beginHostedPopup(config: HostedPopupConfig): Promise<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hosted-popup.d.ts","sourceRoot":"","sources":["../../../src/client/auth/hosted-popup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hosted-popup.d.ts","sourceRoot":"","sources":["../../../src/client/auth/hosted-popup.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AAOtE,KAAK,iBAAiB,GAAG;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAA;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;CAC7B,CAAA;AAgJD,iBAAe,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAU1E;AAED,OAAO,EAAE,gBAAgB,EAAE,CAAA;AAC3B,YAAY,EAAE,iBAAiB,EAAE,CAAA"}
|
|
@@ -1,28 +1,38 @@
|
|
|
1
1
|
import type { PrimerLogger } from "../../logger";
|
|
2
|
-
import { type HostedAuthOptions } from "./browser";
|
|
3
2
|
import { type ResolvedAccessToken } from "./access-token";
|
|
4
3
|
type AccessTokenResolverOptions = {
|
|
5
4
|
readonly origin: string;
|
|
6
5
|
readonly publishableKey: string;
|
|
7
6
|
readonly accessToken?: string;
|
|
8
|
-
readonly hostedAuth?: HostedAuthOptions;
|
|
9
7
|
readonly logger: PrimerLogger;
|
|
10
8
|
};
|
|
11
9
|
type ResolvedAccessTokenResult = {
|
|
12
10
|
readonly kind: "resolved";
|
|
13
11
|
readonly accessToken: ResolvedAccessToken;
|
|
14
|
-
readonly
|
|
12
|
+
readonly clearCachedAccessToken?: () => void;
|
|
15
13
|
};
|
|
16
|
-
type
|
|
17
|
-
readonly kind: "
|
|
18
|
-
readonly error: Error | null;
|
|
14
|
+
type MissingAccessTokenResult = {
|
|
15
|
+
readonly kind: "missing";
|
|
19
16
|
};
|
|
20
17
|
type FatalAccessTokenResult = {
|
|
21
18
|
readonly kind: "fatal";
|
|
22
19
|
readonly error: Error;
|
|
23
20
|
};
|
|
24
|
-
type
|
|
25
|
-
|
|
21
|
+
type AuthUnavailableResult = {
|
|
22
|
+
readonly kind: "auth-unavailable";
|
|
23
|
+
readonly error: Error;
|
|
24
|
+
};
|
|
25
|
+
type ExistingAccessTokenResult = ResolvedAccessTokenResult | MissingAccessTokenResult | AuthUnavailableResult | FatalAccessTokenResult;
|
|
26
|
+
type HostedLoginResult = ResolvedAccessTokenResult | {
|
|
27
|
+
readonly kind: "sign-in-failed";
|
|
28
|
+
readonly error: Error;
|
|
29
|
+
} | {
|
|
30
|
+
readonly kind: "auth-unavailable";
|
|
31
|
+
readonly error: Error;
|
|
32
|
+
} | {
|
|
33
|
+
readonly kind: "auth-config-invalid";
|
|
34
|
+
readonly error: Error;
|
|
35
|
+
};
|
|
26
36
|
declare function resolveExistingAccessToken(options: AccessTokenResolverOptions): ExistingAccessTokenResult;
|
|
27
37
|
declare function beginHostedLogin(options: AccessTokenResolverOptions): Promise<HostedLoginResult>;
|
|
28
38
|
export { beginHostedLogin, resolveExistingAccessToken };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/client/auth/provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/client/auth/provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AAGtE,OAAO,EAEN,KAAK,mBAAmB,EACxB,MAAM,sDAAsD,CAAA;AAS7D,KAAK,0BAA0B,GAAG;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;CAC7B,CAAA;AAED,KAAK,yBAAyB,GAAG;IAChC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAA;IACzC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAA;CAC5C,CAAA;AAED,KAAK,wBAAwB,GAAG;IAC/B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CACxB,CAAA;AAED,KAAK,sBAAsB,GAAG;IAC7B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CACrB,CAAA;AAED,KAAK,qBAAqB,GAAG;IAC5B,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAA;IACjC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CACrB,CAAA;AAED,KAAK,yBAAyB,GAC3B,yBAAyB,GACzB,wBAAwB,GACxB,qBAAqB,GACrB,sBAAsB,CAAA;AAEzB,KAAK,iBAAiB,GACnB,yBAAyB,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAC5D;IAAE,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAA;AA4DlE,iBAAS,0BAA0B,CAClC,OAAO,EAAE,0BAA0B,GACjC,yBAAyB,CAW3B;AAYD,iBAAe,gBAAgB,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAyB/F;AAED,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,CAAA;AACvD,YAAY,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,CAAA"}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
declare function
|
|
3
|
-
declare function
|
|
4
|
-
declare function
|
|
5
|
-
declare function
|
|
6
|
-
declare function
|
|
7
|
-
|
|
8
|
-
declare function clearAuthState(storage: Storage, publishableKey: string): void;
|
|
9
|
-
export { accessTokenStorageKey, authStateStorageKey, clearAuthState, clearStoredAccessToken, loadAuthState, loadStoredAccessToken, storeAccessToken, storeAuthState };
|
|
1
|
+
import type { PrimerLogger } from "../../logger";
|
|
2
|
+
declare function managedAuthAccessTokenStorageKey(publishableKey: string): string;
|
|
3
|
+
declare function managedAuthStorage(logger: PrimerLogger): Storage;
|
|
4
|
+
declare function loadManagedAuthAccessToken(storage: Storage, publishableKey: string): string | null;
|
|
5
|
+
declare function storeManagedAuthAccessToken(storage: Storage, publishableKey: string, accessToken: string): void;
|
|
6
|
+
declare function clearManagedAuthAccessToken(storage: Storage, publishableKey: string): void;
|
|
7
|
+
export { clearManagedAuthAccessToken, loadManagedAuthAccessToken, managedAuthAccessTokenStorageKey, managedAuthStorage, storeManagedAuthAccessToken };
|
|
10
8
|
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/client/auth/storage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/client/auth/storage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AAItE,iBAAS,gCAAgC,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAExE;AAED,iBAAS,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAMzD;AAED,iBAAS,0BAA0B,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM3F;AAED,iBAAS,2BAA2B,CACnC,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,MAAM,GACjB,IAAI,CAEN;AAED,iBAAS,2BAA2B,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,CAEnF;AAED,OAAO,EACN,2BAA2B,EAC3B,0BAA0B,EAC1B,gCAAgC,EAChC,kBAAkB,EAClB,2BAA2B,EAC3B,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PciId } from "../contracts/pci";
|
|
2
|
+
import type { AuthUnavailableState, AuthConfigInvalidState, ManagedStartState, SignInFailedState, SignInRequiredState } from "./types";
|
|
3
|
+
type SignInStateConfig<Pcis extends PciId = PciId> = {
|
|
4
|
+
login(): Promise<ManagedStartState<Pcis>>;
|
|
5
|
+
};
|
|
6
|
+
declare function signInRequiredState<Pcis extends PciId>(config: SignInStateConfig<Pcis>): SignInRequiredState<Pcis>;
|
|
7
|
+
declare function signInFailedState<Pcis extends PciId>(config: SignInStateConfig<Pcis> & {
|
|
8
|
+
readonly error: Error;
|
|
9
|
+
}): SignInFailedState<Pcis>;
|
|
10
|
+
declare function authUnavailableState(error: Error): AuthUnavailableState;
|
|
11
|
+
declare function authConfigInvalidState(error: Error): AuthConfigInvalidState;
|
|
12
|
+
export { authConfigInvalidState, authUnavailableState, signInFailedState, signInRequiredState };
|
|
13
|
+
export type { SignInStateConfig };
|
|
14
|
+
//# sourceMappingURL=auth-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-state.d.ts","sourceRoot":"","sources":["../../src/client/auth-state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAA;AACtE,OAAO,KAAK,EACX,oBAAoB,EACpB,sBAAsB,EACtB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,MAAM,0CAA0C,CAAA;AAEjD,KAAK,iBAAiB,CAAC,IAAI,SAAS,KAAK,GAAG,KAAK,IAAI;IACpD,KAAK,IAAI,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAA;CACzC,CAAA;AAkBD,iBAAS,mBAAmB,CAAC,IAAI,SAAS,KAAK,EAC9C,MAAM,EAAE,iBAAiB,CAAC,IAAI,CAAC,GAC7B,mBAAmB,CAAC,IAAI,CAAC,CAM3B;AAED,iBAAS,iBAAiB,CAAC,IAAI,SAAS,KAAK,EAC5C,MAAM,EAAE,iBAAiB,CAAC,IAAI,CAAC,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GACzD,iBAAiB,CAAC,IAAI,CAAC,CAOzB;AAED,iBAAS,oBAAoB,CAAC,KAAK,EAAE,KAAK,GAAG,oBAAoB,CAMhE;AAED,iBAAS,sBAAsB,CAAC,KAAK,EAAE,KAAK,GAAG,sBAAsB,CAMpE;AAED,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAA;AAC/F,YAAY,EAAE,iBAAiB,EAAE,CAAA"}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { start } from "./start";
|
|
2
|
-
export type { PrimerOptions } from "./start";
|
|
3
|
-
export type { ChoiceState, CompletedState, ErroredState, ExtendedTextMultipleState, ExtendedTextSingleState, ExtendedTextState, FatalState, FeedbackState, InteractionState, MatchState, NonSerializable, ObservationState, OrderState, PciInteractionState, PciPendingRenderProps, PciRenderProps, PciSubmittedRenderProps, PrimerState, TextEntryState,
|
|
2
|
+
export type { PrimerOptions, PrimerOptionsWithAccessToken, PrimerOptionsWithManagedAuth } from "./start";
|
|
3
|
+
export type { ChoiceState, CompletedState, ErroredState, ExtendedTextMultipleState, ExtendedTextSingleState, ExtendedTextState, FatalState, FeedbackState, InteractionState, MatchState, ManagedStartState, NonSerializable, NonRetriableErroredState, ObservationState, OrderState, PciInteractionState, PciPendingRenderProps, PciRenderProps, PciSubmittedRenderProps, PrimerState, RetriableErroredState, RuntimeState, SignInFailedState, SignInRequiredState, TextEntryState, AccessTokenStartState, AuthConfigInvalidState, AuthUnavailableState } from "./types";
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,0CAA0C,CAAA;AAChE,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,0CAA0C,CAAA;AAChE,YAAY,EACX,aAAa,EACb,4BAA4B,EAC5B,4BAA4B,EAC5B,MAAM,0CAA0C,CAAA;AAEjD,YAAY,EACX,WAAW,EACX,cAAc,EACd,YAAY,EACZ,yBAAyB,EACzB,uBAAuB,EACvB,iBAAiB,EACjB,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,eAAe,EACf,wBAAwB,EACxB,gBAAgB,EAChB,UAAU,EACV,mBAAmB,EACnB,qBAAqB,EACrB,cAAc,EACd,uBAAuB,EACvB,WAAW,EACX,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,MAAM,0CAA0C,CAAA"}
|
package/dist/client/index.js
CHANGED
|
@@ -6783,7 +6783,7 @@ function submissionValidationMessage(result) {
|
|
|
6783
6783
|
import * as errors3 from "@superbuilders/errors";
|
|
6784
6784
|
|
|
6785
6785
|
// src/version.ts
|
|
6786
|
-
var SDK_VERSION = "4.0.
|
|
6786
|
+
var SDK_VERSION = "4.0.5";
|
|
6787
6787
|
var NPM_PACKAGE_URL = "https://www.npmjs.com/package/@superbuilders/primer-tives";
|
|
6788
6788
|
|
|
6789
6789
|
// src/client/transport.ts
|
|
@@ -7471,15 +7471,19 @@ function makeSession(sc) {
|
|
|
7471
7471
|
function errored(error, failedPhase, intent) {
|
|
7472
7472
|
let pending;
|
|
7473
7473
|
const retriable = isRetriableError(error);
|
|
7474
|
+
if (!retriable) {
|
|
7475
|
+
return {
|
|
7476
|
+
phase: "errored",
|
|
7477
|
+
error,
|
|
7478
|
+
retriable: false,
|
|
7479
|
+
toJSON: poisonToJSON
|
|
7480
|
+
};
|
|
7481
|
+
}
|
|
7474
7482
|
const state = {
|
|
7475
7483
|
phase: "errored",
|
|
7476
7484
|
error,
|
|
7477
|
-
retriable,
|
|
7485
|
+
retriable: true,
|
|
7478
7486
|
retry: function retry() {
|
|
7479
|
-
if (!retriable) {
|
|
7480
|
-
logger.debug({ failedPhase }, "retry ignored for non-retriable error");
|
|
7481
|
-
return Promise.resolve(state);
|
|
7482
|
-
}
|
|
7483
7487
|
if (pending) {
|
|
7484
7488
|
return pending;
|
|
7485
7489
|
}
|
|
@@ -7503,9 +7507,9 @@ function makeSession(sc) {
|
|
|
7503
7507
|
};
|
|
7504
7508
|
const result = await sc.transport(body);
|
|
7505
7509
|
if (!result.ok) {
|
|
7506
|
-
if (errors10.is(result.error, ErrTokenExpired) && sc.reauthenticate !== null) {
|
|
7507
|
-
logger.warn({ phase, intentKind: intent.kind }, "session token
|
|
7508
|
-
return sc.reauthenticate();
|
|
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);
|
|
7509
7513
|
}
|
|
7510
7514
|
if (isFatalError(result.error)) {
|
|
7511
7515
|
logger.error({
|
|
@@ -7585,38 +7589,16 @@ function makeSession(sc) {
|
|
|
7585
7589
|
import * as errors12 from "@superbuilders/errors";
|
|
7586
7590
|
|
|
7587
7591
|
// src/client/auth/browser.ts
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
}
|
|
7592
|
-
if (typeof globalThis.sessionStorage === "undefined") {
|
|
7593
|
-
logger.error("auth storage unavailable");
|
|
7594
|
-
throw ErrAuthUnavailable;
|
|
7595
|
-
}
|
|
7596
|
-
return globalThis.sessionStorage;
|
|
7597
|
-
}
|
|
7598
|
-
function currentUrl(options, logger) {
|
|
7599
|
-
if (options !== undefined && options.currentUrl !== undefined) {
|
|
7600
|
-
if (!URL.canParse(options.currentUrl)) {
|
|
7601
|
-
logger.error({ currentUrl: options.currentUrl }, "auth current url invalid");
|
|
7602
|
-
throw ErrAuthConfigInvalid;
|
|
7603
|
-
}
|
|
7604
|
-
return new URL(options.currentUrl);
|
|
7605
|
-
}
|
|
7592
|
+
var POPUP_TARGET = "primer-auth";
|
|
7593
|
+
var POPUP_FEATURES = "popup,width=480,height=720";
|
|
7594
|
+
function currentUrl(logger) {
|
|
7606
7595
|
if (typeof globalThis.location === "undefined") {
|
|
7607
7596
|
logger.error("auth location unavailable");
|
|
7608
7597
|
throw ErrAuthUnavailable;
|
|
7609
7598
|
}
|
|
7610
7599
|
return new URL(globalThis.location.href);
|
|
7611
7600
|
}
|
|
7612
|
-
function redirectUri(
|
|
7613
|
-
if (options !== undefined && options.redirectUri !== undefined) {
|
|
7614
|
-
if (!URL.canParse(options.redirectUri)) {
|
|
7615
|
-
logger.error({ redirectUri: options.redirectUri }, "auth redirect uri invalid");
|
|
7616
|
-
throw ErrAuthConfigInvalid;
|
|
7617
|
-
}
|
|
7618
|
-
return options.redirectUri;
|
|
7619
|
-
}
|
|
7601
|
+
function redirectUri(url) {
|
|
7620
7602
|
return `${url.origin}${url.pathname}${url.search}`;
|
|
7621
7603
|
}
|
|
7622
7604
|
function randomClientState(logger) {
|
|
@@ -7632,20 +7614,12 @@ function randomClientState(logger) {
|
|
|
7632
7614
|
}
|
|
7633
7615
|
return result;
|
|
7634
7616
|
}
|
|
7635
|
-
function openAuthPopup(url,
|
|
7617
|
+
function openAuthPopup(url, logger) {
|
|
7636
7618
|
if (typeof globalThis.open === "undefined") {
|
|
7637
7619
|
logger.error("auth popup api unavailable");
|
|
7638
7620
|
throw ErrAuthUnavailable;
|
|
7639
7621
|
}
|
|
7640
|
-
|
|
7641
|
-
if (options !== undefined && options.popupTarget !== undefined) {
|
|
7642
|
-
target = options.popupTarget;
|
|
7643
|
-
}
|
|
7644
|
-
let features = "popup,width=480,height=720";
|
|
7645
|
-
if (options !== undefined && options.popupFeatures !== undefined) {
|
|
7646
|
-
features = options.popupFeatures;
|
|
7647
|
-
}
|
|
7648
|
-
const popup = globalThis.open(url, target, features);
|
|
7622
|
+
const popup = globalThis.open(url, POPUP_TARGET, POPUP_FEATURES);
|
|
7649
7623
|
if (popup === null) {
|
|
7650
7624
|
logger.error("auth popup blocked");
|
|
7651
7625
|
throw ErrAuthPopupBlocked;
|
|
@@ -7657,26 +7631,18 @@ function openAuthPopup(url, options, logger) {
|
|
|
7657
7631
|
var DEFAULT_POPUP_TIMEOUT_MS = 10 * 60 * 1000;
|
|
7658
7632
|
var POPUP_POLL_MS = 250;
|
|
7659
7633
|
var AUTH_MESSAGE_TYPE = "primer-tives.auth.result.v1";
|
|
7660
|
-
var AUTH_RESPONSE_MODE = "web_message";
|
|
7661
7634
|
function hostedAuthUrl(config) {
|
|
7662
7635
|
const logger = config.logger;
|
|
7663
7636
|
if (!URL.canParse(config.origin)) {
|
|
7664
7637
|
logger.error({ origin: config.origin }, "hosted auth origin invalid");
|
|
7665
|
-
throw
|
|
7638
|
+
throw ErrAuthConfigInvalid;
|
|
7666
7639
|
}
|
|
7667
|
-
const authUrl = new URL("/auth/timeback", config.origin);
|
|
7640
|
+
const authUrl = new URL("/api/auth/timeback/start", config.origin);
|
|
7668
7641
|
authUrl.searchParams.set("publishableKey", config.publishableKey);
|
|
7669
|
-
authUrl.searchParams.set("redirectUri", redirectUri(config.
|
|
7642
|
+
authUrl.searchParams.set("redirectUri", redirectUri(config.currentUrl));
|
|
7670
7643
|
authUrl.searchParams.set("state", config.clientState);
|
|
7671
|
-
authUrl.searchParams.set("responseMode", AUTH_RESPONSE_MODE);
|
|
7672
7644
|
return authUrl.toString();
|
|
7673
7645
|
}
|
|
7674
|
-
function popupTimeoutMs(options) {
|
|
7675
|
-
if (options !== undefined && options.popupTimeoutMs !== undefined) {
|
|
7676
|
-
return options.popupTimeoutMs;
|
|
7677
|
-
}
|
|
7678
|
-
return DEFAULT_POPUP_TIMEOUT_MS;
|
|
7679
|
-
}
|
|
7680
7646
|
function isRecord(value) {
|
|
7681
7647
|
return typeof value === "object" && value !== null;
|
|
7682
7648
|
}
|
|
@@ -7750,7 +7716,7 @@ async function waitForPopupMessage(popup, config, expectedOrigin) {
|
|
|
7750
7716
|
const timeoutId = globalThis.setTimeout(function timeout() {
|
|
7751
7717
|
logger.error("hosted auth popup timed out");
|
|
7752
7718
|
finishWithError(ErrAuthCancelled);
|
|
7753
|
-
},
|
|
7719
|
+
}, DEFAULT_POPUP_TIMEOUT_MS);
|
|
7754
7720
|
stack.defer(function clearPopupTimeout() {
|
|
7755
7721
|
globalThis.clearTimeout(timeoutId);
|
|
7756
7722
|
});
|
|
@@ -7794,10 +7760,10 @@ async function beginHostedPopup(config) {
|
|
|
7794
7760
|
const url = hostedAuthUrl(config);
|
|
7795
7761
|
if (!URL.canParse(config.origin)) {
|
|
7796
7762
|
logger.error({ origin: config.origin }, "hosted auth origin invalid");
|
|
7797
|
-
throw
|
|
7763
|
+
throw ErrAuthConfigInvalid;
|
|
7798
7764
|
}
|
|
7799
7765
|
const expectedOrigin = new URL(config.origin).origin;
|
|
7800
|
-
const popup = openAuthPopup(url,
|
|
7766
|
+
const popup = openAuthPopup(url, logger);
|
|
7801
7767
|
return waitForPopupMessage(popup, config, expectedOrigin);
|
|
7802
7768
|
}
|
|
7803
7769
|
|
|
@@ -7876,32 +7842,29 @@ function resolveAccessToken(token, logger) {
|
|
|
7876
7842
|
}
|
|
7877
7843
|
|
|
7878
7844
|
// src/client/auth/storage.ts
|
|
7879
|
-
var
|
|
7880
|
-
|
|
7881
|
-
|
|
7882
|
-
return `${ACCESS_TOKEN_KEY_PREFIX}:${publishableKey}`;
|
|
7845
|
+
var MANAGED_AUTH_ACCESS_TOKEN_KEY_PREFIX = "primer:access-token";
|
|
7846
|
+
function managedAuthAccessTokenStorageKey(publishableKey) {
|
|
7847
|
+
return `${MANAGED_AUTH_ACCESS_TOKEN_KEY_PREFIX}:${publishableKey}`;
|
|
7883
7848
|
}
|
|
7884
|
-
function
|
|
7885
|
-
|
|
7849
|
+
function managedAuthStorage(logger) {
|
|
7850
|
+
if (typeof globalThis.sessionStorage === "undefined") {
|
|
7851
|
+
logger.error("managed auth storage unavailable");
|
|
7852
|
+
throw ErrAuthUnavailable;
|
|
7853
|
+
}
|
|
7854
|
+
return globalThis.sessionStorage;
|
|
7886
7855
|
}
|
|
7887
|
-
function
|
|
7888
|
-
const token = storage.getItem(
|
|
7856
|
+
function loadManagedAuthAccessToken(storage, publishableKey) {
|
|
7857
|
+
const token = storage.getItem(managedAuthAccessTokenStorageKey(publishableKey));
|
|
7889
7858
|
if (token === null || token.length === 0) {
|
|
7890
7859
|
return null;
|
|
7891
7860
|
}
|
|
7892
7861
|
return token;
|
|
7893
7862
|
}
|
|
7894
|
-
function
|
|
7895
|
-
storage.setItem(
|
|
7896
|
-
}
|
|
7897
|
-
function clearStoredAccessToken(storage, publishableKey) {
|
|
7898
|
-
storage.removeItem(accessTokenStorageKey(publishableKey));
|
|
7863
|
+
function storeManagedAuthAccessToken(storage, publishableKey, accessToken) {
|
|
7864
|
+
storage.setItem(managedAuthAccessTokenStorageKey(publishableKey), accessToken);
|
|
7899
7865
|
}
|
|
7900
|
-
function
|
|
7901
|
-
storage.
|
|
7902
|
-
}
|
|
7903
|
-
function clearAuthState(storage, publishableKey) {
|
|
7904
|
-
storage.removeItem(authStateStorageKey(publishableKey));
|
|
7866
|
+
function clearManagedAuthAccessToken(storage, publishableKey) {
|
|
7867
|
+
storage.removeItem(managedAuthAccessTokenStorageKey(publishableKey));
|
|
7905
7868
|
}
|
|
7906
7869
|
|
|
7907
7870
|
// src/client/auth/provider.ts
|
|
@@ -7914,103 +7877,93 @@ function resolveProvidedAccessToken(token, logger) {
|
|
|
7914
7877
|
}
|
|
7915
7878
|
return { kind: "resolved", accessToken: result.data };
|
|
7916
7879
|
}
|
|
7917
|
-
function
|
|
7918
|
-
const result = errors12.trySync(function
|
|
7919
|
-
return resolveAccessToken(token, logger);
|
|
7880
|
+
function resolveHostedAccessToken(token, storage, options) {
|
|
7881
|
+
const result = errors12.trySync(function resolveHostedToken() {
|
|
7882
|
+
return resolveAccessToken(token, options.logger);
|
|
7920
7883
|
});
|
|
7921
7884
|
if (result.error) {
|
|
7922
|
-
return { kind: "
|
|
7885
|
+
return { kind: "sign-in-failed", error: result.error };
|
|
7923
7886
|
}
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7887
|
+
storeManagedAuthAccessToken(storage, options.publishableKey, token);
|
|
7888
|
+
return {
|
|
7889
|
+
kind: "resolved",
|
|
7890
|
+
accessToken: result.data,
|
|
7891
|
+
clearCachedAccessToken: function clearCachedAccessToken() {
|
|
7892
|
+
clearManagedAuthAccessToken(storage, options.publishableKey);
|
|
7893
|
+
}
|
|
7929
7894
|
};
|
|
7930
7895
|
}
|
|
7931
|
-
function
|
|
7932
|
-
const
|
|
7933
|
-
const contextResult = errors12.trySync(function readContext() {
|
|
7934
|
-
const storage = browserStorage(options.hostedAuth, logger);
|
|
7935
|
-
const url = currentUrl(options.hostedAuth, logger);
|
|
7936
|
-
return { storage, url };
|
|
7937
|
-
});
|
|
7938
|
-
if (contextResult.error) {
|
|
7939
|
-
return { kind: "unauthenticated", error: contextResult.error };
|
|
7940
|
-
}
|
|
7941
|
-
return contextResult.data;
|
|
7942
|
-
}
|
|
7943
|
-
function resolveStoredAccessToken(context, options) {
|
|
7944
|
-
const stored = loadStoredAccessToken(context.storage, options.publishableKey);
|
|
7896
|
+
function resolveStoredAccessToken(storage, options) {
|
|
7897
|
+
const stored = loadManagedAuthAccessToken(storage, options.publishableKey);
|
|
7945
7898
|
if (stored === null) {
|
|
7946
|
-
return
|
|
7899
|
+
return { kind: "missing" };
|
|
7947
7900
|
}
|
|
7948
|
-
const
|
|
7949
|
-
|
|
7950
|
-
|
|
7951
|
-
|
|
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" };
|
|
7952
7907
|
}
|
|
7953
|
-
return
|
|
7908
|
+
return {
|
|
7909
|
+
kind: "resolved",
|
|
7910
|
+
accessToken: result.data,
|
|
7911
|
+
clearCachedAccessToken: function clearCachedAccessToken() {
|
|
7912
|
+
clearManagedAuthAccessToken(storage, options.publishableKey);
|
|
7913
|
+
}
|
|
7914
|
+
};
|
|
7954
7915
|
}
|
|
7955
7916
|
function resolveExistingAccessToken(options) {
|
|
7956
7917
|
if (options.accessToken !== undefined) {
|
|
7957
7918
|
return resolveProvidedAccessToken(options.accessToken, options.logger);
|
|
7958
7919
|
}
|
|
7959
|
-
const
|
|
7960
|
-
|
|
7961
|
-
|
|
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 };
|
|
7925
|
+
}
|
|
7926
|
+
return resolveStoredAccessToken(storageResult.data, options);
|
|
7927
|
+
}
|
|
7928
|
+
function authFailureResult(error) {
|
|
7929
|
+
if (errors12.is(error, ErrAuthConfigInvalid)) {
|
|
7930
|
+
return { kind: "auth-config-invalid", error };
|
|
7962
7931
|
}
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
return stored;
|
|
7932
|
+
if (errors12.is(error, ErrAuthUnavailable)) {
|
|
7933
|
+
return { kind: "auth-unavailable", error };
|
|
7966
7934
|
}
|
|
7967
|
-
return { kind: "
|
|
7935
|
+
return { kind: "sign-in-failed", error };
|
|
7968
7936
|
}
|
|
7969
7937
|
async function beginHostedLogin(options) {
|
|
7970
7938
|
const logger = options.logger;
|
|
7971
|
-
const
|
|
7972
|
-
|
|
7973
|
-
|
|
7974
|
-
|
|
7975
|
-
|
|
7976
|
-
const url = context.url;
|
|
7977
|
-
const clientStateResult = errors12.trySync(function prepareHostedLogin() {
|
|
7978
|
-
const clientState2 = randomClientState(logger);
|
|
7979
|
-
storeAuthState(storage, options.publishableKey, clientState2);
|
|
7980
|
-
return clientState2;
|
|
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 };
|
|
7981
7944
|
});
|
|
7982
|
-
if (
|
|
7983
|
-
return
|
|
7945
|
+
if (contextResult.error) {
|
|
7946
|
+
return authFailureResult(contextResult.error);
|
|
7984
7947
|
}
|
|
7985
|
-
const
|
|
7948
|
+
const context = contextResult.data;
|
|
7986
7949
|
const accessTokenResult = await errors12.try(beginHostedPopup({
|
|
7987
7950
|
origin: options.origin,
|
|
7988
7951
|
publishableKey: options.publishableKey,
|
|
7989
|
-
currentUrl: url,
|
|
7990
|
-
clientState,
|
|
7991
|
-
options: options.hostedAuth,
|
|
7952
|
+
currentUrl: context.url,
|
|
7953
|
+
clientState: context.clientState,
|
|
7992
7954
|
logger
|
|
7993
7955
|
}));
|
|
7994
7956
|
if (accessTokenResult.error) {
|
|
7995
|
-
|
|
7996
|
-
|
|
7997
|
-
|
|
7998
|
-
const invalidate = createStoredTokenInvalidator(storage, options.publishableKey);
|
|
7999
|
-
const resolved = resolveManagedAccessToken(accessTokenResult.data, logger, invalidate);
|
|
8000
|
-
if (resolved.kind === "unauthenticated") {
|
|
8001
|
-
clearAuthState(storage, options.publishableKey);
|
|
8002
|
-
return resolved;
|
|
8003
|
-
}
|
|
8004
|
-
storeAccessToken(storage, options.publishableKey, accessTokenResult.data);
|
|
8005
|
-
clearAuthState(storage, options.publishableKey);
|
|
8006
|
-
return resolved;
|
|
7957
|
+
return authFailureResult(accessTokenResult.error);
|
|
7958
|
+
}
|
|
7959
|
+
return resolveHostedAccessToken(accessTokenResult.data, context.storage, options);
|
|
8007
7960
|
}
|
|
8008
7961
|
|
|
8009
|
-
// src/client/
|
|
8010
|
-
function
|
|
7962
|
+
// src/client/auth-state.ts
|
|
7963
|
+
function pendingLogin(config) {
|
|
8011
7964
|
let pending;
|
|
8012
7965
|
function login() {
|
|
8013
|
-
if (pending) {
|
|
7966
|
+
if (pending !== undefined) {
|
|
8014
7967
|
return pending;
|
|
8015
7968
|
}
|
|
8016
7969
|
pending = config.login().finally(function clearPending() {
|
|
@@ -8018,10 +7971,34 @@ function unauthenticatedState(config) {
|
|
|
8018
7971
|
});
|
|
8019
7972
|
return pending;
|
|
8020
7973
|
}
|
|
7974
|
+
return login;
|
|
7975
|
+
}
|
|
7976
|
+
function signInRequiredState(config) {
|
|
8021
7977
|
return {
|
|
8022
|
-
phase: "
|
|
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",
|
|
8023
7986
|
error: config.error,
|
|
8024
|
-
login,
|
|
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,
|
|
8025
8002
|
toJSON: poisonToJSON
|
|
8026
8003
|
};
|
|
8027
8004
|
}
|
|
@@ -8048,11 +8025,10 @@ function primerOrigin(origin) {
|
|
|
8048
8025
|
}
|
|
8049
8026
|
async function startRuntime(config, resolved) {
|
|
8050
8027
|
let reauthenticate = null;
|
|
8051
|
-
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
8055
|
-
return Promise.resolve(makeUnauthenticatedState(config, ErrTokenExpired));
|
|
8028
|
+
if (resolved.clearCachedAccessToken !== undefined) {
|
|
8029
|
+
reauthenticate = function reauthenticateManagedAuth(error) {
|
|
8030
|
+
resolved.clearCachedAccessToken?.();
|
|
8031
|
+
return Promise.resolve(makeSignInFailedState(config, error));
|
|
8056
8032
|
};
|
|
8057
8033
|
}
|
|
8058
8034
|
const transport = createTransport({
|
|
@@ -8073,8 +8049,15 @@ async function startRuntime(config, resolved) {
|
|
|
8073
8049
|
});
|
|
8074
8050
|
return session.execute({ kind: "observation" }, "start");
|
|
8075
8051
|
}
|
|
8076
|
-
function
|
|
8077
|
-
return
|
|
8052
|
+
function makeSignInRequiredState(config) {
|
|
8053
|
+
return signInRequiredState({
|
|
8054
|
+
login: function login() {
|
|
8055
|
+
return loginAndStart(config);
|
|
8056
|
+
}
|
|
8057
|
+
});
|
|
8058
|
+
}
|
|
8059
|
+
function makeSignInFailedState(config, error) {
|
|
8060
|
+
return signInFailedState({
|
|
8078
8061
|
error,
|
|
8079
8062
|
login: function login() {
|
|
8080
8063
|
return loginAndStart(config);
|
|
@@ -8087,10 +8070,19 @@ async function loginAndStart(config) {
|
|
|
8087
8070
|
publishableKey: config.publishableKey,
|
|
8088
8071
|
logger: config.logger
|
|
8089
8072
|
});
|
|
8090
|
-
if (result.kind === "
|
|
8091
|
-
return
|
|
8073
|
+
if (result.kind === "auth-unavailable") {
|
|
8074
|
+
return authUnavailableState(result.error);
|
|
8092
8075
|
}
|
|
8093
|
-
|
|
8076
|
+
if (result.kind === "auth-config-invalid") {
|
|
8077
|
+
return authConfigInvalidState(result.error);
|
|
8078
|
+
}
|
|
8079
|
+
if (result.kind === "sign-in-failed") {
|
|
8080
|
+
return makeSignInFailedState(config, result.error);
|
|
8081
|
+
}
|
|
8082
|
+
return startRuntime(config, {
|
|
8083
|
+
accessToken: result.accessToken,
|
|
8084
|
+
clearCachedAccessToken: result.clearCachedAccessToken
|
|
8085
|
+
});
|
|
8094
8086
|
}
|
|
8095
8087
|
async function start(options) {
|
|
8096
8088
|
const logger = options.logger;
|
|
@@ -8119,8 +8111,11 @@ async function start(options) {
|
|
|
8119
8111
|
toJSON: poisonToJSON
|
|
8120
8112
|
};
|
|
8121
8113
|
}
|
|
8122
|
-
if (accessToken.kind === "
|
|
8123
|
-
return
|
|
8114
|
+
if (accessToken.kind === "auth-unavailable") {
|
|
8115
|
+
return authUnavailableState(accessToken.error);
|
|
8116
|
+
}
|
|
8117
|
+
if (accessToken.kind === "missing") {
|
|
8118
|
+
return makeSignInRequiredState(config);
|
|
8124
8119
|
}
|
|
8125
8120
|
return startRuntime(config, accessToken);
|
|
8126
8121
|
}
|
|
@@ -8128,4 +8123,4 @@ export {
|
|
|
8128
8123
|
start
|
|
8129
8124
|
};
|
|
8130
8125
|
|
|
8131
|
-
//# debugId=
|
|
8126
|
+
//# debugId=D2887B68B961EF6364756E2164756E21
|