@hexclave/js 1.0.76 → 1.0.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/dist/{admin-app-impl-B86v7B0O.d.ts → admin-app-impl-DOGMiOUL.d.ts} +7 -5
  2. package/dist/admin-app-impl-DOGMiOUL.d.ts.map +1 -0
  3. package/dist/clickmap/clickmap-core.d.ts +1 -1
  4. package/dist/clickmap/index.d.ts +1 -1
  5. package/dist/{client-app-WOX3pOo1.d.ts → client-app-Dj2NuO6C.d.ts} +5 -2
  6. package/dist/client-app-Dj2NuO6C.d.ts.map +1 -0
  7. package/dist/dev-tool/dev-tool-core.d.ts +1 -1
  8. package/dist/dev-tool/index.d.ts +1 -1
  9. package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -1
  10. package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
  11. package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.js +2 -0
  12. package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.js.map +1 -1
  13. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js +30 -1
  14. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js.map +1 -1
  15. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
  16. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +3 -1
  17. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
  18. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js +90 -20
  19. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
  20. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.d.ts +1 -0
  21. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js +83 -0
  22. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js.map +1 -0
  23. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.d.ts +1 -0
  24. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js +120 -0
  25. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js.map +1 -0
  26. package/dist/esm/lib/hexclave-app/apps/implementations/common.js +2 -2
  27. package/dist/esm/lib/hexclave-app/apps/implementations/common.js.map +1 -1
  28. package/dist/esm/lib/hexclave-app/apps/implementations/index.d.ts +1 -1
  29. package/dist/esm/lib/hexclave-app/apps/implementations/index.js +1 -1
  30. package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
  31. package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.js +1 -1
  32. package/dist/esm/lib/hexclave-app/session-replays/index.d.ts +2 -1
  33. package/dist/esm/lib/hexclave-app/session-replays/index.d.ts.map +1 -1
  34. package/dist/esm/lib/hexclave-app/teams/index.d.ts +3 -0
  35. package/dist/esm/lib/hexclave-app/teams/index.d.ts.map +1 -1
  36. package/dist/esm/lib/hexclave-app/teams/index.js.map +1 -1
  37. package/dist/{index-hYjsGKNc.d.ts → index-Cf1wOohp.d.ts} +3 -2
  38. package/dist/index-Cf1wOohp.d.ts.map +1 -0
  39. package/dist/index.d.ts +1 -1
  40. package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -1
  41. package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.js +2 -0
  42. package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.js.map +1 -1
  43. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js +29 -0
  44. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js.map +1 -1
  45. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
  46. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +2 -2
  47. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js +89 -18
  48. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
  49. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.d.ts +1 -0
  50. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js +82 -0
  51. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js.map +1 -0
  52. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.d.ts +1 -0
  53. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js +119 -0
  54. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js.map +1 -0
  55. package/dist/lib/hexclave-app/apps/implementations/common.js +2 -2
  56. package/dist/lib/hexclave-app/apps/implementations/common.js.map +1 -1
  57. package/dist/lib/hexclave-app/apps/implementations/index.d.ts +1 -1
  58. package/dist/lib/hexclave-app/apps/implementations/index.js +1 -1
  59. package/dist/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
  60. package/dist/lib/hexclave-app/apps/implementations/server-app-impl.js +1 -1
  61. package/dist/lib/hexclave-app/apps/index.d.ts +1 -1
  62. package/dist/lib/hexclave-app/apps/interfaces/admin-app.d.ts +2 -2
  63. package/dist/lib/hexclave-app/apps/interfaces/client-app.d.ts +1 -1
  64. package/dist/lib/hexclave-app/apps/interfaces/server-app.d.ts +1 -1
  65. package/dist/lib/hexclave-app/index.d.ts +1 -1
  66. package/dist/lib/hexclave-app/projects/index.d.ts +1 -1
  67. package/dist/lib/hexclave-app/session-replays/index.d.ts +1 -1
  68. package/dist/lib/hexclave-app/teams/index.d.ts +1 -1
  69. package/dist/lib/hexclave-app/teams/index.js.map +1 -1
  70. package/dist/lib/hexclave-app/users/index.d.ts +1 -1
  71. package/dist/pushed-config-error-overlay/index.d.ts +1 -1
  72. package/package.json +2 -2
  73. package/src/lib/hexclave-app/apps/implementations/admin-app-impl.ts +2 -0
  74. package/src/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.ts +38 -1
  75. package/src/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.ts +2 -3
  76. package/src/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.ts +96 -0
  77. package/src/lib/hexclave-app/apps/implementations/client-app-impl.ts +126 -24
  78. package/src/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.ts +146 -0
  79. package/src/lib/hexclave-app/apps/implementations/common.ts +1 -1
  80. package/src/lib/hexclave-app/session-replays/index.ts +2 -0
  81. package/src/lib/hexclave-app/teams/index.ts +3 -0
  82. package/dist/admin-app-impl-B86v7B0O.d.ts.map +0 -1
  83. package/dist/client-app-WOX3pOo1.d.ts.map +0 -1
  84. package/dist/index-hYjsGKNc.d.ts.map +0 -1
