@hexclave/next 1.0.75 → 1.0.77

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 (231) hide show
  1. package/dist/{admin-app-impl-sTQD2d-3.d.ts → admin-app-impl-CxDpE89P.d.ts} +12 -10
  2. package/dist/admin-app-impl-CxDpE89P.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-BCT5p_70.d.ts → client-app-wFodsYml.d.ts} +4 -1
  6. package/dist/client-app-wFodsYml.d.ts.map +1 -0
  7. package/dist/common-BK_sydxF.d.ts.map +1 -1
  8. package/dist/components/elements/sidebar-layout.d.ts.map +1 -1
  9. package/dist/components/elements/sidebar-layout.js +12 -2
  10. package/dist/components/elements/sidebar-layout.js.map +1 -1
  11. package/dist/components/selected-team-switcher.d.ts +1 -1
  12. package/dist/components/team-icon.d.ts +1 -1
  13. package/dist/components/team-switcher.d.ts +1 -1
  14. package/dist/components-page/account-settings/payments/payments-page.d.ts +1 -1
  15. package/dist/components-page/account-settings/teams/leave-team-section.d.ts +1 -1
  16. package/dist/components-page/account-settings/teams/team-api-keys-section.d.ts +1 -1
  17. package/dist/components-page/account-settings/teams/team-display-name-section.d.ts +1 -1
  18. package/dist/components-page/account-settings/teams/team-member-invitation-section.d.ts +1 -1
  19. package/dist/components-page/account-settings/teams/team-member-list-section.d.ts +1 -1
  20. package/dist/components-page/account-settings/teams/team-page.d.ts +1 -1
  21. package/dist/components-page/account-settings/teams/team-profile-image-section.d.ts +1 -1
  22. package/dist/components-page/account-settings/teams/team-profile-user-section.d.ts +1 -1
  23. package/dist/components-page/hexclave-handler-client.d.ts +1 -1
  24. package/dist/components-page/hexclave-handler.d.ts +1 -1
  25. package/dist/components-page/team-invitation-cache.d.ts +5 -0
  26. package/dist/components-page/team-invitation-cache.d.ts.map +1 -0
  27. package/dist/components-page/team-invitation-cache.js +12 -0
  28. package/dist/components-page/team-invitation-cache.js.map +1 -0
  29. package/dist/components-page/team-invitation-cache.test.d.ts +1 -0
  30. package/dist/components-page/team-invitation-cache.test.js +23 -0
  31. package/dist/components-page/team-invitation-cache.test.js.map +1 -0
  32. package/dist/components-page/team-invitation.js +8 -7
  33. package/dist/components-page/team-invitation.js.map +1 -1
  34. package/dist/dev-tool/dev-tool-core.d.ts +1 -1
  35. package/dist/dev-tool/dev-tool-core.d.ts.map +1 -1
  36. package/dist/dev-tool/dev-tool-core.js +2 -2
  37. package/dist/dev-tool/dev-tool-core.js.map +1 -1
  38. package/dist/dev-tool/index.d.ts +1 -1
  39. package/dist/dev-tool/index.d.ts.map +1 -1
  40. package/dist/dev-tool/index.js +2 -8
  41. package/dist/dev-tool/index.js.map +1 -1
  42. package/dist/esm/components/elements/sidebar-layout.d.ts.map +1 -1
  43. package/dist/esm/components/elements/sidebar-layout.js +12 -2
  44. package/dist/esm/components/elements/sidebar-layout.js.map +1 -1
  45. package/dist/esm/components-page/team-invitation-cache.d.ts +5 -0
  46. package/dist/esm/components-page/team-invitation-cache.d.ts.map +1 -0
  47. package/dist/esm/components-page/team-invitation-cache.js +11 -0
  48. package/dist/esm/components-page/team-invitation-cache.js.map +1 -0
  49. package/dist/esm/components-page/team-invitation-cache.test.d.ts +1 -0
  50. package/dist/esm/components-page/team-invitation-cache.test.js +24 -0
  51. package/dist/esm/components-page/team-invitation-cache.test.js.map +1 -0
  52. package/dist/esm/components-page/team-invitation.d.ts.map +1 -1
  53. package/dist/esm/components-page/team-invitation.js +8 -7
  54. package/dist/esm/components-page/team-invitation.js.map +1 -1
  55. package/dist/esm/dev-tool/dev-tool-core.d.ts.map +1 -1
  56. package/dist/esm/dev-tool/dev-tool-core.js +1 -1
  57. package/dist/esm/dev-tool/dev-tool-core.js.map +1 -1
  58. package/dist/esm/dev-tool/index.d.ts.map +1 -1
  59. package/dist/esm/dev-tool/index.js +2 -8
  60. package/dist/esm/dev-tool/index.js.map +1 -1
  61. package/dist/esm/generated/quetzal-translations.d.ts +2 -2
  62. package/dist/esm/in-page-ui/dev-environment.d.ts +5 -0
  63. package/dist/esm/in-page-ui/dev-environment.d.ts.map +1 -0
  64. package/dist/esm/in-page-ui/dev-environment.js +18 -0
  65. package/dist/esm/in-page-ui/dev-environment.js.map +1 -0
  66. package/dist/esm/in-page-ui/issue-card.d.ts +52 -0
  67. package/dist/esm/in-page-ui/issue-card.d.ts.map +1 -0
  68. package/dist/esm/in-page-ui/issue-card.js +392 -0
  69. package/dist/esm/in-page-ui/issue-card.js.map +1 -0
  70. package/dist/esm/in-page-ui/issue-card.test.d.ts +1 -0
  71. package/dist/esm/in-page-ui/issue-card.test.js +60 -0
  72. package/dist/esm/in-page-ui/issue-card.test.js.map +1 -0
  73. package/dist/esm/in-page-ui/logo.d.ts +5 -0
  74. package/dist/esm/in-page-ui/logo.d.ts.map +1 -0
  75. package/dist/esm/in-page-ui/logo.js +6 -0
  76. package/dist/esm/in-page-ui/logo.js.map +1 -0
  77. package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +4 -4
  78. package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
  79. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js +30 -1
  80. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js.map +1 -1
  81. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js +140 -15
  82. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
  83. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +5 -3
  84. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
  85. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js +184 -28
  86. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
  87. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.d.ts +1 -0
  88. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js +83 -0
  89. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js.map +1 -0
  90. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.d.ts +1 -0
  91. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js +120 -0
  92. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js.map +1 -0
  93. package/dist/esm/lib/hexclave-app/apps/implementations/common.d.ts.map +1 -1
  94. package/dist/esm/lib/hexclave-app/apps/implementations/common.js +4 -3
  95. package/dist/esm/lib/hexclave-app/apps/implementations/common.js.map +1 -1
  96. package/dist/esm/lib/hexclave-app/apps/implementations/index.d.ts +1 -1
  97. package/dist/esm/lib/hexclave-app/apps/implementations/index.js +1 -1
  98. package/dist/esm/lib/hexclave-app/apps/implementations/redirect-page-urls.test.js +1 -0
  99. package/dist/esm/lib/hexclave-app/apps/implementations/redirect-page-urls.test.js.map +1 -1
  100. package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +2 -2
  101. package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.js +1 -1
  102. package/dist/esm/lib/hexclave-app/teams/index.d.ts +3 -0
  103. package/dist/esm/lib/hexclave-app/teams/index.d.ts.map +1 -1
  104. package/dist/esm/lib/hexclave-app/teams/index.js.map +1 -1
  105. package/dist/esm/pushed-config-error-overlay/index.d.ts.map +1 -1
  106. package/dist/esm/pushed-config-error-overlay/index.js +42 -343
  107. package/dist/esm/pushed-config-error-overlay/index.js.map +1 -1
  108. package/dist/esm/setup-error-overlay/index.d.ts +16 -0
  109. package/dist/esm/setup-error-overlay/index.d.ts.map +1 -0
  110. package/dist/esm/setup-error-overlay/index.js +106 -0
  111. package/dist/esm/setup-error-overlay/index.js.map +1 -0
  112. package/dist/esm/setup-error-overlay/index.test.d.ts +1 -0
  113. package/dist/esm/setup-error-overlay/index.test.js +125 -0
  114. package/dist/esm/setup-error-overlay/index.test.js.map +1 -0
  115. package/dist/esm/utils/monkey-patch.d.ts +7 -0
  116. package/dist/esm/utils/monkey-patch.d.ts.map +1 -0
  117. package/dist/esm/utils/monkey-patch.js +20 -0
  118. package/dist/esm/utils/monkey-patch.js.map +1 -0
  119. package/dist/esm/utils/react.d.ts +17 -0
  120. package/dist/esm/utils/react.d.ts.map +1 -0
  121. package/dist/esm/utils/react.js +60 -0
  122. package/dist/esm/utils/react.js.map +1 -0
  123. package/dist/generated/quetzal-translations.d.ts +1 -1
  124. package/dist/{hexclave-provider-BKxi3Vy-.d.ts → hexclave-provider-CMz2mt4I.d.ts} +3 -3
  125. package/dist/{hexclave-provider-BKxi3Vy-.d.ts.map → hexclave-provider-CMz2mt4I.d.ts.map} +1 -1
  126. package/dist/{hooks-CUe5bc3s.d.ts → hooks-CUHt4Y9C.d.ts} +2 -2
  127. package/dist/{hooks-CUe5bc3s.d.ts.map → hooks-CUHt4Y9C.d.ts.map} +1 -1
  128. package/dist/in-page-ui/dev-environment.d.ts +5 -0
  129. package/dist/in-page-ui/dev-environment.d.ts.map +1 -0
  130. package/dist/in-page-ui/dev-environment.js +19 -0
  131. package/dist/in-page-ui/dev-environment.js.map +1 -0
  132. package/dist/in-page-ui/issue-card.d.ts +52 -0
  133. package/dist/in-page-ui/issue-card.d.ts.map +1 -0
  134. package/dist/in-page-ui/issue-card.js +397 -0
  135. package/dist/in-page-ui/issue-card.js.map +1 -0
  136. package/dist/in-page-ui/issue-card.test.d.ts +1 -0
  137. package/dist/in-page-ui/issue-card.test.js +59 -0
  138. package/dist/in-page-ui/issue-card.test.js.map +1 -0
  139. package/dist/in-page-ui/logo.d.ts +5 -0
  140. package/dist/in-page-ui/logo.d.ts.map +1 -0
  141. package/dist/in-page-ui/logo.js +7 -0
  142. package/dist/in-page-ui/logo.js.map +1 -0
  143. package/dist/{index-CvB7XacB.d.ts → index-fR0LkUK7.d.ts} +2 -2
  144. package/dist/{index-CvB7XacB.d.ts.map → index-fR0LkUK7.d.ts.map} +1 -1
  145. package/dist/index.d.ts +4 -4
  146. package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -1
  147. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js +29 -0
  148. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js.map +1 -1
  149. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js +140 -15
  150. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
  151. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +2 -2
  152. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js +182 -25
  153. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
  154. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.d.ts +1 -0
  155. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js +82 -0
  156. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js.map +1 -0
  157. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.d.ts +1 -0
  158. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js +119 -0
  159. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js.map +1 -0
  160. package/dist/lib/hexclave-app/apps/implementations/common.js +4 -3
  161. package/dist/lib/hexclave-app/apps/implementations/common.js.map +1 -1
  162. package/dist/lib/hexclave-app/apps/implementations/index.d.ts +1 -1
  163. package/dist/lib/hexclave-app/apps/implementations/index.js +1 -1
  164. package/dist/lib/hexclave-app/apps/implementations/redirect-page-urls.test.js +1 -0
  165. package/dist/lib/hexclave-app/apps/implementations/redirect-page-urls.test.js.map +1 -1
  166. package/dist/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
  167. package/dist/lib/hexclave-app/apps/implementations/server-app-impl.js +1 -1
  168. package/dist/lib/hexclave-app/apps/index.d.ts +1 -1
  169. package/dist/lib/hexclave-app/apps/interfaces/admin-app.d.ts +1 -1
  170. package/dist/lib/hexclave-app/apps/interfaces/client-app.d.ts +1 -1
  171. package/dist/lib/hexclave-app/apps/interfaces/server-app.d.ts +1 -1
  172. package/dist/lib/hexclave-app/index.d.ts +1 -1
  173. package/dist/lib/hexclave-app/projects/index.d.ts +1 -1
  174. package/dist/lib/hexclave-app/teams/index.d.ts +1 -1
  175. package/dist/lib/hexclave-app/teams/index.js.map +1 -1
  176. package/dist/lib/hexclave-app/users/index.d.ts +1 -1
  177. package/dist/lib/hooks.d.ts +1 -1
  178. package/dist/providers/hexclave-context.d.ts +1 -1
  179. package/dist/providers/hexclave-provider-client.d.ts +1 -1
  180. package/dist/providers/hexclave-provider.d.ts +1 -1
  181. package/dist/providers/translation-provider.d.ts +1 -1
  182. package/dist/pushed-config-error-overlay/index.d.ts +1 -1
  183. package/dist/pushed-config-error-overlay/index.d.ts.map +1 -1
  184. package/dist/pushed-config-error-overlay/index.js +41 -342
  185. package/dist/pushed-config-error-overlay/index.js.map +1 -1
  186. package/dist/{quetzal-translations-XeH3zQ9l.d.ts → quetzal-translations-B2jT2SWm.d.ts} +3 -3
  187. package/dist/{quetzal-translations-XeH3zQ9l.d.ts.map → quetzal-translations-B2jT2SWm.d.ts.map} +1 -1
  188. package/dist/setup-error-overlay/index.d.ts +16 -0
  189. package/dist/setup-error-overlay/index.d.ts.map +1 -0
  190. package/dist/setup-error-overlay/index.js +108 -0
  191. package/dist/setup-error-overlay/index.js.map +1 -0
  192. package/dist/setup-error-overlay/index.test.d.ts +1 -0
  193. package/dist/setup-error-overlay/index.test.js +124 -0
  194. package/dist/setup-error-overlay/index.test.js.map +1 -0
  195. package/dist/team-invitation-B5Ol7vdd.d.ts.map +1 -1
  196. package/dist/{translation-provider-WOaZXqAz.d.ts → translation-provider-6505O9P2.d.ts} +2 -2
  197. package/dist/{translation-provider-WOaZXqAz.d.ts.map → translation-provider-6505O9P2.d.ts.map} +1 -1
  198. package/dist/utils/monkey-patch.d.ts +7 -0
  199. package/dist/utils/monkey-patch.d.ts.map +1 -0
  200. package/dist/utils/monkey-patch.js +23 -0
  201. package/dist/utils/monkey-patch.js.map +1 -0
  202. package/dist/utils/react.d.ts +17 -0
  203. package/dist/utils/react.d.ts.map +1 -0
  204. package/dist/utils/react.js +63 -0
  205. package/dist/utils/react.js.map +1 -0
  206. package/package.json +4 -4
  207. package/src/components/elements/sidebar-layout.tsx +16 -1
  208. package/src/components-page/team-invitation-cache.test.ts +25 -0
  209. package/src/components-page/team-invitation-cache.ts +13 -0
  210. package/src/components-page/team-invitation.tsx +16 -7
  211. package/src/dev-tool/dev-tool-core.ts +1 -4
  212. package/src/dev-tool/index.ts +4 -15
  213. package/src/in-page-ui/dev-environment.ts +33 -0
  214. package/src/in-page-ui/issue-card.test.ts +67 -0
  215. package/src/in-page-ui/issue-card.ts +500 -0
  216. package/src/in-page-ui/logo.ts +8 -0
  217. package/src/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.ts +38 -1
  218. package/src/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.ts +157 -0
  219. package/src/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.ts +96 -0
  220. package/src/lib/hexclave-app/apps/implementations/client-app-impl.ts +255 -37
  221. package/src/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.ts +146 -0
  222. package/src/lib/hexclave-app/apps/implementations/common.ts +2 -1
  223. package/src/lib/hexclave-app/apps/implementations/redirect-page-urls.test.ts +1 -0
  224. package/src/lib/hexclave-app/teams/index.ts +3 -0
  225. package/src/pushed-config-error-overlay/index.ts +45 -386
  226. package/src/setup-error-overlay/index.test.ts +133 -0
  227. package/src/setup-error-overlay/index.ts +150 -0
  228. package/src/utils/monkey-patch.tsx +31 -0
  229. package/src/utils/react.tsx +93 -0
  230. package/dist/admin-app-impl-sTQD2d-3.d.ts.map +0 -1
  231. package/dist/client-app-BCT5p_70.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"quetzal-translations-XeH3zQ9l.d.ts","names":[],"sources":["../src/generated/quetzal-translations.ts"],"mappings":";cAUa,WAAA,EAAW,GAAA;AAAA,cAqTX,cAAA,EAAc,GAAA,mIAAA,GAAA,66qBAAA,GAAA,usnBAAA,GAAA,iyrBAAA,GAAA,g+qBAAA,GAAA,8srBAAA,GAAA,2trBAAA,GAAA,41pBAAA,GAAA,m3eAAA,GAAA,oreAAA,GAAA,swpBAAA,GAAA,+0qBAAA,GAAA,8wbAAA,GAAA"}