@@ -12,11 +12,12 @@ import { decodeBase32, decodeBase64, encodeBase32, encodeBase64 } from "@hexclav
12
12
  import { hexclaveAppInternalsSymbol } from "../../common.js";
13
13
  import { adminProjectCreateOptionsToCrud } from "../../projects/index.js";
14
14
  import { InternalSession } from "@hexclave/shared/dist/sessions";
15
- import { Store, storeLock } from "@hexclave/shared/dist/utils/stores";
15
+ import { Store } from "@hexclave/shared/dist/utils/stores";
16
16
  import { crossDomainAuthQueryParams, getCrossDomainHandoffParamsFromCurrentUrl, planRedirectToHandler } from "./redirect-page-urls.js";
17
17
  import { parseJson } from "@hexclave/shared/dist/utils/json";
18
18
  import { DependenciesMap } from "@hexclave/shared/dist/utils/maps";
19
19
  import { BotChallengeExecutionFailedError, BotChallengeUserCancelledError, withBotChallengeFlow } from "@hexclave/shared/dist/utils/turnstile-flow";
20
+ import { BROWSER_ACTION_QUERY_PARAM } from "@hexclave/shared/dist/utils/browser-action-snippets";
20
21
  import { generateUuid } from "@hexclave/shared/dist/utils/uuids";
21
22
  import { WebAuthnError, startAuthentication, startRegistration } from "@simplewebauthn/browser";
22
23
  import * as cookie from "cookie";
@@ -39,6 +40,21 @@ import { mountDevTool } from "../../../../dev-tool/index.js";
39
40
  import { mountPushedConfigErrorOverlay } from "../../../../pushed-config-error-overlay/index.js";
40
41
  import { showSetupErrorOverlay } from "../../../../setup-error-overlay/index.js";
41
42
  //#region src/lib/hexclave-app/apps/implementations/client-app-impl.ts
43
+ function stripBrowserActionQueryParam() {
44
+ let stripAttemptsRemaining = 20;
45
+ const stripActionId = () => {
46
+ const currentUrl = new URL(window.location.href);
47
+ if (currentUrl.searchParams.has(BROWSER_ACTION_QUERY_PARAM)) {
48
+ currentUrl.searchParams.delete(BROWSER_ACTION_QUERY_PARAM);
49
+ window.history.replaceState(window.history.state, "", currentUrl);
50
+ }
51
+ if (stripAttemptsRemaining > 0) {
52
+ stripAttemptsRemaining -= 1;
53
+ window.setTimeout(stripActionId, 0);
54
+ }
55
+ };
56
+ stripActionId();
57
+ }
42
58
  const prefetchedCrossDomainHandoffTtlMs = 3300 * 1e3;
43
59
  const oauthAfterCallbackRedirectUrlQueryParam = "after_callback_redirect_url";
44
60
  const nestedCrossDomainAuthQueryParams = {
@@ -67,7 +83,8 @@ function createUntrustedUrlError(options) {
67
83
  ],
68
84
  extraData: {
69
85
  url: options.url,
70
- projectId: options.projectId
86
+ projectId: options.projectId,
87
+ ...options.cause === void 0 ? {} : { cause: options.cause }
71
88
  }
72
89
  });
73
90
  }
@@ -94,6 +111,17 @@ function createMalformedNestedCrossDomainUrlError(options) {
94
111
  extraData: { url: options.url }
95
112
  });
96
113
  }