1
+ {"version":3,"file":"quetzal-translations-B2jT2SWm.d.ts","names":[],"sources":["../src/generated/quetzal-translations.ts"],"mappings":";cAUa,WAAA,EAAW,GAAA;AAAA,cAqTX,cAAA,EAAc,GAAA,mIAAA,GAAA,66qBAAA,GAAA,usnBAAA,GAAA,iyrBAAA,GAAA,g+qBAAA,GAAA,8srBAAA,GAAA,2trBAAA,GAAA,41pBAAA,GAAA,m3eAAA,GAAA,oreAAA,GAAA,swpBAAA,GAAA,+0qBAAA,GAAA,8wbAAA,GAAA"}
@@ -0,0 +1,16 @@
1
+ //#region src/setup-error-overlay/index.d.ts
2
+ /** Exported so tests can drop the singleton; every other consumer should go through `showSetupErrorOverlay`. */
3
+ declare const SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY = "__hexclave-setup-error-overlay";
4
+ /**
5
+ * Shows `error` as an error card on the current page.
6
+ *
7
+ * Setup errors are always shown, because the flow they aborted cannot complete no matter what the end user does, and
8
+ * their messages are written for exactly this purpose. Any other error is only shown while developing — those messages
9
+ * are internal diagnostics, so in production they stay in captureError's hands.
10
+ *
11
+ * Returns a cleanup function that removes the card again.
12
+ */
13
+ declare function showSetupErrorOverlay(error: unknown): () => void;
14
+ //#endregion
15
+ export { SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY, showSetupErrorOverlay };
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/setup-error-overlay/index.ts"],"mappings":";;cAiBa,uCAAA;;;;AAAuC;AA8DpD;;;;AAAoD;iBAApC,qBAAA,CAAsB,KAAc"}
@@ -0,0 +1,108 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let ___in_page_ui_dom_js = require("../in-page-ui/dom.js");
3
+ let _hexclave_shared_dist_utils_errors = require("@hexclave/shared/dist/utils/errors");
4
+ let ___in_page_ui_dev_environment_js = require("../in-page-ui/dev-environment.js");
5
+ let ___in_page_ui_issue_card_js = require("../in-page-ui/issue-card.js");
6
+ //#region src/setup-error-overlay/index.ts
7
+ /** Exported so tests can drop the singleton; every other consumer should go through `showSetupErrorOverlay`. */
8
+ const SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY = "__hexclave-setup-error-overlay";
9
+ const css = (0, ___in_page_ui_issue_card_js.getIssueCardCSS)(".hexclave-setup-error-overlay");
10
+ const unexpectedErrorHowToFix = [
11
+ "Check the browser console and your Hexclave dashboard's logs for the full error.",
12
+ "Make sure your Hexclave SDK packages are up to date.",
13
+ "If the error looks like a bug in Hexclave, report it at https://github.com/hexclave/hexclave/issues."
14
+ ];
15
+ function toSetupIssue(error) {
16
+ if (_hexclave_shared_dist_utils_errors.HexclaveSetupError.isSetupError(error)) return {
17
+ title: error.title,
18
+ message: error.message,
19
+ howToFix: error.howToFix,
20
+ isFatal: true
21
+ };
22
+ return {
23
+ title: "Hexclave could not finish an authentication flow",
24
+ message: (0, _hexclave_shared_dist_utils_errors.errorToNiceString)(error),
25
+ howToFix: unexpectedErrorHowToFix,
26
+ isFatal: false
27
+ };
28
+ }
29
+ function buildAiPrompt(issue) {
30
+ return [
31
+ "Help me fix this Hexclave setup error.",
32
+ "",
33
+ "Hexclave reminder:",
34
+ "This error was thrown by the Hexclave SDK in the browser while it was resolving an authentication flow, and the flow was aborted because of it. It is a problem with how my project is set up, not something my users can work around. Use your web fetch tool to read https://skill.hexclave.com to learn more about Hexclave.",
35
+ "",
36
+ "Error message:",
37
+ issue.message,
38
+ "",
39
+ "Hexclave suggests:",
40
+ ...issue.howToFix.map((step) => `- ${step}`),
41
+ "",
42
+ "Please explain the likely cause and suggest the smallest safe fix."
43
+ ].join("\n");
44
+ }
45
+ /**
46
+ * Shows `error` as an error card on the current page.
47
+ *
48
+ * Setup errors are always shown, because the flow they aborted cannot complete no matter what the end user does, and
49
+ * their messages are written for exactly this purpose. Any other error is only shown while developing — those messages
50
+ * are internal diagnostics, so in production they stay in captureError's hands.
51
+ *
52
+ * Returns a cleanup function that removes the card again.
53
+ */
54
+ function showSetupErrorOverlay(error) {
55
+ if (!(0, ___in_page_ui_dom_js.canMountIntoDom)()) return () => {};
56
+ if (!_hexclave_shared_dist_utils_errors.HexclaveSetupError.isSetupError(error) && !(0, ___in_page_ui_dev_environment_js.isLikelyDevelopmentEnvironment)()) return () => {};
57
+ const issue = toSetupIssue(error);
58
+ if ((0, ___in_page_ui_dom_js.getGlobalUiInstance)("__hexclave-setup-error-overlay") != null) return () => {};
59
+ const root = (0, ___in_page_ui_dom_js.h)("div", { className: "hexclave-setup-error-overlay" });
60
+ const style = (0, ___in_page_ui_dom_js.h)("style", null, css);
61
+ root.appendChild(style);
62
+ document.body.appendChild(root);
63
+ let focusTrap = null;
64
+ const render = (minimized) => {
65
+ focusTrap?.release();
66
+ focusTrap = null;
67
+ root.replaceChildren(style);
68
+ if (minimized) {
69
+ root.appendChild((0, ___in_page_ui_issue_card_js.renderIssuePill)({
70
+ kind: "error",
71
+ label: "Setup error",
72
+ ariaLabel: "Show Hexclave setup error",
73
+ onClick: () => render(false)
74
+ }));
75
+ return;
76
+ }
77
+ root.appendChild((0, ___in_page_ui_issue_card_js.renderIssueCard)({
78
+ kind: "error",
79
+ badge: "Setup error",
80
+ title: issue.title,
81
+ bodyText: issue.isFatal ? "Hexclave stopped the authentication flow that ran into this error, so nobody can sign in through it until it is fixed." : "Hexclave aborted the authentication flow that ran into this error. Errors like this one may also be transient, in which case retrying the flow works.",
82
+ messageLabel: "Error message",
83
+ message: issue.message,
84
+ howToFix: issue.howToFix,
85
+ footerText: issue.isFatal ? "This card is shown by the Hexclave SDK. Fix the problem above and reload the page to try the flow again." : "This card is shown by the Hexclave SDK while developing, and never in production.",
86
+ ariaLabel: "Hexclave setup error",
87
+ aiPrompt: buildAiPrompt(issue),
88
+ onCopyError: (copyError) => (0, _hexclave_shared_dist_utils_errors.captureError)("setup-error-overlay-copy", copyError),
89
+ onCopyAiPromptError: (copyError) => (0, _hexclave_shared_dist_utils_errors.captureError)("setup-error-overlay-copy-ai-prompt", copyError),
90
+ onMinimize: () => render(true)
91
+ }));
92
+ focusTrap = (0, ___in_page_ui_issue_card_js.trapFocusInIssueCard)(root);
93
+ };
94
+ render(false);
95
+ const cleanup = () => {
96
+ focusTrap?.release();
97
+ focusTrap = null;
98
+ root.remove();
99
+ if ((0, ___in_page_ui_dom_js.getGlobalUiInstance)("__hexclave-setup-error-overlay")?.cleanup === cleanup) (0, ___in_page_ui_dom_js.setGlobalUiInstance)(SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY, null);
100
+ };
101
+ (0, ___in_page_ui_dom_js.setGlobalUiInstance)(SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY, { cleanup });
102
+ return cleanup;
103
+ }
104
+ //#endregion
105
+ exports.SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY = SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY;
106
+ exports.showSetupErrorOverlay = showSetupErrorOverlay;
107
+
108
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["HexclaveSetupError"],"sources":["../../src/setup-error-overlay/index.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\n// Renders an error card on the page when an auth flow dies because of a setup problem in the developer's project (a\n// domain missing from the trusted domains, for example). Those failures used to be logged with captureError only, which\n// meant the flow just quietly stopped: the developer saw a page that never signed anybody in, with the explanation\n// buried in the console or in Sentry. Setup errors are always the developer's to fix and never recoverable at runtime,\n// so they get the same treatment as a broken pushed config: a card that is impossible to miss.\n\nimport { HexclaveSetupError, captureError, errorToNiceString } from \"@hexclave/shared/dist/utils/errors\";\nimport { isLikelyDevelopmentEnvironment } from \"../in-page-ui/dev-environment\";\nimport { canMountIntoDom, getGlobalUiInstance, h, setGlobalUiInstance } from \"../in-page-ui/dom\";\nimport { getIssueCardCSS, renderIssueCard, renderIssuePill, trapFocusInIssueCard, type IssueCardFocusTrap } from \"../in-page-ui/issue-card\";\n\n/** Exported so tests can drop the singleton; every other consumer should go through `showSetupErrorOverlay`. */\nexport const SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY = \"__hexclave-setup-error-overlay\";\n\nconst css = getIssueCardCSS(\".hexclave-setup-error-overlay\");\n\nconst unexpectedErrorHowToFix = [\n \"Check the browser console and your Hexclave dashboard's logs for the full error.\",\n \"Make sure your Hexclave SDK packages are up to date.\",\n \"If the error looks like a bug in Hexclave, report it at https://github.com/hexclave/hexclave/issues.\",\n] as const;\n\ntype SetupIssue = {\n title: string,\n message: string,\n howToFix: readonly string[],\n // Setup errors are known to be fatal for their flow, so the card says so. Anything else may well be transient (a\n // network blip while resolving a session, say), and claiming that nobody can sign in anymore would be misleading.\n isFatal: boolean,\n};\n\nfunction toSetupIssue(error: unknown): SetupIssue {\n if (HexclaveSetupError.isSetupError(error)) {\n return {\n title: error.title,\n message: error.message,\n howToFix: error.howToFix,\n isFatal: true,\n };\n }\n return {\n title: \"Hexclave could not finish an authentication flow\",\n message: errorToNiceString(error),\n howToFix: unexpectedErrorHowToFix,\n isFatal: false,\n };\n}\n\nfunction buildAiPrompt(issue: SetupIssue): string {\n return [\n \"Help me fix this Hexclave setup error.\",\n \"\",\n \"Hexclave reminder:\",\n \"This error was thrown by the Hexclave SDK in the browser while it was resolving an authentication flow, and the flow was aborted because of it. It is a problem with how my project is set up, not something my users can work around. Use your web fetch tool to read https://skill.hexclave.com to learn more about Hexclave.\",\n \"\",\n \"Error message:\",\n issue.message,\n \"\",\n \"Hexclave suggests:\",\n ...issue.howToFix.map((step) => `- ${step}`),\n \"\",\n \"Please explain the likely cause and suggest the smallest safe fix.\",\n ].join(\"\\n\");\n}\n\n/**\n * Shows `error` as an error card on the current page.\n *\n * Setup errors are always shown, because the flow they aborted cannot complete no matter what the end user does, and\n * their messages are written for exactly this purpose. Any other error is only shown while developing — those messages\n * are internal diagnostics, so in production they stay in captureError's hands.\n *\n * Returns a cleanup function that removes the card again.\n */\nexport function showSetupErrorOverlay(error: unknown): () => void {\n if (!canMountIntoDom()) {\n return () => {};\n }\n if (!HexclaveSetupError.isSetupError(error) && !isLikelyDevelopmentEnvironment()) {\n return () => {};\n }\n\n const issue = toSetupIssue(error);\n const existingInstance = getGlobalUiInstance(SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY);\n if (existingInstance != null) {\n // A single broken setup usually throws the same error on every page load and sometimes from several flows at once.\n // Stacking cards on top of each other would only hide the first (and most relevant) one, so later errors keep to\n // captureError and leave the card that is already up alone.\n return () => {};\n }\n\n const root = h(\"div\", { className: \"hexclave-setup-error-overlay\" });\n const style = h(\"style\", null, css);\n root.appendChild(style);\n document.body.appendChild(root);\n\n let focusTrap: IssueCardFocusTrap | null = null;\n const render = (minimized: boolean) => {\n focusTrap?.release();\n focusTrap = null;\n root.replaceChildren(style);\n if (minimized) {\n root.appendChild(renderIssuePill({\n kind: \"error\",\n label: \"Setup error\",\n ariaLabel: \"Show Hexclave setup error\",\n onClick: () => render(false),\n }));\n return;\n }\n root.appendChild(renderIssueCard({\n kind: \"error\",\n badge: \"Setup error\",\n title: issue.title,\n bodyText: issue.isFatal\n ? \"Hexclave stopped the authentication flow that ran into this error, so nobody can sign in through it until it is fixed.\"\n : \"Hexclave aborted the authentication flow that ran into this error. Errors like this one may also be transient, in which case retrying the flow works.\",\n messageLabel: \"Error message\",\n message: issue.message,\n howToFix: issue.howToFix,\n footerText: issue.isFatal\n ? \"This card is shown by the Hexclave SDK. Fix the problem above and reload the page to try the flow again.\"\n : \"This card is shown by the Hexclave SDK while developing, and never in production.\",\n ariaLabel: \"Hexclave setup error\",\n aiPrompt: buildAiPrompt(issue),\n onCopyError: (copyError) => captureError(\"setup-error-overlay-copy\", copyError),\n onCopyAiPromptError: (copyError) => captureError(\"setup-error-overlay-copy-ai-prompt\", copyError),\n onMinimize: () => render(true),\n }));\n focusTrap = trapFocusInIssueCard(root);\n };\n render(false);\n\n const cleanup = () => {\n focusTrap?.release();\n focusTrap = null;\n root.remove();\n if (getGlobalUiInstance(SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY)?.cleanup === cleanup) {\n setGlobalUiInstance(SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY, null);\n }\n };\n setGlobalUiInstance(SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY, { cleanup });\n return cleanup;\n}\n\n"],"mappings":";;;;;;;AAiBA,MAAa,0CAA0C;AAEvD,MAAM,OAAA,GAAA,4BAAA,gBAAA,CAAsB,+BAA+B;AAE3D,MAAM,0BAA0B;CAC9B;CACA;CACA;AACF;AAWA,SAAS,aAAa,OAA4B;CAChD,IAAIA,mCAAAA,mBAAmB,aAAa,KAAK,GACvC,OAAO;EACL,OAAO,MAAM;EACb,SAAS,MAAM;EACf,UAAU,MAAM;EAChB,SAAS;CACX;CAEF,OAAO;EACL,OAAO;EACP,UAAA,GAAA,mCAAA,kBAAA,CAA2B,KAAK;EAChC,UAAU;EACV,SAAS;CACX;AACF;AAEA,SAAS,cAAc,OAA2B;CAChD,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA,MAAM;EACN;EACA;EACA,GAAG,MAAM,SAAS,KAAK,SAAS,KAAK,MAAM;EAC3C;EACA;CACF,CAAC,CAAC,KAAK,IAAI;AACb;;;;;;;;;;AAWA,SAAgB,sBAAsB,OAA4B;CAChE,IAAI,EAAA,GAAA,qBAAA,gBAAA,CAAiB,GACnB,aAAa,CAAC;CAEhB,IAAI,CAACA,mCAAAA,mBAAmB,aAAa,KAAK,KAAK,EAAA,GAAA,iCAAA,+BAAA,CAAgC,GAC7E,aAAa,CAAC;CAGhB,MAAM,QAAQ,aAAa,KAAK;CAEhC,KAAA,GAAA,qBAAA,oBAAA,CAAA,gCAAmB,KAAK,MAItB,aAAa,CAAC;CAGhB,MAAM,QAAA,GAAA,qBAAA,EAAA,CAAS,OAAO,EAAE,WAAW,+BAA+B,CAAC;CACnE,MAAM,SAAA,GAAA,qBAAA,EAAA,CAAU,SAAS,MAAM,GAAG;CAClC,KAAK,YAAY,KAAK;CACtB,SAAS,KAAK,YAAY,IAAI;CAE9B,IAAI,YAAuC;CAC3C,MAAM,UAAU,cAAuB;EACrC,WAAW,QAAQ;EACnB,YAAY;EACZ,KAAK,gBAAgB,KAAK;EAC1B,IAAI,WAAW;GACb,KAAK,aAAA,GAAA,4BAAA,gBAAA,CAA4B;IAC/B,MAAM;IACN,OAAO;IACP,WAAW;IACX,eAAe,OAAO,KAAK;GAC7B,CAAC,CAAC;GACF;EACF;EACA,KAAK,aAAA,GAAA,4BAAA,gBAAA,CAA4B;GAC/B,MAAM;GACN,OAAO;GACP,OAAO,MAAM;GACb,UAAU,MAAM,UACZ,2HACA;GACJ,cAAc;GACd,SAAS,MAAM;GACf,UAAU,MAAM;GAChB,YAAY,MAAM,UACd,6GACA;GACJ,WAAW;GACX,UAAU,cAAc,KAAK;GAC7B,cAAc,eAAA,GAAA,mCAAA,aAAA,CAA2B,4BAA4B,SAAS;GAC9E,sBAAsB,eAAA,GAAA,mCAAA,aAAA,CAA2B,sCAAsC,SAAS;GAChG,kBAAkB,OAAO,IAAI;EAC/B,CAAC,CAAC;EACF,aAAA,GAAA,4BAAA,qBAAA,CAAiC,IAAI;CACvC;CACA,OAAO,KAAK;CAEZ,MAAM,gBAAgB;EACpB,WAAW,QAAQ;EACnB,YAAY;EACZ,KAAK,OAAO;EACZ,KAAA,GAAA,qBAAA,oBAAA,CAAA,gCAA+D,CAAC,EAAE,YAAY,SAC5E,CAAA,GAAA,qBAAA,oBAAA,CAAoB,yCAAyC,IAAI;CAErE;CACA,CAAA,GAAA,qBAAA,oBAAA,CAAoB,yCAAyC,EAAE,QAAQ,CAAC;CACxE,OAAO;AACT"}
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,124 @@
1
+ let _hexclave_shared_dist_utils_errors = require("@hexclave/shared/dist/utils/errors");
2
+ let vitest = require("vitest");
3
+ let __index_js = require("./index.js");
4
+ //#region src/setup-error-overlay/index.test.ts
5
+ // @vitest-environment jsdom
6
+ function createSetupError() {
7
+ return new _hexclave_shared_dist_utils_errors.HexclaveSetupError({
8
+ title: "A domain in your authentication flow is not one of your project's trusted domains",
9
+ message: "Nested cross-domain auth callback URL https://demo.example.test/ is not trusted.",
10
+ howToFix: ["Add https://demo.example.test to your project's trusted domains."]
11
+ });
12
+ }
13
+ function overlayRoots() {
14
+ return document.querySelectorAll(".hexclave-setup-error-overlay");
15
+ }
16
+ (0, vitest.describe)("setup error overlay", () => {
17
+ (0, vitest.afterEach)(() => {
18
+ vitest.vi.unstubAllEnvs();
19
+ for (const root of overlayRoots()) root.remove();
20
+ Reflect.deleteProperty(window, __index_js.SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY);
21
+ });
22
+ (0, vitest.it)("renders setup errors with their fix instructions, even outside development", () => {
23
+ vitest.vi.stubEnv("NODE_ENV", "production");
24
+ const error = createSetupError();
25
+ const cleanup = (0, __index_js.showSetupErrorOverlay)(error);
26
+ try {
27
+ (0, vitest.expect)(overlayRoots()).toHaveLength(1);
28
+ const text = document.body.textContent;
29
+ (0, vitest.expect)(text).toContain(error.title);
30
+ (0, vitest.expect)(text).toContain(error.message);
31
+ (0, vitest.expect)(text).toContain(error.howToFix[0]);
32
+ (0, vitest.expect)(document.querySelector("[role='alertdialog']")).not.toBeNull();
33
+ } finally {
34
+ cleanup();
35
+ }
36
+ (0, vitest.expect)(overlayRoots()).toHaveLength(0);
37
+ });
38
+ (0, vitest.it)("minimizes into a pill and back", () => {
39
+ vitest.vi.stubEnv("NODE_ENV", "production");
40
+ const cleanup = (0, __index_js.showSetupErrorOverlay)(createSetupError());
41
+ try {
42
+ const minimizeButton = document.querySelector("[aria-label='Minimize Hexclave setup error']");
43
+ (0, vitest.expect)(minimizeButton).not.toBeNull();
44
+ minimizeButton?.click();
45
+ (0, vitest.expect)(document.querySelector("[role='alertdialog']")).toBeNull();
46
+ const pill = document.querySelector("[aria-label='Show Hexclave setup error']");
47
+ (0, vitest.expect)(pill).not.toBeNull();
48
+ pill?.click();
49
+ (0, vitest.expect)(document.querySelector("[role='alertdialog']")).not.toBeNull();
50
+ } finally {
51
+ cleanup();
52
+ }
53
+ });
54
+ (0, vitest.it)("keeps keyboard focus inside the card and gives it back afterwards", () => {
55
+ vitest.vi.stubEnv("NODE_ENV", "production");
56
+ const pageButton = document.body.appendChild(document.createElement("button"));
57
+ pageButton.focus();
58
+ const cleanup = (0, __index_js.showSetupErrorOverlay)(createSetupError());
59
+ try {
60
+ const card = document.querySelector("[role='alertdialog']");
61
+ (0, vitest.expect)(document.activeElement).toBe(card);
62
+ const cardButtons = [...card?.querySelectorAll("button") ?? []];
63
+ (0, vitest.expect)(cardButtons.length).toBeGreaterThan(1);
64
+ cardButtons[cardButtons.length - 1].focus();
65
+ document.dispatchEvent(new KeyboardEvent("keydown", {
66
+ key: "Tab",
67
+ bubbles: true,
68
+ cancelable: true
69
+ }));
70
+ (0, vitest.expect)(document.activeElement).toBe(cardButtons[0]);
71
+ document.dispatchEvent(new KeyboardEvent("keydown", {
72
+ key: "Tab",
73
+ bubbles: true,
74
+ cancelable: true,
75
+ shiftKey: true
76
+ }));
77
+ (0, vitest.expect)(document.activeElement).toBe(cardButtons[cardButtons.length - 1]);
78
+ pageButton.focus();
79
+ document.dispatchEvent(new KeyboardEvent("keydown", {
80
+ key: "Tab",
81
+ bubbles: true,
82
+ cancelable: true
83
+ }));
84
+ (0, vitest.expect)(document.activeElement).toBe(cardButtons[0]);
85
+ } finally {
86
+ cleanup();
87
+ }
88
+ (0, vitest.expect)(document.activeElement).toBe(pageButton);
89
+ pageButton.remove();
90
+ });
91
+ (0, vitest.it)("keeps the first card instead of stacking later errors on top of it", () => {
92
+ vitest.vi.stubEnv("NODE_ENV", "production");
93
+ const firstError = createSetupError();
94
+ const cleanup = (0, __index_js.showSetupErrorOverlay)(firstError);
95
+ try {
96
+ (0, __index_js.showSetupErrorOverlay)(new _hexclave_shared_dist_utils_errors.HexclaveSetupError({
97
+ title: "Another setup error",
98
+ message: "Another setup error happened.",
99
+ howToFix: ["Fix the other thing."]
100
+ }))();
101
+ (0, vitest.expect)(overlayRoots()).toHaveLength(1);
102
+ (0, vitest.expect)(document.body.textContent).toContain(firstError.message);
103
+ (0, vitest.expect)(document.body.textContent).not.toContain("Another setup error happened.");
104
+ } finally {
105
+ cleanup();
106
+ }
107
+ });
108
+ (0, vitest.it)("only shows errors that are not setup errors while developing", () => {
109
+ vitest.vi.stubEnv("NODE_ENV", "production");
110
+ (0, __index_js.showSetupErrorOverlay)(/* @__PURE__ */ new Error("Some internal failure."))();
111
+ (0, vitest.expect)(overlayRoots()).toHaveLength(0);
112
+ vitest.vi.stubEnv("NODE_ENV", "development");
113
+ const cleanup = (0, __index_js.showSetupErrorOverlay)(/* @__PURE__ */ new Error("Some internal failure."));
114
+ try {
115
+ (0, vitest.expect)(overlayRoots()).toHaveLength(1);
116
+ (0, vitest.expect)(document.body.textContent).toContain("Some internal failure.");
117
+ } finally {
118
+ cleanup();
119
+ }
120
+ });
121
+ });
122
+ //#endregion
123
+
124
+ //# sourceMappingURL=index.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.js","names":["HexclaveSetupError","SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY"],"sources":["../../src/setup-error-overlay/index.test.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n// @vitest-environment jsdom\n\nimport { HexclaveSetupError } from \"@hexclave/shared/dist/utils/errors\";\nimport { afterEach, describe, expect, it, vi } from \"vitest\";\nimport { SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY, showSetupErrorOverlay } from \".\";\n\nfunction createSetupError() {\n return new HexclaveSetupError({\n title: \"A domain in your authentication flow is not one of your project's trusted domains\",\n message: \"Nested cross-domain auth callback URL https://demo.example.test/ is not trusted.\",\n howToFix: [\"Add https://demo.example.test to your project's trusted domains.\"],\n });\n}\n\nfunction overlayRoots() {\n return document.querySelectorAll(\".hexclave-setup-error-overlay\");\n}\n\ndescribe(\"setup error overlay\", () => {\n afterEach(() => {\n vi.unstubAllEnvs();\n for (const root of overlayRoots()) {\n root.remove();\n }\n Reflect.deleteProperty(window, SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY);\n });\n\n it(\"renders setup errors with their fix instructions, even outside development\", () => {\n vi.stubEnv(\"NODE_ENV\", \"production\");\n const error = createSetupError();\n\n const cleanup = showSetupErrorOverlay(error);\n try {\n expect(overlayRoots()).toHaveLength(1);\n const text = document.body.textContent;\n expect(text).toContain(error.title);\n expect(text).toContain(error.message);\n expect(text).toContain(error.howToFix[0]);\n expect(document.querySelector(\"[role='alertdialog']\")).not.toBeNull();\n } finally {\n cleanup();\n }\n\n expect(overlayRoots()).toHaveLength(0);\n });\n\n it(\"minimizes into a pill and back\", () => {\n vi.stubEnv(\"NODE_ENV\", \"production\");\n const cleanup = showSetupErrorOverlay(createSetupError());\n try {\n const minimizeButton = document.querySelector<HTMLButtonElement>(\"[aria-label='Minimize Hexclave setup error']\");\n expect(minimizeButton).not.toBeNull();\n minimizeButton?.click();\n expect(document.querySelector(\"[role='alertdialog']\")).toBeNull();\n\n const pill = document.querySelector<HTMLButtonElement>(\"[aria-label='Show Hexclave setup error']\");\n expect(pill).not.toBeNull();\n pill?.click();\n expect(document.querySelector(\"[role='alertdialog']\")).not.toBeNull();\n } finally {\n cleanup();\n }\n });\n\n it(\"keeps keyboard focus inside the card and gives it back afterwards\", () => {\n vi.stubEnv(\"NODE_ENV\", \"production\");\n const pageButton = document.body.appendChild(document.createElement(\"button\"));\n pageButton.focus();\n\n const cleanup = showSetupErrorOverlay(createSetupError());\n try {\n const card = document.querySelector<HTMLElement>(\"[role='alertdialog']\");\n expect(document.activeElement).toBe(card);\n\n const cardButtons = [...(card?.querySelectorAll(\"button\") ?? [])];\n expect(cardButtons.length).toBeGreaterThan(1);\n cardButtons[cardButtons.length - 1].focus();\n document.dispatchEvent(new KeyboardEvent(\"keydown\", { key: \"Tab\", bubbles: true, cancelable: true }));\n expect(document.activeElement).toBe(cardButtons[0]);\n document.dispatchEvent(new KeyboardEvent(\"keydown\", { key: \"Tab\", bubbles: true, cancelable: true, shiftKey: true }));\n expect(document.activeElement).toBe(cardButtons[cardButtons.length - 1]);\n\n // Tabbing away from the card is what the trap exists to prevent, even when the page itself moves focus.\n pageButton.focus();\n document.dispatchEvent(new KeyboardEvent(\"keydown\", { key: \"Tab\", bubbles: true, cancelable: true }));\n expect(document.activeElement).toBe(cardButtons[0]);\n } finally {\n cleanup();\n }\n\n expect(document.activeElement).toBe(pageButton);\n pageButton.remove();\n });\n\n it(\"keeps the first card instead of stacking later errors on top of it\", () => {\n vi.stubEnv(\"NODE_ENV\", \"production\");\n const firstError = createSetupError();\n const cleanup = showSetupErrorOverlay(firstError);\n try {\n const secondCleanup = showSetupErrorOverlay(new HexclaveSetupError({\n title: \"Another setup error\",\n message: \"Another setup error happened.\",\n howToFix: [\"Fix the other thing.\"],\n }));\n secondCleanup();\n\n expect(overlayRoots()).toHaveLength(1);\n expect(document.body.textContent).toContain(firstError.message);\n expect(document.body.textContent).not.toContain(\"Another setup error happened.\");\n } finally {\n cleanup();\n }\n });\n\n it(\"only shows errors that are not setup errors while developing\", () => {\n vi.stubEnv(\"NODE_ENV\", \"production\");\n showSetupErrorOverlay(new Error(\"Some internal failure.\"))();\n expect(overlayRoots()).toHaveLength(0);\n\n vi.stubEnv(\"NODE_ENV\", \"development\");\n const cleanup = showSetupErrorOverlay(new Error(\"Some internal failure.\"));\n try {\n expect(overlayRoots()).toHaveLength(1);\n expect(document.body.textContent).toContain(\"Some internal failure.\");\n } finally {\n cleanup();\n }\n });\n});\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"team-invitation-B5Ol7vdd.d.ts","names":[],"sources":["../src/components-page/team-invitation.tsx"],"mappings":";iBAyEgB,cAAA;EAAiB,QAAA;EAAgB;AAAA;EAAkB,QAAA;EAAoB,YAAA,EAAc,MAAA;AAAA,gCAAwB,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"team-invitation-B5Ol7vdd.d.ts","names":[],"sources":["../src/components-page/team-invitation.tsx"],"mappings":";iBA2EgB,cAAA;EAAiB,QAAA;EAAgB;AAAA;EAAkB,QAAA;EAAoB,YAAA,EAAc,MAAA;AAAA,gCAAwB,GAAA,CAAA,OAAA"}
@@ -1,4 +1,4 @@
1
- import { n as quetzalLocales } from "./quetzal-translations-XeH3zQ9l.js";
1
+ import { n as quetzalLocales } from "./quetzal-translations-B2jT2SWm.js";
2
2
 