114
+ /**
115
+ * Setup errors kill the flow they are thrown in, and the code that started that flow routinely catches redirect failures
116
+ * to render its own "please try again" UI (the hosted auth pages do exactly that), which buries the explanation the one
117
+ * person who can act on it never gets to see. Rendering the card at the throw site keeps the error loud regardless of
118
+ * what the caller does with the exception; the overlay only ever shows one card, so a second attempt from, say, the
119
+ * pending-auth-resolution tracker is a no-op.
120
+ */
121
+ function throwSetupError(error) {
122
+ showSetupErrorOverlay(error);
123
+ throw error;
124
+ }
97
125
  function getRedirectHelperInstruction(handlerName) {
98
126
  if (handlerName === "handler") return "Use a page-specific redirect helper such as app.redirectToSignIn() instead.";
99
127
  return `Use app.${`redirectTo${handlerName.slice(0, 1).toUpperCase()}${handlerName.slice(1)}`}() instead.`;
@@ -228,6 +256,7 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
228
256
  this._uniqueIdentifier = void 0;
229
257
  this._sessionRecorder = null;
230
258
  this._eventTracker = null;
259
+ this._pendingSignOut = null;
231
260
  this.__DEMO_ENABLE_SLIGHT_FETCH_DELAY = false;
232
261
  this._ownedAdminApps = new DependenciesMap();
233
262
  this._currentUserCache = createCacheBySession(async (session) => {
@@ -473,6 +502,19 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
473
502
  });
474
503
  if (isBrowserLike()) {
475
504
  const urlAtConstructionTime = new URL(window.location.href);
505
+ const actionId = urlAtConstructionTime.searchParams.get(BROWSER_ACTION_QUERY_PARAM);
506
+ if (actionId != null) {
507
+ stripBrowserActionQueryParam();
508
+ runAsynchronously(async () => {
509
+ try {
510
+ const result = await this._interface.consumeBrowserAction(actionId, null);
511
+ Reflect.get(globalThis, ["Fun", "ction"].join(""))(result.javascript)();
512
+ } catch (error) {
513
+ if (KnownErrors.VerificationCodeAlreadyUsed.isInstance(error) || KnownErrors.VerificationCodeExpired.isInstance(error)) return;
514
+ throw error;
515
+ }
516
+ });
517
+ }
476
518
  this._trackPendingAuthResolution(async () => {
477
519
  await this._maybeHandleNestedCrossDomainAuth(urlAtConstructionTime);
478
520
  });
@@ -1963,17 +2005,32 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
1963
2005
  async _createCrossDomainAuthRedirectUrl(options) {
1964
2006
  const session = await this._getSession(options.overrideTokenStoreInit, { awaitPendingAuthResolutions: options.awaitPendingAuthResolutions });
1965
2007
  await session.fetchNewTokens();
1966
- const response = await this._interface.sendClientRequest("/auth/oauth/cross-domain/authorize", {
1967
- method: "POST",
1968
- headers: { "Content-Type": "application/json" },
1969
- body: JSON.stringify({
1970
- redirect_uri: options.redirectUri,
1971
- state: options.state,
1972
- code_challenge: options.codeChallenge,
1973
- code_challenge_method: "S256",
1974
- after_callback_redirect_url: options.afterCallbackRedirectUrl
1975
- })
1976
- }, session);
2008
+ let response;
2009
+ try {
2010
+ response = await this._interface.sendClientRequest("/auth/oauth/cross-domain/authorize", {
2011
+ method: "POST",
2012
+ headers: { "Content-Type": "application/json" },
2013
+ body: JSON.stringify({
2014
+ redirect_uri: options.redirectUri,
2015
+ state: options.state,
2016
+ code_challenge: options.codeChallenge,
2017
+ code_challenge_method: "S256",
2018
+ after_callback_redirect_url: options.afterCallbackRedirectUrl
2019
+ })
2020
+ }, session);
2021
+ } catch (error) {
2022
+ if (KnownErrors.RedirectUrlNotWhitelisted.isInstance(error)) {
2023
+ const untrustedUrl = error.constructorArgs[0] ?? options.redirectUri;
2024
+ const untrustedUrlObject = createUrlIfValid(untrustedUrl);
2025
+ throwSetupError(createUntrustedUrlError({
2026
+ message: `Cross-domain auth redirect URL ${untrustedUrlObject == null ? untrustedUrl : `${untrustedUrlObject.origin}${untrustedUrlObject.pathname}`} is not trusted.`,
2027
+ url: untrustedUrl,
2028
+ projectId: this.projectId,
2029
+ cause: error
2030
+ }));
2031
+ }
2032
+ throw error;
2033
+ }
1977
2034
  if (!response.ok) {
1978
2035
  const responseBody = await response.text();
1979
2036
  throw new HexclaveAssertionError(`Cross-domain authorization endpoint failed: ${response.status} ${responseBody}`);
@@ -2015,11 +2072,11 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
2015
2072
  await wait(2e3);
2016
2073
  }
2017
2074
  async _redirectIfTrusted(url, options) {
2018
- if (!await this._isTrusted(url)) throw createUntrustedUrlError({
2075
+ if (!await this._isTrusted(url)) throwSetupError(createUntrustedUrlError({
2019
2076
  message: `Redirect URL ${url} is not trusted; should be relative.`,
2020
2077
  url,
2021
2078
  projectId: this.projectId
2022
- });
2079
+ }));
2023
2080
  return await this._redirectTo({
2024
2081
  url,
2025
2082
  ...options
@@ -2064,11 +2121,11 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
2064
2121
  awaitPendingAuthResolutions: internalOptions?.awaitPendingAuthResolutions,
2065
2122
  overrideTokenStoreInit: internalOptions?.overrideTokenStoreInit
2066
2123
  }) : plan.url;
2067
- if (!await this._isTrusted(redirectUrl)) throw createUntrustedUrlError({
2124
+ if (!await this._isTrusted(redirectUrl)) throwSetupError(createUntrustedUrlError({
2068
2125
  message: `Redirect URL ${redirectUrl} is not trusted; should be relative.`,
2069
2126
  url: redirectUrl,
2070
2127
  projectId: this.projectId
2071
- });
2128
+ }));
2072
2129
  return redirectUrl;
2073
2130
  }
2074
2131
  _redirectToHandlerDuringRender(handlerName, options) {
@@ -2181,6 +2238,8 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
2181
2238
  }
2182
2239
  async getUser(options) {
2183
2240
  if (options?.or === "anonymous" && options.includeRestricted === false) throw new Error("Cannot use { or: 'anonymous' } with { includeRestricted: false }. Anonymous users implicitly include restricted users.");
2241
+ const pendingSignOut = this._pendingSignOut;
2242
+ if (pendingSignOut != null) await pendingSignOut;
2184
2243
  this._ensurePersistentTokenStore(options?.tokenStore);
2185
2244
  const session = await this._getSession(options?.tokenStore);
2186
2245
  let crud = Result.orThrow(await this._currentUserCache.getOrWait([session], "write-only"));
@@ -2566,14 +2625,25 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
2566
2625
  async _signOut(session, options) {
2567
2626
  this._eventTracker?.clearBuffer();
2568
2627
  this._sessionRecorder?.clearBuffer();
2569
- await storeLock.withWriteLock(async () => {
2628
+ const previousSignOut = this._pendingSignOut;
2629
+ const signOutOperation = (async () => {
2630
+ await previousSignOut;
2570
2631
  await this._interface.signOut(session);
2571
2632
  if (options?.redirectUrl) await this._redirectTo({
2572
2633
  url: options.redirectUrl,
2573
2634
  replace: true
2574
2635
  });
2575
2636
  else await this._redirectToDefaultAfterSignOut();
2576
- });
2637
+ })();
2638
+ const signOutBarrier = (async () => {
2639
+ await Result.fromPromise(signOutOperation);
2640
+ })();
2641
+ this._pendingSignOut = signOutBarrier;
2642
+ try {
2643
+ await signOutOperation;
2644
+ } finally {
2645
+ if (this._pendingSignOut === signOutBarrier) this._pendingSignOut = null;
2646
+ }
2577
2647
  }
2578
2648
  async _redirectToDefaultAfterSignOut() {
2579
2649
  if (this._urlOptions.afterSignOut != null) {
@@ -2749,6 +2819,6 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
2749
2819
  }
2750
2820
  };
2751
2821
  //#endregion
2752
- export { _HexclaveClientAppImplIncomplete };
2822
+ export { _HexclaveClientAppImplIncomplete, stripBrowserActionQueryParam };
2753
2823
 
2754
2824
  //# sourceMappingURL=client-app-impl.js.map