3
3
  //#region src/providers/translation-provider.d.ts
4
4
  declare function TranslationProvider({
@@ -12,4 +12,4 @@ declare function TranslationProvider({
12
12
  }): import("react/jsx-runtime").JSX.Element;
13
13
  //#endregion
14
14
  export { TranslationProvider as t };
15
- //# sourceMappingURL=translation-provider-WOaZXqAz.d.ts.map
15
+ //# sourceMappingURL=translation-provider-6505O9P2.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"translation-provider-WOaZXqAz.d.ts","names":[],"sources":["../src/providers/translation-provider.tsx"],"mappings":";;;iBAQgB,mBAAA;EAAsB,IAAA;EAAM,oBAAA;EAAsB;AAAA;EAChE,IAAA,EAAM,UAAA,QAAkB,cAAA,CAAe,GAAA;EACvC,oBAAA,GAAuB,MAAA;EACvB,QAAA,EAAU,KAAA,CAAM,SAAA;AAAA,gCACjB,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"translation-provider-6505O9P2.d.ts","names":[],"sources":["../src/providers/translation-provider.tsx"],"mappings":";;;iBAQgB,mBAAA;EAAsB,IAAA;EAAM,oBAAA;EAAsB;AAAA;EAChE,IAAA,EAAM,UAAA,QAAkB,cAAA,CAAe,GAAA;EACvC,oBAAA,GAAuB,MAAA;EACvB,QAAA,EAAU,KAAA,CAAM,SAAA;AAAA,gCACjB,GAAA,CAAA,OAAA"}
@@ -0,0 +1,7 @@
1
+ //#region src/utils/monkey-patch.d.ts
2
+ declare const NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL = "__stack-no-suspense-boundary-error__";
3
+ declare function isNoSuspenseBoundaryError(value: unknown): boolean;
4
+ declare function ensureMonkeyPatch(): void;
5
+ //#endregion
6
+ export { NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL, ensureMonkeyPatch, isNoSuspenseBoundaryError };
7
+ //# sourceMappingURL=monkey-patch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monkey-patch.d.ts","names":[],"sources":["../../src/utils/monkey-patch.tsx"],"mappings":";cAMa,mCAAA;AAAA,iBAEG,yBAAA,CAA0B,KAAc;AAAA,iBASxC,iBAAA"}
@@ -0,0 +1,23 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _hexclave_shared_dist_utils_globals = require("@hexclave/shared/dist/utils/globals");
3
+ //#region src/utils/monkey-patch.tsx
4
+ const NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL = "__stack-no-suspense-boundary-error__";
5
+ function isNoSuspenseBoundaryError(value) {
6
+ return typeof value === "object" && value !== null && "__noSuspenseBoundarySentinel" in value && value.__noSuspenseBoundarySentinel === "__stack-no-suspense-boundary-error__";
7
+ }
8
+ function ensureMonkeyPatch() {
9
+ (0, _hexclave_shared_dist_utils_globals.createGlobal)("__console-error-monkey-patch__", () => {
10
+ const originalConsoleError = console.error;
11
+ console.error = function(...args) {
12
+ if (args.length === 1 && isNoSuspenseBoundaryError(args[0])) return;
13
+ return originalConsoleError.apply(this, args);
14
+ };
15
+ return true;
16
+ });
17
+ }
18
+ //#endregion
19
+ exports.NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL = NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL;
20
+ exports.ensureMonkeyPatch = ensureMonkeyPatch;
21
+ exports.isNoSuspenseBoundaryError = isNoSuspenseBoundaryError;
22
+
23
+ //# sourceMappingURL=monkey-patch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monkey-patch.js","names":[],"sources":["../../src/utils/monkey-patch.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { createGlobal } from \"@hexclave/shared/dist/utils/globals\";\n\nexport const NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL = \"__stack-no-suspense-boundary-error__\";\n\nexport function isNoSuspenseBoundaryError(value: unknown): boolean {\n return (\n typeof value === \"object\"\n && value !== null\n && \"__noSuspenseBoundarySentinel\" in value\n && value.__noSuspenseBoundarySentinel === NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL\n );\n}\n\nexport function ensureMonkeyPatch() {\n createGlobal(\"__console-error-monkey-patch__\", () => {\n const originalConsoleError = console.error;\n console.error = function (...args: unknown[]) {\n // React's default error handlers log every server error, including the one we intentionally throw to skip SSR.\n // Frameworks that use those defaults (including vanilla React) therefore need this narrow sentinel-based filter.\n if (args.length === 1 && isNoSuspenseBoundaryError(args[0])) {\n return;\n }\n return originalConsoleError.apply(this, args);\n };\n return true;\n });\n}\n"],"mappings":";;;AAMA,MAAa,sCAAsC;AAEnD,SAAgB,0BAA0B,OAAyB;CACjE,OACE,OAAO,UAAU,YACd,UAAU,QACV,kCAAkC,SAClC,MAAM,iCAAA;AAEb;AAEA,SAAgB,oBAAoB;CAClC,CAAA,GAAA,oCAAA,aAAA,CAAa,wCAAwC;EACnD,MAAM,uBAAuB,QAAQ;EACrC,QAAQ,QAAQ,SAAU,GAAG,MAAiB;GAG5C,IAAI,KAAK,WAAW,KAAK,0BAA0B,KAAK,EAAE,GACxD;GAEF,OAAO,qBAAqB,MAAM,MAAM,IAAI;EAC9C;EACA,OAAO;CACT,CAAC;AACH"}
@@ -0,0 +1,17 @@
1
+ //#region src/utils/react.d.ts
2
+ declare function shouldRethrowRenderingError(error: unknown): boolean;
3
+ declare class NoSuspenseBoundaryError extends Error {
4
+ digest: string;
5
+ reason: string;
6
+ __noSuspenseBoundarySentinel: string;
7
+ constructor(options: {
8
+ caller?: string;
9
+ });
10
+ }
11
+ /**
12
+ * Use this in a component or hook to disable SSR. It must be wrapped in a Suspense boundary.
13
+ */
14
+ declare function suspendIfSsr(caller?: string): void;
15
+ //#endregion
16
+ export { NoSuspenseBoundaryError, shouldRethrowRenderingError, suspendIfSsr };
17
+ //# sourceMappingURL=react.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.d.ts","names":[],"sources":["../../src/utils/react.tsx"],"mappings":";iBAYgB,2BAAA,CAA4B,KAAc;AAAA,cAI7C,uBAAA,SAAgC,KAAK;EAChD,MAAA;EACA,MAAA;EACA,4BAAA;cAEY,OAAA;IAAW,MAAA;EAAA;AAAA;;;;iBA8DT,YAAA,CAAa,MAAe"}
@@ -0,0 +1,63 @@
1
+ "use client";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ let _hexclave_shared_dist_utils_strings = require("@hexclave/shared/dist/utils/strings");
4
+ let next_navigation = require("next/navigation");
5
+ let _hexclave_shared_dist_utils_env = require("@hexclave/shared/dist/utils/env");
6
+ let __monkey_patch_js = require("./monkey-patch.js");
7
+ //#region src/utils/react.tsx
8
+ function shouldRethrowRenderingError(error) {
9
+ return !!error && typeof error === "object" && "digest" in error && error.digest === "BAILOUT_TO_CLIENT_SIDE_RENDERING";
10
+ }
11
+ var NoSuspenseBoundaryError = class extends Error {
12
+ constructor(options) {
13
+ (0, __monkey_patch_js.ensureMonkeyPatch)();
14
+ super(_hexclave_shared_dist_utils_strings.deindent`
15
+ Suspense boundary not found! Read the error message below carefully (or paste it into your AI agent).
16
+
17
+ ${options.caller ?? "This code path"} attempted to display a loading indicator, but didn't find a Suspense boundary above it. Please read the error message below carefully.
18
+
19
+ There are several potential causes:
20
+
21
+ 1. [Next.js] You are missing a loading.tsx file in your app directory. Fix it by adding a loading.tsx file in your app directory.
22
+
23
+ 2. [React] You are missing a <Suspense> boundary in your component. Fix it by wrapping your component (or the entire app) in a <Suspense> component.
24
+
25
+ 3. [Next.js] The component is rendered in the root (outermost) layout.tsx or template.tsx file. Next.js does not wrap those files in a Suspense boundary, even if there is a loading.tsx file in the same folder. To fix it, wrap your layout inside a route group like this:
26
+
27
+ - app
28
+ - - layout.tsx // contains <html> and <body>, alongside providers and other components that don't need ${options.caller ?? "this code path"}
29
+ - - loading.tsx // required for suspense
30
+ - - (main)
31
+ - - - layout.tsx // contains the main layout of your app, like a sidebar or a header, and can use ${options.caller ?? "this code path"}
32
+ - - - route.tsx // your actual main page
33
+ - - - the rest of your app
34
+
35
+ For more information on this approach, see Next's documentation on route groups: https://nextjs.org/docs/app/building-your-application/routing/route-groups
36
+
37
+ 4. You caught this error with try-catch or a custom error boundary. Fix this by rethrowing the error or not catching it in the first place.
38
+
39
+ 5. Your version of Hexclave is too old. Upgrade to the latest version to see if that fixes the issue.
40
+
41
+ See: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout
42
+
43
+ More information on SSR and Suspense boundaries: https://react.dev/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content
44
+ `);
45
+ this.__noSuspenseBoundarySentinel = __monkey_patch_js.NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL;
46
+ this.name = "NoSuspenseBoundaryError";
47
+ this.reason = options.caller ?? "suspendIfSsr()";
48
+ this.digest = "BAILOUT_TO_CLIENT_SIDE_RENDERING";
49
+ }
50
+ };
51
+ /**
52
+ * Use this in a component or hook to disable SSR. It must be wrapped in a Suspense boundary.
53
+ */
54
+ function suspendIfSsr(caller) {
55
+ (0, next_navigation.useSearchParams)();
56
+ if (!(0, _hexclave_shared_dist_utils_env.isBrowserLike)()) throw new NoSuspenseBoundaryError({ caller });
57
+ }
58
+ //#endregion
59
+ exports.NoSuspenseBoundaryError = NoSuspenseBoundaryError;
60
+ exports.shouldRethrowRenderingError = shouldRethrowRenderingError;
61
+ exports.suspendIfSsr = suspendIfSsr;
62
+
63
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.js","names":["NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL"],"sources":["../../src/utils/react.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { isBrowserLike } from \"@hexclave/shared/dist/utils/env\";\nimport { deindent } from \"@hexclave/shared/dist/utils/strings\";\nimport { useSearchParams } from \"next/navigation\"; // THIS_LINE_PLATFORM next\nimport { ensureMonkeyPatch, NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL } from \"./monkey-patch\";\n\nexport function shouldRethrowRenderingError(error: unknown): boolean {\n return !!error && typeof error === \"object\" && \"digest\" in error && error.digest === \"BAILOUT_TO_CLIENT_SIDE_RENDERING\";\n}\n\nexport class NoSuspenseBoundaryError extends Error {\n digest: string;\n reason: string;\n __noSuspenseBoundarySentinel = NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL;\n\n constructor(options: { caller?: string }) {\n ensureMonkeyPatch();\n\n super(deindent`\n Suspense boundary not found! Read the error message below carefully (or paste it into your AI agent).\n\n ${options.caller ?? \"This code path\"} attempted to display a loading indicator, but didn't find a Suspense boundary above it. Please read the error message below carefully.\n \n There are several potential causes:\n \n 1. [Next.js] You are missing a loading.tsx file in your app directory. Fix it by adding a loading.tsx file in your app directory.\n\n 2. [React] You are missing a <Suspense> boundary in your component. Fix it by wrapping your component (or the entire app) in a <Suspense> component.\n\n 3. [Next.js] The component is rendered in the root (outermost) layout.tsx or template.tsx file. Next.js does not wrap those files in a Suspense boundary, even if there is a loading.tsx file in the same folder. To fix it, wrap your layout inside a route group like this:\n\n - app\n - - layout.tsx // contains <html> and <body>, alongside providers and other components that don't need ${options.caller ?? \"this code path\"}\n - - loading.tsx // required for suspense\n - - (main)\n - - - layout.tsx // contains the main layout of your app, like a sidebar or a header, and can use ${options.caller ?? \"this code path\"}\n - - - route.tsx // your actual main page\n - - - the rest of your app\n\n For more information on this approach, see Next's documentation on route groups: https://nextjs.org/docs/app/building-your-application/routing/route-groups\n \n 4. You caught this error with try-catch or a custom error boundary. Fix this by rethrowing the error or not catching it in the first place.\n\n 5. Your version of Hexclave is too old. Upgrade to the latest version to see if that fixes the issue.\n\n See: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout\n\n More information on SSR and Suspense boundaries: https://react.dev/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content\n `);\n\n this.name = \"NoSuspenseBoundaryError\";\n this.reason = options.caller ?? \"suspendIfSsr()\";\n\n // Next recognizes this digest as an intentional request-time CSR bailout and suppresses its normal error reporting.\n this.digest = \"BAILOUT_TO_CLIENT_SIDE_RENDERING\";\n }\n}\nimport.meta.vitest?.test(\"NoSuspenseBoundaryError\", ({ expect }) => {\n const defaultError = new NoSuspenseBoundaryError({});\n expect(defaultError.name).toBe(\"NoSuspenseBoundaryError\");\n expect(defaultError.reason).toBe(\"suspendIfSsr()\");\n expect(defaultError.digest).toBe(\"BAILOUT_TO_CLIENT_SIDE_RENDERING\");\n expect(defaultError.message).toContain(\"This code path attempted to display a loading indicator\");\n\n const customError = new NoSuspenseBoundaryError({ caller: \"CustomComponent\" });\n expect(customError.name).toBe(\"NoSuspenseBoundaryError\");\n expect(customError.reason).toBe(\"CustomComponent\");\n expect(customError.digest).toBe(\"BAILOUT_TO_CLIENT_SIDE_RENDERING\");\n expect(customError.message).toContain(\"CustomComponent attempted to display a loading indicator\");\n expect(customError.message).toContain(\"loading.tsx\");\n expect(customError.message).toContain(\"route groups\");\n expect(customError.message).toContain(\"https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout\");\n});\n\n/**\n * Use this in a component or hook to disable SSR. It must be wrapped in a Suspense boundary.\n */\nexport function suspendIfSsr(caller?: string) {\n // Next 16.3's Instant Insights is a silly goose! It rejects our deliberate recoverable SSR error during\n // prerender validation, even though React uses the surrounding Suspense fallback correctly. Calling\n // useSearchParams first makes Next create its own recognized `prerender-client` dynamic hole, so prerendering\n // stops before our error while request-time SSR still reaches the error and retries this subtree on the client.\n useSearchParams();\n if (!isBrowserLike()) {\n throw new NoSuspenseBoundaryError({ caller });\n }\n}\n"],"mappings":";;;;;;;AAYA,SAAgB,4BAA4B,OAAyB;CACnE,OAAO,CAAC,CAAC,SAAS,OAAO,UAAU,YAAY,YAAY,SAAS,MAAM,WAAW;AACvF;AAEA,IAAa,0BAAb,cAA6C,MAAM;CAKjD,YAAY,SAA8B;EACxC,CAAA,GAAA,kBAAA,kBAAA,CAAkB;EAElB,MAAM,oCAAA,QAAQ;;;QAGV,QAAQ,UAAU,iBAAiB;;;;;;;;;;;kHAWuE,QAAQ,UAAU,iBAAiB;;;6GAGxC,QAAQ,UAAU,iBAAiB;;;;;;;;;;;;;KAa3I;sCAnC4BA,kBAAAA;EAqC7B,KAAK,OAAO;EACZ,KAAK,SAAS,QAAQ,UAAU;EAGhC,KAAK,SAAS;CAChB;AACF;;;;AAqBA,SAAgB,aAAa,QAAiB;CAK5C,CAAA,GAAA,gBAAA,gBAAA,CAAgB;CAChB,IAAI,EAAA,GAAA,gCAAA,cAAA,CAAe,GACjB,MAAM,IAAI,wBAAwB,EAAE,OAAO,CAAC;AAEhD"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)",
3
3
  "name": "@hexclave/next",
4
- "version": "1.0.75",
4
+ "version": "1.0.77",
5
5
  "repository": "https://github.com/hexclave/hexclave",
6
6
  "sideEffects": false,
7
7
  "main": "./dist/index.js",
@@ -74,9 +74,9 @@
74
74
  "tailwindcss-animate": "^1.0.7",
75
75
  "rrweb": "^1.1.3",
76
76
  "yup": "^1.7.1",
77
- "@hexclave/sc": "1.0.75",
78
- "@hexclave/ui": "1.0.75",
79
- "@hexclave/shared": "1.0.75"
77
+ "@hexclave/sc": "1.0.77",
78
+ "@hexclave/ui": "1.0.77",
79
+ "@hexclave/shared": "1.0.77"
80
80
  },
81
81
  "peerDependencies": {
82
82
  "@types/react": ">=18.0.0",
@@ -5,11 +5,11 @@
5
5
  // THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template
6
6
  //===========================================
7
7
 
8
- import { useHash } from '@hexclave/shared/dist/hooks/use-hash';
9
8
  import { Button, Typography, cn } from '@hexclave/ui';
10
9
  import { XIcon } from 'lucide-react';
11
10
  import React, { ReactNode } from 'react';
12
11
  import { useStackApp } from '../..';
12
+ import { suspendIfSsr } from '../../utils/react';
13
13
 
14
14
  export type SidebarItem = {
15
15
  title: React.ReactNode,
@@ -21,6 +21,21 @@ export type SidebarItem = {
21
21
  contentTitle?: React.ReactNode,
22
22
  }
23
23
 
24
+ function normalizeHash(hash: string) {
25
+ return hash.startsWith("#") ? hash.slice(1) : hash;
26
+ }
27
+
28
+ function useHash() {
29
+ suspendIfSsr();
30
+ return React.useSyncExternalStore(
31
+ (onStoreChange) => {
32
+ window.addEventListener("hashchange", onStoreChange);
33
+ return () => window.removeEventListener("hashchange", onStoreChange);
34
+ },
35
+ () => normalizeHash(window.location.hash),
36
+ );
37
+ }
38
+
24
39
  export function SidebarLayout(props: { items: SidebarItem[], title?: ReactNode, className?: string }) {
25
40
  const hash = useHash();
26
41
  const selectedIndex = props.items.findIndex(item => item.id && (item.id === hash));
@@ -0,0 +1,25 @@
1
+
2
+ //===========================================
3
+ // THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template
4
+ //===========================================
5
+ import { describe, expect, it, vi } from "vitest";
6
+ import { cacheTeamInvitationOperation } from "./team-invitation-cache";
7
+
8
+ describe("cacheTeamInvitationOperation", () => {
9
+ it("reuses results within a session but not across sessions", async () => {
10
+ let resultNumber = 0;
11
+ const operation = vi.fn(async (_app: object, _code: string) => {
12
+ resultNumber += 1;
13
+ return resultNumber;
14
+ });
15
+ const cachedOperation = cacheTeamInvitationOperation(operation);
16
+ const app = {};
17
+ const firstSession = {};
18
+ const secondSession = {};
19
+
20
+ await expect(cachedOperation(app, firstSession, "shared-code")).resolves.toBe(1);
21
+ await expect(cachedOperation(app, firstSession, "shared-code")).resolves.toBe(1);
22
+ await expect(cachedOperation(app, secondSession, "shared-code")).resolves.toBe(2);
23
+ expect(operation).toHaveBeenCalledTimes(2);
24
+ });
25
+ });
@@ -0,0 +1,13 @@
1
+
2
+ //===========================================
3
+ // THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template
4
+ //===========================================
5
+ import { cacheFunction } from "@hexclave/shared/dist/utils/caches";
6
+
7
+ export function cacheTeamInvitationOperation<TApp extends object, TResult>(
8
+ operation: (app: TApp, code: string) => Promise<TResult>,
9
+ ): (app: TApp, session: object, code: string) => Promise<TResult> {
10
+ return cacheFunction(async (app: TApp, _session: object, code: string) => {
11
+ return await operation(app, code);
12
+ });
13
+ }
@@ -6,7 +6,6 @@
6
6
  //===========================================
7
7
 
8
8
  import { KnownErrors } from "@hexclave/shared";
9
- import { cacheFunction } from "@hexclave/shared/dist/utils/caches";
10
9
  import { runAsynchronouslyWithAlert } from "@hexclave/shared/dist/utils/promises";
11
10
  import { use } from "@hexclave/shared/dist/utils/react";
12
11
  import { Typography } from "@hexclave/ui";
@@ -14,21 +13,24 @@ import React from "react";
14
13
  import { MessageCard, StackClientApp, useStackApp, useUser } from "..";
15
14
  import { PredefinedMessageCard } from "../components/message-cards/predefined-message-card";
16
15
  import { useTranslation } from "../lib/translations";
16
+ import { cacheTeamInvitationOperation } from "./team-invitation-cache";
17
17
 
18
- const cachedVerifyInvitation = cacheFunction(async (hexclaveApp: StackClientApp<true>, code: string) => {
18
+ // Both endpoints inspect the current account, so the session must participate in
19
+ // the key even though the SDK app object and invitation code are otherwise stable.
20
+ const cachedVerifyInvitation = cacheTeamInvitationOperation(async (hexclaveApp: StackClientApp<true>, code: string) => {
19
21
  return await hexclaveApp.verifyTeamInvitationCode(code);
20
22
  });
21
23
 
22
- const cachedGetInvitationDetails = cacheFunction(async (hexclaveApp: StackClientApp<true>, code: string) => {
24
+ const cachedGetInvitationDetails = cacheTeamInvitationOperation(async (hexclaveApp: StackClientApp<true>, code: string) => {
23
25
  return await hexclaveApp.getTeamInvitationDetails(code);
24
26
  });
25
27
 
26
- function TeamInvitationInner(props: { fullPage?: boolean, searchParams: Record<string, string> }) {
28
+ function TeamInvitationInner(props: { fullPage?: boolean, searchParams: Record<string, string>, session: object }) {
27
29
  const { t } = useTranslation();
28
30
  const hexclaveApp = useStackApp();
29
31
  const [success, setSuccess] = React.useState(false);
30
32
  const [errorMessage, setErrorMessage] = React.useState<string | null>(null);
31
- const details = use(cachedGetInvitationDetails(hexclaveApp, props.searchParams.code || ''));
33
+ const details = use(cachedGetInvitationDetails(hexclaveApp, props.session, props.searchParams.code || ''));
32
34
 
33
35
  if (errorMessage || details.status === 'error') {
34
36
  return (
@@ -131,7 +133,7 @@ export function TeamInvitation({ fullPage=false, searchParams }: { fullPage?: bo
131
133
  );
132
134
  }
133
135
 
134
- const verificationResult = use(cachedVerifyInvitation(hexclaveApp, searchParams.code || ''));
136
+ const verificationResult = use(cachedVerifyInvitation(hexclaveApp, user._internalSession, searchParams.code || ''));
135
137
 
136
138
  if (verificationResult.status === 'error') {
137
139
  const error = verificationResult.error;
@@ -146,5 +148,12 @@ export function TeamInvitation({ fullPage=false, searchParams }: { fullPage?: bo
146
148
  }
147
149
  }
148
150
 
149
- return <TeamInvitationInner fullPage={fullPage} searchParams={searchParams} />;
151
+ return (
152
+ <TeamInvitationInner
153
+ key={user.id}
154
+ fullPage={fullPage}
155
+ searchParams={searchParams}
156
+ session={user._internalSession}
157
+ />
158
+ );
150
159
  };