@hexclave/next 1.0.76 → 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 (155) hide show
  1. package/dist/{admin-app-impl-CsH-gVGz.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/api-key-dialogs.d.ts +1 -1
  9. package/dist/components/elements/sidebar-layout.d.ts.map +1 -1
  10. package/dist/components/elements/sidebar-layout.js +12 -2
  11. package/dist/components/elements/sidebar-layout.js.map +1 -1
  12. package/dist/components/selected-team-switcher.d.ts +1 -1
  13. package/dist/components/team-icon.d.ts +1 -1
  14. package/dist/components/team-switcher.d.ts +1 -1
  15. package/dist/components-page/account-settings/payments/payments-page.d.ts +1 -1
  16. package/dist/components-page/account-settings/teams/leave-team-section.d.ts +1 -1
  17. package/dist/components-page/account-settings/teams/team-api-keys-section.d.ts +1 -1
  18. package/dist/components-page/account-settings/teams/team-display-name-section.d.ts +1 -1
  19. package/dist/components-page/account-settings/teams/team-member-invitation-section.d.ts +1 -1
  20. package/dist/components-page/account-settings/teams/team-member-list-section.d.ts +1 -1
  21. package/dist/components-page/account-settings/teams/team-page.d.ts +1 -1
  22. package/dist/components-page/account-settings/teams/team-profile-image-section.d.ts +1 -1
  23. package/dist/components-page/account-settings/teams/team-profile-user-section.d.ts +1 -1
  24. package/dist/components-page/hexclave-handler-client.d.ts +1 -1
  25. package/dist/components-page/hexclave-handler.d.ts +1 -1
  26. package/dist/components-page/team-invitation-cache.d.ts +5 -0
  27. package/dist/components-page/team-invitation-cache.d.ts.map +1 -0
  28. package/dist/components-page/team-invitation-cache.js +12 -0
  29. package/dist/components-page/team-invitation-cache.js.map +1 -0
  30. package/dist/components-page/team-invitation-cache.test.d.ts +1 -0
  31. package/dist/components-page/team-invitation-cache.test.js +23 -0
  32. package/dist/components-page/team-invitation-cache.test.js.map +1 -0
  33. package/dist/components-page/team-invitation.js +8 -7
  34. package/dist/components-page/team-invitation.js.map +1 -1
  35. package/dist/dev-tool/dev-tool-core.d.ts +1 -1
  36. package/dist/dev-tool/index.d.ts +1 -1
  37. package/dist/esm/components/api-key-dialogs.d.ts +1 -1
  38. package/dist/esm/components/elements/sidebar-layout.d.ts.map +1 -1
  39. package/dist/esm/components/elements/sidebar-layout.js +12 -2
  40. package/dist/esm/components/elements/sidebar-layout.js.map +1 -1
  41. package/dist/esm/components-page/team-invitation-cache.d.ts +5 -0
  42. package/dist/esm/components-page/team-invitation-cache.d.ts.map +1 -0
  43. package/dist/esm/components-page/team-invitation-cache.js +11 -0
  44. package/dist/esm/components-page/team-invitation-cache.js.map +1 -0
  45. package/dist/esm/components-page/team-invitation-cache.test.d.ts +1 -0
  46. package/dist/esm/components-page/team-invitation-cache.test.js +24 -0
  47. package/dist/esm/components-page/team-invitation-cache.test.js.map +1 -0
  48. package/dist/esm/components-page/team-invitation.d.ts.map +1 -1
  49. package/dist/esm/components-page/team-invitation.js +8 -7
  50. package/dist/esm/components-page/team-invitation.js.map +1 -1
  51. package/dist/esm/generated/quetzal-translations.d.ts +2 -2
  52. package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +4 -4
  53. package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
  54. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js +30 -1
  55. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js.map +1 -1
  56. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
  57. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +5 -3
  58. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
  59. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js +94 -21
  60. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
  61. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.d.ts +1 -0
  62. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js +83 -0
  63. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js.map +1 -0
  64. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.d.ts +1 -0
  65. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js +120 -0
  66. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js.map +1 -0
  67. package/dist/esm/lib/hexclave-app/apps/implementations/common.d.ts.map +1 -1
  68. package/dist/esm/lib/hexclave-app/apps/implementations/common.js +4 -3
  69. package/dist/esm/lib/hexclave-app/apps/implementations/common.js.map +1 -1
  70. package/dist/esm/lib/hexclave-app/apps/implementations/index.d.ts +1 -1
  71. package/dist/esm/lib/hexclave-app/apps/implementations/index.js +1 -1
  72. package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +2 -2
  73. package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.js +1 -1
  74. package/dist/esm/lib/hexclave-app/teams/index.d.ts +3 -0
  75. package/dist/esm/lib/hexclave-app/teams/index.d.ts.map +1 -1
  76. package/dist/esm/lib/hexclave-app/teams/index.js.map +1 -1
  77. package/dist/esm/utils/monkey-patch.d.ts +7 -0
  78. package/dist/esm/utils/monkey-patch.d.ts.map +1 -0
  79. package/dist/esm/utils/monkey-patch.js +20 -0
  80. package/dist/esm/utils/monkey-patch.js.map +1 -0
  81. package/dist/esm/utils/react.d.ts +17 -0
  82. package/dist/esm/utils/react.d.ts.map +1 -0
  83. package/dist/esm/utils/react.js +60 -0
  84. package/dist/esm/utils/react.js.map +1 -0
  85. package/dist/generated/quetzal-translations.d.ts +1 -1
  86. package/dist/{hexclave-provider-BA97jMlx.d.ts → hexclave-provider-CMz2mt4I.d.ts} +3 -3
  87. package/dist/{hexclave-provider-BA97jMlx.d.ts.map → hexclave-provider-CMz2mt4I.d.ts.map} +1 -1
  88. package/dist/{hooks-CUe5bc3s.d.ts → hooks-CUHt4Y9C.d.ts} +2 -2
  89. package/dist/{hooks-CUe5bc3s.d.ts.map → hooks-CUHt4Y9C.d.ts.map} +1 -1
  90. package/dist/{index-CvB7XacB.d.ts → index-fR0LkUK7.d.ts} +2 -2
  91. package/dist/{index-CvB7XacB.d.ts.map → index-fR0LkUK7.d.ts.map} +1 -1
  92. package/dist/index.d.ts +4 -4
  93. package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -1
  94. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js +29 -0
  95. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.js.map +1 -1
  96. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
  97. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +2 -2
  98. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js +93 -19
  99. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
  100. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.d.ts +1 -0
  101. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js +82 -0
  102. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.js.map +1 -0
  103. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.d.ts +1 -0
  104. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js +119 -0
  105. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.js.map +1 -0
  106. package/dist/lib/hexclave-app/apps/implementations/common.js +4 -3
  107. package/dist/lib/hexclave-app/apps/implementations/common.js.map +1 -1
  108. package/dist/lib/hexclave-app/apps/implementations/index.d.ts +1 -1
  109. package/dist/lib/hexclave-app/apps/implementations/index.js +1 -1
  110. package/dist/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
  111. package/dist/lib/hexclave-app/apps/implementations/server-app-impl.js +1 -1
  112. package/dist/lib/hexclave-app/apps/index.d.ts +1 -1
  113. package/dist/lib/hexclave-app/apps/interfaces/admin-app.d.ts +1 -1
  114. package/dist/lib/hexclave-app/apps/interfaces/client-app.d.ts +1 -1
  115. package/dist/lib/hexclave-app/apps/interfaces/server-app.d.ts +1 -1
  116. package/dist/lib/hexclave-app/index.d.ts +1 -1
  117. package/dist/lib/hexclave-app/projects/index.d.ts +1 -1
  118. package/dist/lib/hexclave-app/teams/index.d.ts +1 -1
  119. package/dist/lib/hexclave-app/teams/index.js.map +1 -1
  120. package/dist/lib/hexclave-app/users/index.d.ts +1 -1
  121. package/dist/lib/hooks.d.ts +1 -1
  122. package/dist/providers/hexclave-context.d.ts +1 -1
  123. package/dist/providers/hexclave-provider-client.d.ts +1 -1
  124. package/dist/providers/hexclave-provider.d.ts +1 -1
  125. package/dist/providers/translation-provider.d.ts +1 -1
  126. package/dist/pushed-config-error-overlay/index.d.ts +1 -1
  127. package/dist/{quetzal-translations-CK45_4Dm.d.ts → quetzal-translations-B2jT2SWm.d.ts} +3 -3
  128. package/dist/{quetzal-translations-CK45_4Dm.d.ts.map → quetzal-translations-B2jT2SWm.d.ts.map} +1 -1
  129. package/dist/team-invitation-B5Ol7vdd.d.ts.map +1 -1
  130. package/dist/{translation-provider-DQ3BJepJ.d.ts → translation-provider-6505O9P2.d.ts} +2 -2
  131. package/dist/{translation-provider-DQ3BJepJ.d.ts.map → translation-provider-6505O9P2.d.ts.map} +1 -1
  132. package/dist/utils/monkey-patch.d.ts +7 -0
  133. package/dist/utils/monkey-patch.d.ts.map +1 -0
  134. package/dist/utils/monkey-patch.js +23 -0
  135. package/dist/utils/monkey-patch.js.map +1 -0
  136. package/dist/utils/react.d.ts +17 -0
  137. package/dist/utils/react.d.ts.map +1 -0
  138. package/dist/utils/react.js +63 -0
  139. package/dist/utils/react.js.map +1 -0
  140. package/package.json +4 -4
  141. package/src/components/elements/sidebar-layout.tsx +16 -1
  142. package/src/components-page/team-invitation-cache.test.ts +25 -0
  143. package/src/components-page/team-invitation-cache.ts +13 -0
  144. package/src/components-page/team-invitation.tsx +16 -7
  145. package/src/lib/hexclave-app/apps/implementations/client-app-impl.automatic-side-effects.test.ts +38 -1
  146. package/src/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.ts +2 -3
  147. package/src/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.ts +96 -0
  148. package/src/lib/hexclave-app/apps/implementations/client-app-impl.ts +134 -24
  149. package/src/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.ts +146 -0
  150. package/src/lib/hexclave-app/apps/implementations/common.ts +2 -1
  151. package/src/lib/hexclave-app/teams/index.ts +3 -0
  152. package/src/utils/monkey-patch.tsx +31 -0
  153. package/src/utils/react.tsx +93 -0
  154. package/dist/admin-app-impl-CsH-gVGz.d.ts.map +0 -1
  155. package/dist/client-app-BCT5p_70.d.ts.map +0 -1
@@ -0,0 +1,83 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { StackClientApp } from "../interfaces/client-app.js";
3
+ import { AsyncStore } from "@hexclave/shared/dist/utils/stores";
4
+ //#region src/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.test.ts
5
+ function createAccessTokenString(refreshTokenId) {
6
+ const encode = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
7
+ const nowSeconds = Math.floor(Date.now() / 1e3);
8
+ return [
9
+ encode({
10
+ alg: "none",
11
+ typ: "JWT"
12
+ }),
13
+ encode({
14
+ sub: "user-id",
15
+ exp: nowSeconds + 60,
16
+ iat: nowSeconds,
17
+ refresh_token_id: refreshTokenId
18
+ }),
19
+ ""
20
+ ].join(".");
21
+ }
22
+ var Deferred = class {
23
+ constructor() {
24
+ this._resolve = () => {
25
+ throw new Error("Deferred promise was resolved before initialization");
26
+ };
27
+ this.promise = new Promise((resolve) => {
28
+ this._resolve = resolve;
29
+ });
30
+ }
31
+ resolve() {
32
+ this._resolve();
33
+ }
34
+ };
35
+ describe("StackClientApp sign-out", () => {
36
+ it("delays current-user reads until redirect dispatch without blocking unrelated stores", async () => {
37
+ const clientApp = new StackClientApp({
38
+ baseUrl: "http://localhost:12345",
39
+ projectId: "00000000-0000-4000-8000-000000000000",
40
+ publishableClientKey: "stack-pk-test",
41
+ tokenStore: "memory",
42
+ redirectMethod: "none",
43
+ noAutomaticPrefetch: true,
44
+ devTool: false
45
+ });
46
+ const clientInterface = Reflect.get(clientApp, "_interface");
47
+ Reflect.set(clientInterface, "getClientUserByToken", async () => ({
48
+ id: "user-id",
49
+ is_anonymous: false,
50
+ is_restricted: false
51
+ }));
52
+ await Reflect.get(clientApp, "_signInToAccountWithTokens").call(clientApp, {
53
+ accessToken: createAccessTokenString("refresh-token-id"),
54
+ refreshToken: "refresh-token"
55
+ });
56
+ const createCookieHelper = Reflect.get(clientApp, "_createCookieHelper");
57
+ const getOrCreateTokenStore = Reflect.get(clientApp, "_getOrCreateTokenStore");
58
+ const getSessionFromTokenStore = Reflect.get(clientApp, "_getSessionFromTokenStore");
59
+ const tokenStore = getOrCreateTokenStore.call(clientApp, await createCookieHelper.call(clientApp));
60
+ const session = getSessionFromTokenStore.call(clientApp, tokenStore);
61
+ Reflect.set(clientInterface, "signOut", async () => {
62
+ session.markInvalid();
63
+ });
64
+ const redirectStarted = new Deferred();
65
+ const redirectFinished = new Deferred();
66
+ Reflect.set(clientApp, "_redirectToDefaultAfterSignOut", async () => {
67
+ redirectStarted.resolve();
68
+ await redirectFinished.promise;
69
+ });
70
+ expect(new AsyncStore().setAsync(new Promise(() => {}))).toBeInstanceOf(Promise);
71
+ const signOutPromise = Reflect.get(clientApp, "_signOut").call(clientApp, session);
72
+ await redirectStarted.promise;
73
+ const getUserDuringSignOut = clientApp.getUser();
74
+ expect(await Promise.race([getUserDuringSignOut.then(() => "resolved"), Promise.resolve("pending")])).toBe("pending");
75
+ redirectFinished.resolve();
76
+ await signOutPromise;
77
+ await expect(getUserDuringSignOut).resolves.toBeNull();
78
+ });
79
+ });
80
+ //#endregion
81
+ export {};
82
+
83
+ //# sourceMappingURL=client-app-impl.sign-out.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-app-impl.sign-out.test.js","names":[],"sources":["../../../../../../src/lib/hexclave-app/apps/implementations/client-app-impl.sign-out.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//===========================================\nimport { AsyncStore } from \"@hexclave/shared/dist/utils/stores\";\nimport { describe, expect, it } from \"vitest\";\nimport { StackClientApp } from \"../interfaces/client-app\";\n\nfunction createAccessTokenString(refreshTokenId: string): string {\n const encode = (value: unknown) => Buffer.from(JSON.stringify(value)).toString(\"base64url\");\n const nowSeconds = Math.floor(Date.now() / 1000);\n return [\n encode({ alg: \"none\", typ: \"JWT\" }),\n encode({ sub: \"user-id\", exp: nowSeconds + 60, iat: nowSeconds, refresh_token_id: refreshTokenId }),\n \"\",\n ].join(\".\");\n}\n\nclass Deferred {\n readonly promise: Promise<void>;\n private _resolve: () => void = () => {\n throw new Error(\"Deferred promise was resolved before initialization\");\n };\n\n constructor() {\n this.promise = new Promise<void>((resolve) => {\n this._resolve = resolve;\n });\n }\n\n resolve(): void {\n this._resolve();\n }\n}\n\ndescribe(\"StackClientApp sign-out\", () => {\n it(\"delays current-user reads until redirect dispatch without blocking unrelated stores\", async () => {\n const clientApp = new StackClientApp({\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: \"memory\",\n redirectMethod: \"none\",\n noAutomaticPrefetch: true,\n devTool: false,\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n Reflect.set(clientInterface, \"getClientUserByToken\", async () => ({\n id: \"user-id\",\n is_anonymous: false,\n is_restricted: false,\n }));\n\n const signInToAccountWithTokens = Reflect.get(clientApp, \"_signInToAccountWithTokens\");\n await signInToAccountWithTokens.call(clientApp, {\n accessToken: createAccessTokenString(\"refresh-token-id\"),\n refreshToken: \"refresh-token\",\n });\n\n const createCookieHelper = Reflect.get(clientApp, \"_createCookieHelper\");\n const getOrCreateTokenStore = Reflect.get(clientApp, \"_getOrCreateTokenStore\");\n const getSessionFromTokenStore = Reflect.get(clientApp, \"_getSessionFromTokenStore\");\n const tokenStore = getOrCreateTokenStore.call(clientApp, await createCookieHelper.call(clientApp));\n const session = getSessionFromTokenStore.call(clientApp, tokenStore);\n\n Reflect.set(clientInterface, \"signOut\", async () => {\n session.markInvalid();\n });\n\n const redirectStarted = new Deferred();\n const redirectFinished = new Deferred();\n Reflect.set(clientApp, \"_redirectToDefaultAfterSignOut\", async () => {\n redirectStarted.resolve();\n await redirectFinished.promise;\n });\n\n const unrelatedStore = new AsyncStore<void>();\n expect(unrelatedStore.setAsync(new Promise<void>(() => {}))).toBeInstanceOf(Promise);\n\n const signOut = Reflect.get(clientApp, \"_signOut\");\n const signOutPromise = signOut.call(clientApp, session);\n await redirectStarted.promise;\n\n const getUserDuringSignOut = clientApp.getUser();\n const stateBeforeRedirectFinished = await Promise.race([\n getUserDuringSignOut.then(() => \"resolved\"),\n Promise.resolve(\"pending\"),\n ]);\n expect(stateBeforeRedirectFinished).toBe(\"pending\");\n\n redirectFinished.resolve();\n await signOutPromise;\n\n await expect(getUserDuringSignOut).resolves.toBeNull();\n });\n});\n"],"mappings":";;;;AAQA,SAAS,wBAAwB,gBAAgC;CAC/D,MAAM,UAAU,UAAmB,OAAO,KAAK,KAAK,UAAU,KAAK,CAAC,CAAC,CAAC,SAAS,WAAW;CAC1F,MAAM,aAAa,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;CAC/C,OAAO;EACL,OAAO;GAAE,KAAK;GAAQ,KAAK;EAAM,CAAC;EAClC,OAAO;GAAE,KAAK;GAAW,KAAK,aAAa;GAAI,KAAK;GAAY,kBAAkB;EAAe,CAAC;EAClG;CACF,CAAC,CAAC,KAAK,GAAG;AACZ;AAEA,IAAM,WAAN,MAAe;CAMb,cAAc;wBAJuB;GACnC,MAAM,IAAI,MAAM,qDAAqD;EACvE;EAGE,KAAK,UAAU,IAAI,SAAe,YAAY;GAC5C,KAAK,WAAW;EAClB,CAAC;CACH;CAEA,UAAgB;EACd,KAAK,SAAS;CAChB;AACF;AAEA,SAAS,iCAAiC;CACxC,GAAG,uFAAuF,YAAY;EACpG,MAAM,YAAY,IAAI,eAAe;GACnC,SAAS;GACT,WAAW;GACX,sBAAsB;GACtB,YAAY;GACZ,gBAAgB;GAChB,qBAAqB;GACrB,SAAS;EACX,CAAC;EACD,MAAM,kBAAkB,QAAQ,IAAI,WAAW,YAAY;EAC3D,QAAQ,IAAI,iBAAiB,wBAAwB,aAAa;GAChE,IAAI;GACJ,cAAc;GACd,eAAe;EACjB,EAAE;EAGF,MADkC,QAAQ,IAAI,WAAW,4BAC3B,CAAC,CAAC,KAAK,WAAW;GAC9C,aAAa,wBAAwB,kBAAkB;GACvD,cAAc;EAChB,CAAC;EAED,MAAM,qBAAqB,QAAQ,IAAI,WAAW,qBAAqB;EACvE,MAAM,wBAAwB,QAAQ,IAAI,WAAW,wBAAwB;EAC7E,MAAM,2BAA2B,QAAQ,IAAI,WAAW,2BAA2B;EACnF,MAAM,aAAa,sBAAsB,KAAK,WAAW,MAAM,mBAAmB,KAAK,SAAS,CAAC;EACjG,MAAM,UAAU,yBAAyB,KAAK,WAAW,UAAU;EAEnE,QAAQ,IAAI,iBAAiB,WAAW,YAAY;GAClD,QAAQ,YAAY;EACtB,CAAC;EAED,MAAM,kBAAkB,IAAI,SAAS;EACrC,MAAM,mBAAmB,IAAI,SAAS;EACtC,QAAQ,IAAI,WAAW,kCAAkC,YAAY;GACnE,gBAAgB,QAAQ;GACxB,MAAM,iBAAiB;EACzB,CAAC;EAGD,OAAO,IADoB,WACP,CAAC,CAAC,SAAS,IAAI,cAAoB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,OAAO;EAGnF,MAAM,iBADU,QAAQ,IAAI,WAAW,UACV,CAAC,CAAC,KAAK,WAAW,OAAO;EACtD,MAAM,gBAAgB;EAEtB,MAAM,uBAAuB,UAAU,QAAQ;EAK/C,OAAO,MAJmC,QAAQ,KAAK,CACrD,qBAAqB,WAAW,UAAU,GAC1C,QAAQ,QAAQ,SAAS,CAC3B,CAAC,CACiC,CAAC,CAAC,KAAK,SAAS;EAElD,iBAAiB,QAAQ;EACzB,MAAM;EAEN,MAAM,OAAO,oBAAoB,CAAC,CAAC,SAAS,SAAS;CACvD,CAAC;AACH,CAAC"}
@@ -0,0 +1,120 @@
1
+ import { HexclaveSetupError } from "@hexclave/shared/dist/utils/errors";
2
+ import { KnownErrors } from "@hexclave/shared";
3
+ import { afterEach, describe, expect, it } from "vitest";
4
+ import { StackClientApp } from "../interfaces/client-app.js";
5
+ import { AccessToken } from "@hexclave/shared/dist/sessions";
6
+ import { SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY } from "../../../../setup-error-overlay/index.js";
7
+ import { getGlobalUiInstance, setGlobalUiInstance } from "../../../../in-page-ui/dom.js";
8
+ //#region src/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.test.ts
9
+ // @vitest-environment jsdom
10
+ function createAccessTokenString(refreshTokenId) {
11
+ const encode = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
12
+ const nowSeconds = Math.floor(Date.now() / 1e3);
13
+ return [
14
+ encode({
15
+ alg: "none",
16
+ typ: "JWT"
17
+ }),
18
+ encode({
19
+ sub: "user-id",
20
+ exp: nowSeconds + 60,
21
+ iat: nowSeconds,
22
+ iss: "https://api.example.test",
23
+ aud: "project-id",
24
+ project_id: "project-id",
25
+ branch_id: "main",
26
+ refresh_token_id: refreshTokenId,
27
+ role: "authenticated",
28
+ name: null,
29
+ email: null,
30
+ email_verified: false,
31
+ selected_team_id: null,
32
+ signed_up_at: nowSeconds,
33
+ is_anonymous: false,
34
+ is_restricted: false,
35
+ restricted_reason: null,
36
+ requires_totp_mfa: false
37
+ }),
38
+ ""
39
+ ].join(".");
40
+ }
41
+ function overlayRoots() {
42
+ return document.querySelectorAll(".hexclave-setup-error-overlay");
43
+ }
44
+ function createAppWithFailingHandoff(options) {
45
+ const app = new StackClientApp({
46
+ baseUrl: "http://localhost:12345",
47
+ projectId: options.projectId,
48
+ publishableClientKey: "stack-pk-test",
49
+ tokenStore: "memory",
50
+ redirectMethod: "none",
51
+ noAutomaticPrefetch: true,
52
+ devTool: false
53
+ });
54
+ const accessToken = createAccessTokenString("source-refresh-token-id");
55
+ const clientInterface = Reflect.get(app, "_interface");
56
+ Reflect.set(clientInterface, "fetchNewAccessToken", async () => {
57
+ return AccessToken.createIfValid(accessToken) ?? (() => {
58
+ throw new Error("Expected the test access token to be valid.");
59
+ })();
60
+ });
61
+ Reflect.set(clientInterface, "sendClientRequest", options.sendClientRequest);
62
+ return app;
63
+ }
64
+ function createCrossDomainAuthRedirectUrl(app, redirectUri) {
65
+ const method = Reflect.get(app, "_createCrossDomainAuthRedirectUrl");
66
+ if (typeof method !== "function") throw new Error("Expected StackClientApp to expose _createCrossDomainAuthRedirectUrl in tests.");
67
+ return method.call(app, {
68
+ redirectUri,
69
+ state: "handoff-state",
70
+ codeChallenge: "abcdefghijklmnopqrstuvwxyzABCDEFG_0123456789-._~",
71
+ afterCallbackRedirectUrl: redirectUri,
72
+ overrideTokenStoreInit: {
73
+ accessToken: createAccessTokenString("source-refresh-token-id"),
74
+ refreshToken: "source-refresh-token"
75
+ }
76
+ });
77
+ }
78
+ describe("cross-domain handoff rejected by the server", () => {
79
+ afterEach(() => {
80
+ getGlobalUiInstance(SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY)?.cleanup();
81
+ setGlobalUiInstance(SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY, null);
82
+ for (const root of overlayRoots()) root.remove();
83
+ });
84
+ it("shows a setup error card even when the caller swallows the redirect failure", async () => {
85
+ const untrustedUrl = "https://demo.example.test/?hexclave_cross_domain_auth=1";
86
+ const app = createAppWithFailingHandoff({
87
+ projectId: "00000000-0000-4000-8000-000000000101",
88
+ sendClientRequest: async () => {
89
+ throw new KnownErrors.RedirectUrlNotWhitelisted(untrustedUrl);
90
+ }
91
+ });
92
+ let caught = null;
93
+ try {
94
+ await createCrossDomainAuthRedirectUrl(app, untrustedUrl);
95
+ } catch (error) {
96
+ caught = error;
97
+ }
98
+ if (!HexclaveSetupError.isSetupError(caught)) throw new Error(`Expected the rejected handoff to throw a setup error, got: ${caught}`);
99
+ expect(caught.message).toBe("Cross-domain auth redirect URL https://demo.example.test/ is not trusted.");
100
+ expect(Reflect.get(caught, "customCaptureExtraArgs")).toMatchObject([{ url: untrustedUrl }]);
101
+ expect(caught.cause).toBeInstanceOf(KnownErrors.RedirectUrlNotWhitelisted);
102
+ expect(overlayRoots()).toHaveLength(1);
103
+ const cardText = document.body.textContent;
104
+ expect(cardText).toContain("A domain in your authentication flow is not one of your project's trusted domains");
105
+ expect(cardText).toContain("add it to your project's trusted domains in the Hexclave dashboard, under Domains.");
106
+ });
107
+ it("leaves other handoff failures to captureError", async () => {
108
+ await expect(createCrossDomainAuthRedirectUrl(createAppWithFailingHandoff({
109
+ projectId: "00000000-0000-4000-8000-000000000102",
110
+ sendClientRequest: async () => {
111
+ throw new KnownErrors.UserAuthenticationRequired();
112
+ }
113
+ }), "https://demo.example.test/")).rejects.toSatisfy((error) => KnownErrors.UserAuthenticationRequired.isInstance(error));
114
+ expect(overlayRoots()).toHaveLength(0);
115
+ });
116
+ });
117
+ //#endregion
118
+ export {};
119
+
120
+ //# sourceMappingURL=client-app-impl.untrusted-redirect-card.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-app-impl.untrusted-redirect-card.test.js","names":[],"sources":["../../../../../../src/lib/hexclave-app/apps/implementations/client-app-impl.untrusted-redirect-card.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 { KnownErrors } from \"@hexclave/shared\";\nimport { AccessToken } from \"@hexclave/shared/dist/sessions\";\nimport { HexclaveSetupError } from \"@hexclave/shared/dist/utils/errors\";\nimport { afterEach, describe, expect, it } from \"vitest\";\nimport { getGlobalUiInstance, setGlobalUiInstance } from \"../../../../in-page-ui/dom\";\nimport { SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY } from \"../../../../setup-error-overlay\";\nimport { StackClientApp } from \"../interfaces/client-app\";\n\nfunction createAccessTokenString(refreshTokenId: string): string {\n const encode = (value: unknown) => Buffer.from(JSON.stringify(value)).toString(\"base64url\");\n const nowSeconds = Math.floor(Date.now() / 1000);\n return [\n encode({ alg: \"none\", typ: \"JWT\" }),\n encode({\n sub: \"user-id\",\n exp: nowSeconds + 60,\n iat: nowSeconds,\n iss: \"https://api.example.test\",\n aud: \"project-id\",\n project_id: \"project-id\",\n branch_id: \"main\",\n refresh_token_id: refreshTokenId,\n role: \"authenticated\",\n name: null,\n email: null,\n email_verified: false,\n selected_team_id: null,\n signed_up_at: nowSeconds,\n is_anonymous: false,\n is_restricted: false,\n restricted_reason: null,\n requires_totp_mfa: false,\n }),\n \"\",\n ].join(\".\");\n}\n\nfunction overlayRoots(): NodeListOf<Element> {\n return document.querySelectorAll(\".hexclave-setup-error-overlay\");\n}\n\nfunction createAppWithFailingHandoff(options: {\n projectId: string,\n sendClientRequest: () => Promise<never>,\n}): StackClientApp<true> {\n const app = new StackClientApp({\n baseUrl: \"http://localhost:12345\",\n projectId: options.projectId,\n publishableClientKey: \"stack-pk-test\",\n tokenStore: \"memory\",\n redirectMethod: \"none\",\n noAutomaticPrefetch: true,\n devTool: false,\n });\n const accessToken = createAccessTokenString(\"source-refresh-token-id\");\n const clientInterface = Reflect.get(app, \"_interface\");\n Reflect.set(clientInterface, \"fetchNewAccessToken\", async () => {\n return AccessToken.createIfValid(accessToken) ?? (() => {\n throw new Error(\"Expected the test access token to be valid.\");\n })();\n });\n Reflect.set(clientInterface, \"sendClientRequest\", options.sendClientRequest);\n return app;\n}\n\nfunction createCrossDomainAuthRedirectUrl(app: StackClientApp<true>, redirectUri: string): Promise<unknown> {\n const method = Reflect.get(app, \"_createCrossDomainAuthRedirectUrl\");\n if (typeof method !== \"function\") {\n throw new Error(\"Expected StackClientApp to expose _createCrossDomainAuthRedirectUrl in tests.\");\n }\n return method.call(app, {\n redirectUri,\n state: \"handoff-state\",\n codeChallenge: \"abcdefghijklmnopqrstuvwxyzABCDEFG_0123456789-._~\",\n afterCallbackRedirectUrl: redirectUri,\n overrideTokenStoreInit: {\n accessToken: createAccessTokenString(\"source-refresh-token-id\"),\n refreshToken: \"source-refresh-token\",\n },\n });\n}\n\ndescribe(\"cross-domain handoff rejected by the server\", () => {\n afterEach(() => {\n // The card's own cleanup, so that the focus trap it installed is released too and does not follow the next test.\n getGlobalUiInstance(SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY)?.cleanup();\n setGlobalUiInstance(SETUP_ERROR_OVERLAY_GLOBAL_INSTANCE_KEY, null);\n for (const root of overlayRoots()) {\n root.remove();\n }\n });\n\n it(\"shows a setup error card even when the caller swallows the redirect failure\", async () => {\n const untrustedUrl = \"https://demo.example.test/?hexclave_cross_domain_auth=1\";\n const app = createAppWithFailingHandoff({\n projectId: \"00000000-0000-4000-8000-000000000101\",\n sendClientRequest: async () => {\n throw new KnownErrors.RedirectUrlNotWhitelisted(untrustedUrl);\n },\n });\n\n // The hosted auth pages catch a failing post-auth redirect and render their own \"we could not continue\n // automatically, please try again\" screen, which is exactly how this error used to disappear: nothing on the page\n // said that the flow is dead until a domain is added to the project's trusted domains.\n let caught: unknown = null;\n try {\n await createCrossDomainAuthRedirectUrl(app, untrustedUrl);\n } catch (error) {\n caught = error;\n }\n\n if (!HexclaveSetupError.isSetupError(caught)) {\n throw new Error(`Expected the rejected handoff to throw a setup error, got: ${caught}`);\n }\n // The handoff query parameters have nothing to do with the rejection, so the message keeps only the checked part.\n expect(caught.message).toBe(\"Cross-domain auth redirect URL https://demo.example.test/ is not trusted.\");\n // The full URL is still handed to the error sinks, which is where `customCaptureExtraArgs` ends up.\n expect(Reflect.get(caught, \"customCaptureExtraArgs\")).toMatchObject([{ url: untrustedUrl }]);\n expect(caught.cause).toBeInstanceOf(KnownErrors.RedirectUrlNotWhitelisted);\n\n expect(overlayRoots()).toHaveLength(1);\n const cardText = document.body.textContent;\n expect(cardText).toContain(\"A domain in your authentication flow is not one of your project's trusted domains\");\n expect(cardText).toContain(\"add it to your project's trusted domains in the Hexclave dashboard, under Domains.\");\n });\n\n it(\"leaves other handoff failures to captureError\", async () => {\n const app = createAppWithFailingHandoff({\n projectId: \"00000000-0000-4000-8000-000000000102\",\n sendClientRequest: async () => {\n throw new KnownErrors.UserAuthenticationRequired();\n },\n });\n\n await expect(createCrossDomainAuthRedirectUrl(app, \"https://demo.example.test/\")).rejects.toSatisfy(\n (error: unknown) => KnownErrors.UserAuthenticationRequired.isInstance(error),\n );\n expect(overlayRoots()).toHaveLength(0);\n });\n});\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"common.d.ts","names":[],"sources":["../../../../../../src/lib/hexclave-app/apps/implementations/common.ts"],"mappings":";;;;;;;cAqBa,aAAA;AAAA,cAgDA,WAAA,uBAAmC,OAAA,GAAU,YAAA,EAAc,CAAA,KAAM,OAAA,CAAQ,CAAA,MAAE,UAAA,CAAA,CAAA,EAAA,MAAA,CAAA,CAAA;AAAA,cAO3E,oBAAA,uBAA4C,OAAA,GAAU,OAAA,EAAS,eAAA,EAAiB,iBAAA,EAAmB,CAAA,KAAM,OAAA,CAAQ,CAAA,MAAE,UAAA,EAAA,eAAA,KAAA,CAAA,GAAA,MAAA,CAAA,CAAA;AAAA,KAa3H,OAAA;EAAA,CAAa,0BAA0B;IAAK,qBAAqB;EAAA;AAAA;AAAA,iBACtD,yBAAA;EAAsC,YAAA,GAAe,OAAA;AAAA,GAAW,OAAA,EAAS,CAAA,GAAI,CAAA;EAAM,YAAA;AAAA;AAAA,iBAOnF,OAAA,CAAQ,OAAA,EAAS,iBAAA,EAAmB,OAAA;EAAW,SAAA;AAAA,IAAsB,mBAAmB;AAAA,iBAOxF,mBAAA;AAAA,iBAIA,8BAAA;AAAA,iBAIA,yBAAA;AAAA,iBAIA,6BAAA;AAAA,iBAIA,6BAAA;;;;;;;;;;;AAnDwE;AAOxF;;;;;;;iBAkEgB,UAAA,CAAW,oBAAA;EAAiC,OAAA;EAAiB,MAAA;AAAA;AAAA,cAwBhE,cAAA;AAAA,cACA,uBAAA;AAAA,iBAOG,mBAAA,CAAoB,cAAsB;AAAA,iBA0B1C,cAAA,CAAe,mBAAA;EAAgC,OAAA;EAAiB,MAAA;AAAA;AAAA,KAcpE,WAAA;EACV,WAAA;EACA,YAAY;AAAA;AAAA,iBAGE,qBAAA,IAAqB,KAAA,CAAA,WAAA;AAAA,iBASrB,aAAA,qBAAkC,KAAA,EAAO,UAAA,CAAW,CAAA,EAAG,MAAA,CAAO,CAAA,IAAK,YAAA,EAAc,CAAA,EAAG,MAAA,WAAiB,CAAA"}
1
+ {"version":3,"file":"common.d.ts","names":[],"sources":["../../../../../../src/lib/hexclave-app/apps/implementations/common.ts"],"mappings":";;;;;;;cAsBa,aAAA;AAAA,cAgDA,WAAA,uBAAmC,OAAA,GAAU,YAAA,EAAc,CAAA,KAAM,OAAA,CAAQ,CAAA,MAAE,UAAA,CAAA,CAAA,EAAA,MAAA,CAAA,CAAA;AAAA,cAO3E,oBAAA,uBAA4C,OAAA,GAAU,OAAA,EAAS,eAAA,EAAiB,iBAAA,EAAmB,CAAA,KAAM,OAAA,CAAQ,CAAA,MAAE,UAAA,EAAA,eAAA,KAAA,CAAA,GAAA,MAAA,CAAA,CAAA;AAAA,KAa3H,OAAA;EAAA,CAAa,0BAA0B;IAAK,qBAAqB;EAAA;AAAA;AAAA,iBACtD,yBAAA;EAAsC,YAAA,GAAe,OAAA;AAAA,GAAW,OAAA,EAAS,CAAA,GAAI,CAAA;EAAM,YAAA;AAAA;AAAA,iBAOnF,OAAA,CAAQ,OAAA,EAAS,iBAAA,EAAmB,OAAA;EAAW,SAAA;AAAA,IAAsB,mBAAmB;AAAA,iBAOxF,mBAAA;AAAA,iBAIA,8BAAA;AAAA,iBAIA,yBAAA;AAAA,iBAIA,6BAAA;AAAA,iBAIA,6BAAA;;;;;;;;;;;AAnDwE;AAOxF;;;;;;;iBAkEgB,UAAA,CAAW,oBAAA;EAAiC,OAAA;EAAiB,MAAA;AAAA;AAAA,cAwBhE,cAAA;AAAA,cACA,uBAAA;AAAA,iBAOG,mBAAA,CAAoB,cAAsB;AAAA,iBA0B1C,cAAA,CAAe,mBAAA;EAAgC,OAAA;EAAiB,MAAA;AAAA;AAAA,KAcpE,WAAA;EACV,WAAA;EACA,YAAY;AAAA;AAAA,iBAGE,qBAAA,IAAqB,KAAA,CAAA,WAAA;AAAA,iBASrB,aAAA,qBAAkC,KAAA,EAAO,UAAA,CAAW,CAAA,EAAG,MAAA,CAAO,CAAA,IAAK,YAAA,EAAc,CAAA,EAAG,MAAA,WAAiB,CAAA"}
@@ -2,7 +2,7 @@ import { runAsynchronously } from "@hexclave/shared/dist/utils/promises";
2
2
  import { deindent } from "@hexclave/shared/dist/utils/strings";
3
3
  import { captureError, concatStacktraces, throwErr } from "@hexclave/shared/dist/utils/errors";
4
4
  import React, { useCallback } from "react";
5
- import { suspendIfSsr, use } from "@hexclave/shared/dist/utils/react";
5
+ import { use } from "@hexclave/shared/dist/utils/react";
6
6
  import { filterUndefined, omit } from "@hexclave/shared/dist/utils/objects";
7
7
  import { getDefaultApiUrls } from "@hexclave/shared/dist/utils/urls";
8
8
  import { AsyncCache } from "@hexclave/shared/dist/utils/caches";
@@ -12,10 +12,11 @@ import { createGlobal, getGlobal } from "@hexclave/shared/dist/utils/globals";
12
12
  import { hexclaveAppInternalsSymbol } from "../../common.js";
13
13
  import { Store } from "@hexclave/shared/dist/utils/stores";
14
14
  import { envVars } from "../../../../generated/env.js";
15
+ import { suspendIfSsr } from "../../../../utils/react.js";
15
16
  import { resolveHandlerUrls } from "../../url-targets.js";
16
17
  //#region src/lib/hexclave-app/apps/implementations/common.ts
17
- const clientVersion = "js @hexclave/next@1.0.76";
18
- if ("js @hexclave/next@1.0.76".startsWith("STACK_COMPILE_TIME"));
18
+ const clientVersion = "js @hexclave/next@1.0.77";
19
+ if ("js @hexclave/next@1.0.77".startsWith("STACK_COMPILE_TIME"));
19
20
  const replaceHexclavePortPrefix = (input) => {
20
21
  if (!input) return input;
21
22
  const prefix = envVars.HEXCLAVE_PORT_PREFIX;
@@ -1 +1 @@
1
- {"version":3,"file":"common.js","names":[],"sources":["../../../../../../src/lib/hexclave-app/apps/implementations/common.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//===========================================\nimport { InternalSession } from \"@hexclave/shared/dist/sessions\";\nimport { AsyncCache } from \"@hexclave/shared/dist/utils/caches\";\nimport { isBrowserLike } from \"@hexclave/shared/dist/utils/env\";\nimport { HexclaveAssertionError, captureError, concatStacktraces, throwErr } from \"@hexclave/shared/dist/utils/errors\";\nimport { createGlobal, getGlobal } from \"@hexclave/shared/dist/utils/globals\";\nimport { filterUndefined, omit } from \"@hexclave/shared/dist/utils/objects\";\nimport { ReactPromise, runAsynchronously } from \"@hexclave/shared/dist/utils/promises\";\nimport { suspendIfSsr, use } from \"@hexclave/shared/dist/utils/react\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\nimport { Store } from \"@hexclave/shared/dist/utils/stores\";\nimport { deindent } from \"@hexclave/shared/dist/utils/strings\";\nimport { getDefaultApiUrls } from \"@hexclave/shared/dist/utils/urls\";\nimport React, { useCallback } from \"react\"; // THIS_LINE_PLATFORM react-like\nimport { envVars } from \"../../../../generated/env\";\nimport { HandlerUrlOptions, ResolvedHandlerUrls, hexclaveAppInternalsSymbol } from \"../../common\";\nimport { resolveHandlerUrls } from \"../../url-targets\";\n\nexport const clientVersion = \"STACK_COMPILE_TIME_CLIENT_PACKAGE_VERSION_SENTINEL\";\nif (clientVersion.startsWith(\"STACK_COMPILE_TIME\")) {\n throw new HexclaveAssertionError(\"Client version was not replaced. Something went wrong during build!\");\n}\n\nconst replaceHexclavePortPrefix = <T extends string | undefined>(input: T): T => {\n if (!input) return input;\n const prefix = envVars.HEXCLAVE_PORT_PREFIX;\n return prefix ? input.replace(/\\$\\{NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81\\}/g, prefix) as T : input;\n};\n\nconst showMissingConfigAlertInBrowser = (message: string) => {\n if (!isBrowserLike()) return;\n\n const global = globalThis as any;\n const alertAlreadyShownKey = \"__hexclave_missing_config_alert_already_shown\";\n if (global[alertAlreadyShownKey]) return;\n global[alertAlreadyShownKey] = true;\n\n const alertFn = global.alert;\n if (typeof alertFn === \"function\") {\n alertFn(message);\n }\n};\n\nconst throwMissingProjectIdError = (): never => {\n const message = deindent`\n Welcome to Hexclave!\n \n It seems that you haven't provided a project ID.\n\n ${envVars.NODE_ENV?.includes(\"dev\") ? deindent`\n You are running this app in development mode. Make sure that your start command is wrapped in a call to the Hexclave CLI: \\`npx @hexclave/cli dev --config-file <file> -- <command>\\`\n\n Alternatively, you can also connect to a Cloud project's environment variables instead by creating a project on the Hexclave dashboard at https://app.hexclave.com and put its information in the HEXCLAVE_PROJECT_ID environment variable.\n ` : envVars.NODE_ENV?.includes(\"prod\") ? deindent`\n It seems you are running a project in production. Please create a project on the Hexclave dashboard at https://app.hexclave.com and put it in the HEXCLAVE_PROJECT_ID environment variable.\n ` : deindent`\n - If you are running a development project and would like to connect the local Hexclave dashboard, wrap your start command in: \\`npx @hexclave/cli dev --config-file <file> -- <command>\\`\n\n - If you are running a project in production and would like to connect to Hexclave Cloud, create a project on the Hexclave dashboard at https://app.hexclave.com and put its information in the HEXCLAVE_PROJECT_ID environment variable.\n `}\n `;\n showMissingConfigAlertInBrowser(message);\n return throwErr(new Error(message));\n};\n\n\nexport const createCache = <D extends any[], T>(fetcher: (dependencies: D) => Promise<T>) => {\n return new AsyncCache<D, Result<T>>(\n async (dependencies) => await Result.fromThrowingAsync(async () => await fetcher(dependencies)),\n {},\n );\n};\n\nexport const createCacheBySession = <D extends any[], T>(fetcher: (session: InternalSession, extraDependencies: D) => Promise<T> ) => {\n return new AsyncCache<[InternalSession, ...D], Result<T>>(\n async ([session, ...extraDependencies]) => await Result.fromThrowingAsync(async () => await fetcher(session, extraDependencies)),\n {\n onSubscribe: ([session], refresh) => {\n const handler = session.onInvalidate(() => refresh());\n return () => handler.unsubscribe();\n },\n },\n );\n};\n\n\ntype AppLike = { [hexclaveAppInternalsSymbol]: { getConstructorOptions: () => any } };\nexport function resolveConstructorOptions<T extends { inheritsFrom?: AppLike }>(options: T): T & { inheritsFrom?: undefined } {\n return {\n ...options.inheritsFrom?.[hexclaveAppInternalsSymbol].getConstructorOptions() ?? {},\n ...filterUndefined(omit(options, [\"inheritsFrom\"])),\n };\n}\n\nexport function getUrls(partial: HandlerUrlOptions, options: { projectId: string }): ResolvedHandlerUrls {\n return resolveHandlerUrls({\n urls: partial,\n projectId: options.projectId,\n });\n}\n\nexport function getDefaultProjectId() {\n return envVars.HEXCLAVE_PROJECT_ID || throwMissingProjectIdError();\n}\n\nexport function getDefaultPublishableClientKey() {\n return envVars.HEXCLAVE_PUBLISHABLE_CLIENT_KEY;\n}\n\nexport function getDefaultSecretServerKey() {\n return envVars.HEXCLAVE_SECRET_SERVER_KEY || throwErr(new Error(\"No secret server key provided. Please copy your key from the Hexclave dashboard and put it in the HEXCLAVE_SECRET_SERVER_KEY environment variable.\"));\n}\n\nexport function getDefaultSuperSecretAdminKey() {\n return envVars.HEXCLAVE_SUPER_SECRET_ADMIN_KEY || throwErr(new Error(\"No super secret admin key provided. Please copy your key from the Hexclave dashboard and put it in the HEXCLAVE_SUPER_SECRET_ADMIN_KEY environment variable.\"));\n}\n\nexport function getDefaultExtraRequestHeaders() {\n return JSON.parse(envVars.HEXCLAVE_EXTRA_REQUEST_HEADERS || '{}');\n}\n\n/**\n * Returns the base URL for the Stack API.\n *\n * The URL can be specified in several ways, in order of precedence:\n * 1. Directly through userSpecifiedBaseUrl parameter as string or browser/server object\n * 2. Through environment variables:\n * - Browser: NEXT_PUBLIC_HEXCLAVE_API_URL_BROWSER/VITE_HEXCLAVE_API_URL_BROWSER\n * - Server: HEXCLAVE_API_URL_SERVER\n * - Default: HEXCLAVE_API_URL\n * 3. Default base URL if none of the above are specified\n *\n * The function also ensures the URL doesn't end with a trailing slash\n * by removing it if present.\n *\n * @param userSpecifiedBaseUrl - Optional URL override as string or {browser, server} object\n * @returns The configured base URL without trailing slash\n\n */\nexport function getBaseUrl(userSpecifiedBaseUrl: string | { browser: string, server: string } | undefined) {\n let url;\n if (userSpecifiedBaseUrl) {\n if (typeof userSpecifiedBaseUrl === \"string\") {\n url = userSpecifiedBaseUrl;\n } else {\n if (isBrowserLike()) {\n url = userSpecifiedBaseUrl.browser;\n } else {\n url = userSpecifiedBaseUrl.server;\n }\n }\n } else {\n if (isBrowserLike()) {\n url = envVars.HEXCLAVE_API_URL_BROWSER;\n } else {\n url = envVars.HEXCLAVE_API_URL_SERVER;\n }\n url = url || envVars.HEXCLAVE_API_URL || defaultBaseUrl;\n }\n\n return replaceHexclavePortPrefix(url.endsWith('/') ? url.slice(0, -1) : url);\n}\n\nexport const defaultBaseUrl = \"https://api.hexclave.com\";\nexport const defaultAnalyticsBaseUrl = \"https://r.hexclave.com\";\n\nconst analyticsBaseUrlsByApiBaseUrl = new Map<string, string>([\n [defaultBaseUrl, defaultAnalyticsBaseUrl],\n [\"https://api.stack-auth.com\", \"https://r.stack-auth.com\"],\n]);\n\nexport function getAnalyticsBaseUrl(regularBaseUrl: string): string {\n return analyticsBaseUrlsByApiBaseUrl.get(regularBaseUrl) ?? regularBaseUrl;\n}\n\n\nfunction fetchBackendUrlsInBackground(primaryBaseUrl: string): void {\n createGlobal('__stack-fetch-backend-urls-started', () => {\n runAsynchronously(async () => {\n try {\n const res = await fetch(`${primaryBaseUrl}/api/v1/internal/backend-urls`);\n if (!res.ok) {\n return;\n }\n const data = await res.json();\n if (!Array.isArray(data.urls) || !data.urls.every((u: unknown) => typeof u === 'string')) {\n return;\n }\n createGlobal('__stack-fetched-backend-urls', () => data.urls as string[]);\n } catch (e) {\n captureError('fetch-backend-urls-in-background', e);\n }\n });\n return true;\n });\n}\n\nexport function resolveApiUrls(userExplicitBaseUrl: string | { browser: string, server: string } | undefined): () => string[] {\n return () => {\n if (userExplicitBaseUrl != null) {\n return [getBaseUrl(userExplicitBaseUrl)];\n }\n const primary = getBaseUrl(undefined);\n // Always try to fetch server-configured URLs (supports custom domains via\n // STACK_BACKEND_URLS_CONFIG). Hardcoded fallbacks are used as a default\n // until the background fetch completes.\n fetchBackendUrlsInBackground(primary);\n return getGlobal('__stack-fetched-backend-urls') ?? getDefaultApiUrls(primary);\n };\n}\n\nexport type TokenObject = {\n accessToken: string | null,\n refreshToken: string | null,\n};\n\nexport function createEmptyTokenStore() {\n return new Store<TokenObject>({\n refreshToken: null,\n accessToken: null,\n });\n}\n\n\nconst cachePromiseByHookId = new Map<string, ReactPromise<Result<unknown>>>();\nexport function useAsyncCache<D extends any[], T>(cache: AsyncCache<D, Result<T>>, dependencies: D, caller: string): T {\n // we explicitly don't want to run this hook in SSR\n suspendIfSsr(caller);\n\n // on the dashboard, we do some perf monitoring for pre-fetching which should hook right in here\n const asyncCacheHooks: any[] = getGlobal(\"use-async-cache-execution-hooks\") ?? [];\n for (const hook of asyncCacheHooks) {\n hook({ cache, caller, dependencies });\n }\n\n const id = React.useId();\n\n // whenever the dependencies change, we need to refresh the promise cache\n React.useEffect(() => {\n cachePromiseByHookId.delete(id);\n }, [...dependencies, id]);\n\n const subscribe = useCallback((cb: () => void) => {\n const { unsubscribe } = cache.onStateChange(dependencies, () => {\n cachePromiseByHookId.delete(id);\n cb();\n });\n return unsubscribe;\n }, [cache, ...dependencies]);\n\n const getSnapshot = useCallback(() => {\n // React checks whether a promise passed to `use` is still the same as the previous one by comparing the reference.\n // If we didn't cache here, this wouldn't work because the promise would be recreated every time the value changes.\n if (!cachePromiseByHookId.has(id)) {\n cachePromiseByHookId.set(id, cache.getOrWait(dependencies, \"read-write\"));\n }\n return cachePromiseByHookId.get(id) as ReactPromise<Result<T>>;\n }, [cache, ...dependencies]);\n\n // note: we must use React.useSyncExternalStore instead of importing the function directly, as it will otherwise\n // throw an error on Next.js (\"can't import useSyncExternalStore from the server\")\n const promise = React.useSyncExternalStore(\n subscribe,\n getSnapshot,\n getSnapshot,\n );\n\n const result = use(promise);\n if (result.status === \"error\") {\n const error = result.error;\n if (error instanceof Error && !(error as any).__stackHasConcatenatedStacktraces) {\n concatStacktraces(error, new Error());\n (error as any).__stackHasConcatenatedStacktraces = true;\n }\n throw error;\n }\n return result.data;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"common.js","names":[],"sources":["../../../../../../src/lib/hexclave-app/apps/implementations/common.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//===========================================\nimport { InternalSession } from \"@hexclave/shared/dist/sessions\";\nimport { AsyncCache } from \"@hexclave/shared/dist/utils/caches\";\nimport { isBrowserLike } from \"@hexclave/shared/dist/utils/env\";\nimport { HexclaveAssertionError, captureError, concatStacktraces, throwErr } from \"@hexclave/shared/dist/utils/errors\";\nimport { createGlobal, getGlobal } from \"@hexclave/shared/dist/utils/globals\";\nimport { filterUndefined, omit } from \"@hexclave/shared/dist/utils/objects\";\nimport { ReactPromise, runAsynchronously } from \"@hexclave/shared/dist/utils/promises\";\nimport { use } from \"@hexclave/shared/dist/utils/react\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\nimport { Store } from \"@hexclave/shared/dist/utils/stores\";\nimport { deindent } from \"@hexclave/shared/dist/utils/strings\";\nimport { getDefaultApiUrls } from \"@hexclave/shared/dist/utils/urls\";\nimport React, { useCallback } from \"react\"; // THIS_LINE_PLATFORM react-like\nimport { envVars } from \"../../../../generated/env\";\nimport { suspendIfSsr } from \"../../../../utils/react\"; // THIS_LINE_PLATFORM next react\nimport { HandlerUrlOptions, ResolvedHandlerUrls, hexclaveAppInternalsSymbol } from \"../../common\";\nimport { resolveHandlerUrls } from \"../../url-targets\";\n\nexport const clientVersion = \"STACK_COMPILE_TIME_CLIENT_PACKAGE_VERSION_SENTINEL\";\nif (clientVersion.startsWith(\"STACK_COMPILE_TIME\")) {\n throw new HexclaveAssertionError(\"Client version was not replaced. Something went wrong during build!\");\n}\n\nconst replaceHexclavePortPrefix = <T extends string | undefined>(input: T): T => {\n if (!input) return input;\n const prefix = envVars.HEXCLAVE_PORT_PREFIX;\n return prefix ? input.replace(/\\$\\{NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81\\}/g, prefix) as T : input;\n};\n\nconst showMissingConfigAlertInBrowser = (message: string) => {\n if (!isBrowserLike()) return;\n\n const global = globalThis as any;\n const alertAlreadyShownKey = \"__hexclave_missing_config_alert_already_shown\";\n if (global[alertAlreadyShownKey]) return;\n global[alertAlreadyShownKey] = true;\n\n const alertFn = global.alert;\n if (typeof alertFn === \"function\") {\n alertFn(message);\n }\n};\n\nconst throwMissingProjectIdError = (): never => {\n const message = deindent`\n Welcome to Hexclave!\n \n It seems that you haven't provided a project ID.\n\n ${envVars.NODE_ENV?.includes(\"dev\") ? deindent`\n You are running this app in development mode. Make sure that your start command is wrapped in a call to the Hexclave CLI: \\`npx @hexclave/cli dev --config-file <file> -- <command>\\`\n\n Alternatively, you can also connect to a Cloud project's environment variables instead by creating a project on the Hexclave dashboard at https://app.hexclave.com and put its information in the HEXCLAVE_PROJECT_ID environment variable.\n ` : envVars.NODE_ENV?.includes(\"prod\") ? deindent`\n It seems you are running a project in production. Please create a project on the Hexclave dashboard at https://app.hexclave.com and put it in the HEXCLAVE_PROJECT_ID environment variable.\n ` : deindent`\n - If you are running a development project and would like to connect the local Hexclave dashboard, wrap your start command in: \\`npx @hexclave/cli dev --config-file <file> -- <command>\\`\n\n - If you are running a project in production and would like to connect to Hexclave Cloud, create a project on the Hexclave dashboard at https://app.hexclave.com and put its information in the HEXCLAVE_PROJECT_ID environment variable.\n `}\n `;\n showMissingConfigAlertInBrowser(message);\n return throwErr(new Error(message));\n};\n\n\nexport const createCache = <D extends any[], T>(fetcher: (dependencies: D) => Promise<T>) => {\n return new AsyncCache<D, Result<T>>(\n async (dependencies) => await Result.fromThrowingAsync(async () => await fetcher(dependencies)),\n {},\n );\n};\n\nexport const createCacheBySession = <D extends any[], T>(fetcher: (session: InternalSession, extraDependencies: D) => Promise<T> ) => {\n return new AsyncCache<[InternalSession, ...D], Result<T>>(\n async ([session, ...extraDependencies]) => await Result.fromThrowingAsync(async () => await fetcher(session, extraDependencies)),\n {\n onSubscribe: ([session], refresh) => {\n const handler = session.onInvalidate(() => refresh());\n return () => handler.unsubscribe();\n },\n },\n );\n};\n\n\ntype AppLike = { [hexclaveAppInternalsSymbol]: { getConstructorOptions: () => any } };\nexport function resolveConstructorOptions<T extends { inheritsFrom?: AppLike }>(options: T): T & { inheritsFrom?: undefined } {\n return {\n ...options.inheritsFrom?.[hexclaveAppInternalsSymbol].getConstructorOptions() ?? {},\n ...filterUndefined(omit(options, [\"inheritsFrom\"])),\n };\n}\n\nexport function getUrls(partial: HandlerUrlOptions, options: { projectId: string }): ResolvedHandlerUrls {\n return resolveHandlerUrls({\n urls: partial,\n projectId: options.projectId,\n });\n}\n\nexport function getDefaultProjectId() {\n return envVars.HEXCLAVE_PROJECT_ID || throwMissingProjectIdError();\n}\n\nexport function getDefaultPublishableClientKey() {\n return envVars.HEXCLAVE_PUBLISHABLE_CLIENT_KEY;\n}\n\nexport function getDefaultSecretServerKey() {\n return envVars.HEXCLAVE_SECRET_SERVER_KEY || throwErr(new Error(\"No secret server key provided. Please copy your key from the Hexclave dashboard and put it in the HEXCLAVE_SECRET_SERVER_KEY environment variable.\"));\n}\n\nexport function getDefaultSuperSecretAdminKey() {\n return envVars.HEXCLAVE_SUPER_SECRET_ADMIN_KEY || throwErr(new Error(\"No super secret admin key provided. Please copy your key from the Hexclave dashboard and put it in the HEXCLAVE_SUPER_SECRET_ADMIN_KEY environment variable.\"));\n}\n\nexport function getDefaultExtraRequestHeaders() {\n return JSON.parse(envVars.HEXCLAVE_EXTRA_REQUEST_HEADERS || '{}');\n}\n\n/**\n * Returns the base URL for the Stack API.\n *\n * The URL can be specified in several ways, in order of precedence:\n * 1. Directly through userSpecifiedBaseUrl parameter as string or browser/server object\n * 2. Through environment variables:\n * - Browser: NEXT_PUBLIC_HEXCLAVE_API_URL_BROWSER/VITE_HEXCLAVE_API_URL_BROWSER\n * - Server: HEXCLAVE_API_URL_SERVER\n * - Default: HEXCLAVE_API_URL\n * 3. Default base URL if none of the above are specified\n *\n * The function also ensures the URL doesn't end with a trailing slash\n * by removing it if present.\n *\n * @param userSpecifiedBaseUrl - Optional URL override as string or {browser, server} object\n * @returns The configured base URL without trailing slash\n\n */\nexport function getBaseUrl(userSpecifiedBaseUrl: string | { browser: string, server: string } | undefined) {\n let url;\n if (userSpecifiedBaseUrl) {\n if (typeof userSpecifiedBaseUrl === \"string\") {\n url = userSpecifiedBaseUrl;\n } else {\n if (isBrowserLike()) {\n url = userSpecifiedBaseUrl.browser;\n } else {\n url = userSpecifiedBaseUrl.server;\n }\n }\n } else {\n if (isBrowserLike()) {\n url = envVars.HEXCLAVE_API_URL_BROWSER;\n } else {\n url = envVars.HEXCLAVE_API_URL_SERVER;\n }\n url = url || envVars.HEXCLAVE_API_URL || defaultBaseUrl;\n }\n\n return replaceHexclavePortPrefix(url.endsWith('/') ? url.slice(0, -1) : url);\n}\n\nexport const defaultBaseUrl = \"https://api.hexclave.com\";\nexport const defaultAnalyticsBaseUrl = \"https://r.hexclave.com\";\n\nconst analyticsBaseUrlsByApiBaseUrl = new Map<string, string>([\n [defaultBaseUrl, defaultAnalyticsBaseUrl],\n [\"https://api.stack-auth.com\", \"https://r.stack-auth.com\"],\n]);\n\nexport function getAnalyticsBaseUrl(regularBaseUrl: string): string {\n return analyticsBaseUrlsByApiBaseUrl.get(regularBaseUrl) ?? regularBaseUrl;\n}\n\n\nfunction fetchBackendUrlsInBackground(primaryBaseUrl: string): void {\n createGlobal('__stack-fetch-backend-urls-started', () => {\n runAsynchronously(async () => {\n try {\n const res = await fetch(`${primaryBaseUrl}/api/v1/internal/backend-urls`);\n if (!res.ok) {\n return;\n }\n const data = await res.json();\n if (!Array.isArray(data.urls) || !data.urls.every((u: unknown) => typeof u === 'string')) {\n return;\n }\n createGlobal('__stack-fetched-backend-urls', () => data.urls as string[]);\n } catch (e) {\n captureError('fetch-backend-urls-in-background', e);\n }\n });\n return true;\n });\n}\n\nexport function resolveApiUrls(userExplicitBaseUrl: string | { browser: string, server: string } | undefined): () => string[] {\n return () => {\n if (userExplicitBaseUrl != null) {\n return [getBaseUrl(userExplicitBaseUrl)];\n }\n const primary = getBaseUrl(undefined);\n // Always try to fetch server-configured URLs (supports custom domains via\n // STACK_BACKEND_URLS_CONFIG). Hardcoded fallbacks are used as a default\n // until the background fetch completes.\n fetchBackendUrlsInBackground(primary);\n return getGlobal('__stack-fetched-backend-urls') ?? getDefaultApiUrls(primary);\n };\n}\n\nexport type TokenObject = {\n accessToken: string | null,\n refreshToken: string | null,\n};\n\nexport function createEmptyTokenStore() {\n return new Store<TokenObject>({\n refreshToken: null,\n accessToken: null,\n });\n}\n\n\nconst cachePromiseByHookId = new Map<string, ReactPromise<Result<unknown>>>();\nexport function useAsyncCache<D extends any[], T>(cache: AsyncCache<D, Result<T>>, dependencies: D, caller: string): T {\n // we explicitly don't want to run this hook in SSR\n suspendIfSsr(caller);\n\n // on the dashboard, we do some perf monitoring for pre-fetching which should hook right in here\n const asyncCacheHooks: any[] = getGlobal(\"use-async-cache-execution-hooks\") ?? [];\n for (const hook of asyncCacheHooks) {\n hook({ cache, caller, dependencies });\n }\n\n const id = React.useId();\n\n // whenever the dependencies change, we need to refresh the promise cache\n React.useEffect(() => {\n cachePromiseByHookId.delete(id);\n }, [...dependencies, id]);\n\n const subscribe = useCallback((cb: () => void) => {\n const { unsubscribe } = cache.onStateChange(dependencies, () => {\n cachePromiseByHookId.delete(id);\n cb();\n });\n return unsubscribe;\n }, [cache, ...dependencies]);\n\n const getSnapshot = useCallback(() => {\n // React checks whether a promise passed to `use` is still the same as the previous one by comparing the reference.\n // If we didn't cache here, this wouldn't work because the promise would be recreated every time the value changes.\n if (!cachePromiseByHookId.has(id)) {\n cachePromiseByHookId.set(id, cache.getOrWait(dependencies, \"read-write\"));\n }\n return cachePromiseByHookId.get(id) as ReactPromise<Result<T>>;\n }, [cache, ...dependencies]);\n\n // note: we must use React.useSyncExternalStore instead of importing the function directly, as it will otherwise\n // throw an error on Next.js (\"can't import useSyncExternalStore from the server\")\n const promise = React.useSyncExternalStore(\n subscribe,\n getSnapshot,\n getSnapshot,\n );\n\n const result = use(promise);\n if (result.status === \"error\") {\n const error = result.error;\n if (error instanceof Error && !(error as any).__stackHasConcatenatedStacktraces) {\n concatStacktraces(error, new Error());\n (error as any).__stackHasConcatenatedStacktraces = true;\n }\n throw error;\n }\n return result.data;\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { _HexclaveServerAppImplIncomplete } from "./server-app-impl.js";
2
- import { _HexclaveAdminAppImplIncomplete } from "./admin-app-impl.js";
3
2
  import { _HexclaveClientAppImplIncomplete } from "./client-app-impl.js";
3
+ import { _HexclaveAdminAppImplIncomplete } from "./admin-app-impl.js";
4
4
 
5
5
  //#region src/lib/hexclave-app/apps/implementations/index.d.ts
6
6
  declare const _HexclaveAdminAppImpl: typeof _HexclaveAdminAppImplIncomplete, _HexclaveClientAppImpl: typeof _HexclaveClientAppImplIncomplete, _HexclaveServerAppImpl: typeof _HexclaveServerAppImplIncomplete;
@@ -1,7 +1,7 @@
1
1
  import { _HexclaveServerAppImplIncomplete } from "./server-app-impl.js";
2
+ import { _HexclaveClientAppImplIncomplete } from "./client-app-impl.js";
2
3
  import { scrambleDuringCompileTime } from "@hexclave/shared/dist/utils/compile-time";
3
4
  import { _HexclaveAdminAppImplIncomplete } from "./admin-app-impl.js";
4
- import { _HexclaveClientAppImplIncomplete } from "./client-app-impl.js";
5
5
  //#region src/lib/hexclave-app/apps/implementations/index.ts
6
6
  /**
7
7
  * Prevents a circular dependency between the client and admin apps. For more information, see the documentation comment
@@ -3,12 +3,12 @@ import { HexclaveServerInterface, KnownErrors } from "@hexclave/shared";
3
3
  import { Result } from "@hexclave/shared/dist/utils/results";
4
4
  import { ConvexCtx, GetCurrentUserOptions } from "../../common.js";
5
5
  import { AdminProjectPermissionDefinition, AdminTeamPermission, AdminTeamPermissionDefinition } from "../../permissions/index.js";
6
+ import { _HexclaveClientAppImplIncomplete } from "./client-app-impl.js";
6
7
  import { InternalSession } from "@hexclave/shared/dist/sessions";
7
8
  import { ApiKey } from "../../api-keys/index.js";
8
9
  import { ServerContactChannel } from "../../contact-channels/index.js";
9
10
  import { EditableTeamMemberProfile, ReceivedTeamInvitation, SentTeamInvitation, ServerListTeamsOptions, ServerListUsersOptions, ServerTeam, ServerTeamCreateOptions, ServerTeamUser } from "../../teams/index.js";
10
11
  import { ProjectCurrentServerUser, ServerOAuthProvider, ServerUser, ServerUserCreateOptions } from "../../users/index.js";
11
- import { _HexclaveClientAppImplIncomplete } from "./client-app-impl.js";
12
12
  import { TeamApiKeysCrud, UserApiKeysCrud, teamApiKeysCreateOutputSchema, userApiKeysCreateOutputSchema } from "@hexclave/shared/dist/interface/crud/project-api-keys";
13
13
  import { ContactChannelsCrud } from "@hexclave/shared/dist/interface/crud/contact-channels";
14
14
  import { ItemCrud } from "@hexclave/shared/dist/interface/crud/items";
@@ -72,7 +72,7 @@ declare class _HexclaveServerAppImplIncomplete<HasTokenStore extends boolean, Pr
72
72
  protected _serverNotificationCategoryFromCrud(userId: string, crud: NotificationPreferenceCrud['Server']['Read']): NotificationCategory;
73
73
  protected _serverOAuthProviderFromCrud(crud: OAuthProviderCrud['Server']['Read']): {
74
74
  id: string;
75
- type: "x" | "google" | "github" | "microsoft" | "spotify" | "facebook" | "discord" | "gitlab" | "bitbucket" | "linkedin" | "apple" | "twitch";
75
+ type: "google" | "github" | "microsoft" | "spotify" | "facebook" | "discord" | "gitlab" | "bitbucket" | "linkedin" | "apple" | "x" | "twitch";
76
76
  userId: string;
77
77
  accountId: string;
78
78
  email: string | undefined;
@@ -5,6 +5,7 @@ import { HexclaveServerInterface, KnownErrors } from "@hexclave/shared";
5
5
  import { suspend } from "@hexclave/shared/dist/utils/react";
6
6
  import { Result } from "@hexclave/shared/dist/utils/results";
7
7
  import { clientVersion, createCache, createCacheBySession, getDefaultExtraRequestHeaders, getDefaultProjectId, getDefaultPublishableClientKey, getDefaultSecretServerKey, resolveApiUrls, resolveConstructorOptions, useAsyncCache } from "./common.js";
8
+ import { _HexclaveClientAppImplIncomplete } from "./client-app-impl.js";
8
9
  import { InternalSession } from "@hexclave/shared/dist/sessions";
9
10
  import { isUuid } from "@hexclave/shared/dist/utils/uuids";
10
11
  import { WebAuthnError, startRegistration } from "@simplewebauthn/browser";
@@ -14,7 +15,6 @@ import { serverContactChannelCreateOptionsToCrud, serverContactChannelUpdateOpti
14
15
  import { serverTeamCreateOptionsToCrud, serverTeamUpdateOptionsToCrud } from "../../teams/index.js";
15
16
  import { scopePasskeyRegistrationToHostname } from "../../passkey-rp-id.js";
16
17
  import { serverUserCreateOptionsToCrud, serverUserUpdateOptionsToCrud, withUserDestructureGuard } from "../../users/index.js";
17
- import { _HexclaveClientAppImplIncomplete } from "./client-app-impl.js";
18
18
  //#region src/lib/hexclave-app/apps/implementations/server-app-impl.ts
19
19
  var _HexclaveServerAppImplIncomplete = class extends _HexclaveClientAppImplIncomplete {
20
20
  async _refreshTeamMembership(teamId, userId) {
@@ -107,6 +107,9 @@ type ServerTeam = {
107
107
  } & Team;
108
108
  type ServerListUsersOptionsBase = {
109
109
  cursor?: string;
110
+ /**
111
+ * Maximum number of users to return per page. Must be at most 1000.
112
+ */
110
113
  limit?: number;
111
114
  orderBy?: 'signedUpAt' | 'lastActiveAt';
112
115
  desc?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../../../src/lib/hexclave-app/teams/index.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../../../src/lib/hexclave-app/teams/index.ts"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../../src/lib/hexclave-app/teams/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//===========================================\nimport { TeamsCrud } from \"@hexclave/shared/dist/interface/crud/teams\";\nimport { ReadonlyJson } from \"@hexclave/shared/dist/utils/json\";\n\nimport { ApiKeyCreationOptions, TeamApiKey, TeamApiKeyFirstView } from \"../api-keys\";\nimport { AsyncStoreProperty } from \"../common\";\nimport { Customer } from \"../customers\";\nimport { ServerUser } from \"../users\";\n\n\nexport type TeamMemberProfile = {\n displayName: string | null,\n profileImageUrl: string | null,\n}\n\nexport type TeamMemberProfileUpdateOptions = {\n displayName?: string,\n profileImageUrl?: string | null,\n};\n\nexport type EditableTeamMemberProfile = TeamMemberProfile & {\n update(update: TeamMemberProfileUpdateOptions): Promise<void>,\n}\n\nexport type TeamUser = {\n id: string,\n teamProfile: TeamMemberProfile,\n}\n\n/**\n * A team invitation as seen from the team's perspective (ie. the sender).\n *\n * Returned by `team.listInvitations()`. Contains the recipient email and allows\n * revoking the invitation.\n */\nexport type SentTeamInvitation = {\n id: string,\n recipientEmail: string | null,\n expiresAt: Date,\n revoke(): Promise<void>,\n}\n\n/**\n * @deprecated Use `SentTeamInvitation` instead.\n */\nexport type TeamInvitation = SentTeamInvitation;\n\n/**\n * A team invitation as seen from the invited user's perspective (ie. the receiver).\n *\n * Returned by `user.listTeamInvitations()`. Contains information about teams that have\n * sent invitations to any of the user's verified email addresses, and allows accepting\n * the invitation to join the team.\n */\nexport type ReceivedTeamInvitation = {\n id: string,\n teamId: string,\n teamDisplayName: string,\n recipientEmail: string,\n expiresAt: Date,\n\n /**\n * Accepts the invitation, adding the current user to the team.\n *\n * The user must have a verified email address matching the invitation's recipient email.\n */\n accept(): Promise<void>,\n}\n\nexport type Team = {\n id: string,\n displayName: string,\n profileImageUrl: string | null,\n clientMetadata: any,\n clientReadOnlyMetadata: any,\n inviteUser(options: { email: string, callbackUrl?: string }): Promise<void>,\n listUsers(): Promise<TeamUser[]>,\n removeUser(userId: string): Promise<void>,\n useUsers(): TeamUser[], // THIS_LINE_PLATFORM react-like\n listInvitations(): Promise<SentTeamInvitation[]>,\n useInvitations(): SentTeamInvitation[], // THIS_LINE_PLATFORM react-like\n update(update: TeamUpdateOptions): Promise<void>,\n delete(): Promise<void>,\n createApiKey(options: ApiKeyCreationOptions<\"team\">): Promise<TeamApiKeyFirstView>,\n} & AsyncStoreProperty<\"apiKeys\", [], TeamApiKey[], true> & Customer;\n\nexport type TeamUpdateOptions = {\n displayName?: string,\n profileImageUrl?: string | null,\n clientMetadata?: ReadonlyJson,\n};\nexport function teamUpdateOptionsToCrud(options: TeamUpdateOptions): TeamsCrud[\"Client\"][\"Update\"] {\n return {\n display_name: options.displayName,\n profile_image_url: options.profileImageUrl,\n client_metadata: options.clientMetadata,\n };\n}\n\nexport type TeamCreateOptions = {\n displayName: string,\n profileImageUrl?: string,\n}\nexport function teamCreateOptionsToCrud(options: TeamCreateOptions, creatorUserId: string): TeamsCrud[\"Client\"][\"Create\"] {\n return {\n display_name: options.displayName,\n profile_image_url: options.profileImageUrl,\n creator_user_id: creatorUserId,\n };\n}\n\n\nexport type ServerTeamMemberProfile = TeamMemberProfile;\n\nexport type ServerTeamUser = ServerUser & {\n teamProfile: ServerTeamMemberProfile,\n}\n\nexport type ServerTeam = {\n createdAt: Date,\n serverMetadata: any,\n listUsers(): Promise<ServerTeamUser[]>,\n useUsers(): ServerUser[], // THIS_LINE_PLATFORM react-like\n update(update: ServerTeamUpdateOptions): Promise<void>,\n delete(): Promise<void>,\n addUser(userId: string): Promise<void>,\n inviteUser(options: { email: string, callbackUrl?: string }): Promise<void>,\n removeUser(userId: string): Promise<void>,\n} & Team;\n\ntype ServerListUsersOptionsBase = {\n cursor?: string,\n limit?: number,\n orderBy?: 'signedUpAt' | 'lastActiveAt',\n desc?: boolean,\n /**\n * Free-text search. Matches user ID (exact UUID), display name, and contact channels (e.g. primary email).\n */\n query?: string,\n /**\n * Exclude users whose primary email domain matches one of these exact domains.\n */\n excludedEmailDomains?: string[],\n /**\n * Only return users who are members of the given team.\n */\n teamId?: string,\n /**\n * Whether to include restricted users (users who haven't completed onboarding requirements).\n * Defaults to false.\n */\n includeRestricted?: boolean,\n /**\n * Whether to include anonymous users (and restricted users).\n * Defaults to false.\n */\n includeAnonymous?: boolean,\n};\n\nexport type ServerListUsersOptions = ServerListUsersOptionsBase & (\n {\n onlyAnonymous?: false,\n } | {\n /**\n * Whether to return only anonymous users.\n * Requires includeAnonymous=true.\n * Defaults to false.\n */\n onlyAnonymous: true,\n includeAnonymous: true,\n }\n);\n\nexport type ServerListTeamsOptions = {\n orderBy?: 'createdAt',\n desc?: boolean,\n cursor?: string,\n limit?: number,\n /**\n * Free-text search. Matches team ID (exact UUID) and display name.\n */\n query?: string,\n};\n\nexport type ServerTeamCreateOptions = TeamCreateOptions & {\n creatorUserId?: string,\n};\nexport function serverTeamCreateOptionsToCrud(options: ServerTeamCreateOptions): TeamsCrud[\"Server\"][\"Create\"] {\n return {\n display_name: options.displayName,\n profile_image_url: options.profileImageUrl,\n creator_user_id: options.creatorUserId,\n };\n}\n\nexport type ServerTeamUpdateOptions = TeamUpdateOptions & {\n clientReadOnlyMetadata?: ReadonlyJson,\n serverMetadata?: ReadonlyJson,\n};\nexport function serverTeamUpdateOptionsToCrud(options: ServerTeamUpdateOptions): TeamsCrud[\"Server\"][\"Update\"] {\n return {\n display_name: options.displayName,\n profile_image_url: options.profileImageUrl,\n client_metadata: options.clientMetadata,\n client_read_only_metadata: options.clientReadOnlyMetadata,\n server_metadata: options.serverMetadata,\n };\n}\n"],"mappings":";AA8FA,SAAgB,wBAAwB,SAA2D;CACjG,OAAO;EACL,cAAc,QAAQ;EACtB,mBAAmB,QAAQ;EAC3B,iBAAiB,QAAQ;CAC3B;AACF;AAMA,SAAgB,wBAAwB,SAA4B,eAAsD;CACxH,OAAO;EACL,cAAc,QAAQ;EACtB,mBAAmB,QAAQ;EAC3B,iBAAiB;CACnB;AACF;AA8EA,SAAgB,8BAA8B,SAAiE;CAC7G,OAAO;EACL,cAAc,QAAQ;EACtB,mBAAmB,QAAQ;EAC3B,iBAAiB,QAAQ;CAC3B;AACF;AAMA,SAAgB,8BAA8B,SAAiE;CAC7G,OAAO;EACL,cAAc,QAAQ;EACtB,mBAAmB,QAAQ;EAC3B,iBAAiB,QAAQ;EACzB,2BAA2B,QAAQ;EACnC,iBAAiB,QAAQ;CAC3B;AACF"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../../src/lib/hexclave-app/teams/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//===========================================\nimport { TeamsCrud } from \"@hexclave/shared/dist/interface/crud/teams\";\nimport { ReadonlyJson } from \"@hexclave/shared/dist/utils/json\";\n\nimport { ApiKeyCreationOptions, TeamApiKey, TeamApiKeyFirstView } from \"../api-keys\";\nimport { AsyncStoreProperty } from \"../common\";\nimport { Customer } from \"../customers\";\nimport { ServerUser } from \"../users\";\n\n\nexport type TeamMemberProfile = {\n displayName: string | null,\n profileImageUrl: string | null,\n}\n\nexport type TeamMemberProfileUpdateOptions = {\n displayName?: string,\n profileImageUrl?: string | null,\n};\n\nexport type EditableTeamMemberProfile = TeamMemberProfile & {\n update(update: TeamMemberProfileUpdateOptions): Promise<void>,\n}\n\nexport type TeamUser = {\n id: string,\n teamProfile: TeamMemberProfile,\n}\n\n/**\n * A team invitation as seen from the team's perspective (ie. the sender).\n *\n * Returned by `team.listInvitations()`. Contains the recipient email and allows\n * revoking the invitation.\n */\nexport type SentTeamInvitation = {\n id: string,\n recipientEmail: string | null,\n expiresAt: Date,\n revoke(): Promise<void>,\n}\n\n/**\n * @deprecated Use `SentTeamInvitation` instead.\n */\nexport type TeamInvitation = SentTeamInvitation;\n\n/**\n * A team invitation as seen from the invited user's perspective (ie. the receiver).\n *\n * Returned by `user.listTeamInvitations()`. Contains information about teams that have\n * sent invitations to any of the user's verified email addresses, and allows accepting\n * the invitation to join the team.\n */\nexport type ReceivedTeamInvitation = {\n id: string,\n teamId: string,\n teamDisplayName: string,\n recipientEmail: string,\n expiresAt: Date,\n\n /**\n * Accepts the invitation, adding the current user to the team.\n *\n * The user must have a verified email address matching the invitation's recipient email.\n */\n accept(): Promise<void>,\n}\n\nexport type Team = {\n id: string,\n displayName: string,\n profileImageUrl: string | null,\n clientMetadata: any,\n clientReadOnlyMetadata: any,\n inviteUser(options: { email: string, callbackUrl?: string }): Promise<void>,\n listUsers(): Promise<TeamUser[]>,\n removeUser(userId: string): Promise<void>,\n useUsers(): TeamUser[], // THIS_LINE_PLATFORM react-like\n listInvitations(): Promise<SentTeamInvitation[]>,\n useInvitations(): SentTeamInvitation[], // THIS_LINE_PLATFORM react-like\n update(update: TeamUpdateOptions): Promise<void>,\n delete(): Promise<void>,\n createApiKey(options: ApiKeyCreationOptions<\"team\">): Promise<TeamApiKeyFirstView>,\n} & AsyncStoreProperty<\"apiKeys\", [], TeamApiKey[], true> & Customer;\n\nexport type TeamUpdateOptions = {\n displayName?: string,\n profileImageUrl?: string | null,\n clientMetadata?: ReadonlyJson,\n};\nexport function teamUpdateOptionsToCrud(options: TeamUpdateOptions): TeamsCrud[\"Client\"][\"Update\"] {\n return {\n display_name: options.displayName,\n profile_image_url: options.profileImageUrl,\n client_metadata: options.clientMetadata,\n };\n}\n\nexport type TeamCreateOptions = {\n displayName: string,\n profileImageUrl?: string,\n}\nexport function teamCreateOptionsToCrud(options: TeamCreateOptions, creatorUserId: string): TeamsCrud[\"Client\"][\"Create\"] {\n return {\n display_name: options.displayName,\n profile_image_url: options.profileImageUrl,\n creator_user_id: creatorUserId,\n };\n}\n\n\nexport type ServerTeamMemberProfile = TeamMemberProfile;\n\nexport type ServerTeamUser = ServerUser & {\n teamProfile: ServerTeamMemberProfile,\n}\n\nexport type ServerTeam = {\n createdAt: Date,\n serverMetadata: any,\n listUsers(): Promise<ServerTeamUser[]>,\n useUsers(): ServerUser[], // THIS_LINE_PLATFORM react-like\n update(update: ServerTeamUpdateOptions): Promise<void>,\n delete(): Promise<void>,\n addUser(userId: string): Promise<void>,\n inviteUser(options: { email: string, callbackUrl?: string }): Promise<void>,\n removeUser(userId: string): Promise<void>,\n} & Team;\n\ntype ServerListUsersOptionsBase = {\n cursor?: string,\n /**\n * Maximum number of users to return per page. Must be at most 1000.\n */\n limit?: number,\n orderBy?: 'signedUpAt' | 'lastActiveAt',\n desc?: boolean,\n /**\n * Free-text search. Matches user ID (exact UUID), display name, and contact channels (e.g. primary email).\n */\n query?: string,\n /**\n * Exclude users whose primary email domain matches one of these exact domains.\n */\n excludedEmailDomains?: string[],\n /**\n * Only return users who are members of the given team.\n */\n teamId?: string,\n /**\n * Whether to include restricted users (users who haven't completed onboarding requirements).\n * Defaults to false.\n */\n includeRestricted?: boolean,\n /**\n * Whether to include anonymous users (and restricted users).\n * Defaults to false.\n */\n includeAnonymous?: boolean,\n};\n\nexport type ServerListUsersOptions = ServerListUsersOptionsBase & (\n {\n onlyAnonymous?: false,\n } | {\n /**\n * Whether to return only anonymous users.\n * Requires includeAnonymous=true.\n * Defaults to false.\n */\n onlyAnonymous: true,\n includeAnonymous: true,\n }\n);\n\nexport type ServerListTeamsOptions = {\n orderBy?: 'createdAt',\n desc?: boolean,\n cursor?: string,\n limit?: number,\n /**\n * Free-text search. Matches team ID (exact UUID) and display name.\n */\n query?: string,\n};\n\nexport type ServerTeamCreateOptions = TeamCreateOptions & {\n creatorUserId?: string,\n};\nexport function serverTeamCreateOptionsToCrud(options: ServerTeamCreateOptions): TeamsCrud[\"Server\"][\"Create\"] {\n return {\n display_name: options.displayName,\n profile_image_url: options.profileImageUrl,\n creator_user_id: options.creatorUserId,\n };\n}\n\nexport type ServerTeamUpdateOptions = TeamUpdateOptions & {\n clientReadOnlyMetadata?: ReadonlyJson,\n serverMetadata?: ReadonlyJson,\n};\nexport function serverTeamUpdateOptionsToCrud(options: ServerTeamUpdateOptions): TeamsCrud[\"Server\"][\"Update\"] {\n return {\n display_name: options.displayName,\n profile_image_url: options.profileImageUrl,\n client_metadata: options.clientMetadata,\n client_read_only_metadata: options.clientReadOnlyMetadata,\n server_metadata: options.serverMetadata,\n };\n}\n"],"mappings":";AA8FA,SAAgB,wBAAwB,SAA2D;CACjG,OAAO;EACL,cAAc,QAAQ;EACtB,mBAAmB,QAAQ;EAC3B,iBAAiB,QAAQ;CAC3B;AACF;AAMA,SAAgB,wBAAwB,SAA4B,eAAsD;CACxH,OAAO;EACL,cAAc,QAAQ;EACtB,mBAAmB,QAAQ;EAC3B,iBAAiB;CACnB;AACF;AAiFA,SAAgB,8BAA8B,SAAiE;CAC7G,OAAO;EACL,cAAc,QAAQ;EACtB,mBAAmB,QAAQ;EAC3B,iBAAiB,QAAQ;CAC3B;AACF;AAMA,SAAgB,8BAA8B,SAAiE;CAC7G,OAAO;EACL,cAAc,QAAQ;EACtB,mBAAmB,QAAQ;EAC3B,iBAAiB,QAAQ;EACzB,2BAA2B,QAAQ;EACnC,iBAAiB,QAAQ;CAC3B;AACF"}
@@ -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,20 @@
1
+ import { createGlobal } from "@hexclave/shared/dist/utils/globals";
2
+ //#region src/utils/monkey-patch.tsx
3
+ const NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL = "__stack-no-suspense-boundary-error__";
4
+ function isNoSuspenseBoundaryError(value) {
5
+ return typeof value === "object" && value !== null && "__noSuspenseBoundarySentinel" in value && value.__noSuspenseBoundarySentinel === "__stack-no-suspense-boundary-error__";
6
+ }
7
+ function ensureMonkeyPatch() {
8
+ createGlobal("__console-error-monkey-patch__", () => {
9
+ const originalConsoleError = console.error;
10
+ console.error = function(...args) {
11
+ if (args.length === 1 && isNoSuspenseBoundaryError(args[0])) return;
12
+ return originalConsoleError.apply(this, args);
13
+ };
14
+ return true;
15
+ });
16
+ }
17
+ //#endregion
18
+ export { NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL, ensureMonkeyPatch, isNoSuspenseBoundaryError };
19
+
20
+ //# 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,aAAa,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,60 @@
1
+ "use client";
2
+ import { deindent } from "@hexclave/shared/dist/utils/strings";
3
+ import { useSearchParams } from "next/navigation";
4
+ import { isBrowserLike } from "@hexclave/shared/dist/utils/env";
5
+ import { NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL, ensureMonkeyPatch } from "./monkey-patch.js";
6
+ //#region src/utils/react.tsx
7
+ function shouldRethrowRenderingError(error) {
8
+ return !!error && typeof error === "object" && "digest" in error && error.digest === "BAILOUT_TO_CLIENT_SIDE_RENDERING";
9
+ }
10
+ var NoSuspenseBoundaryError = class extends Error {
11
+ constructor(options) {
12
+ ensureMonkeyPatch();
13
+ super(deindent`
14
+ Suspense boundary not found! Read the error message below carefully (or paste it into your AI agent).
15
+
16
+ ${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.
17
+
18
+ There are several potential causes:
19
+
20
+ 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.
21
+
22
+ 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.
23
+
24
+ 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:
25
+
26
+ - app
27
+ - - layout.tsx // contains <html> and <body>, alongside providers and other components that don't need ${options.caller ?? "this code path"}
28
+ - - loading.tsx // required for suspense
29
+ - - (main)
30
+ - - - layout.tsx // contains the main layout of your app, like a sidebar or a header, and can use ${options.caller ?? "this code path"}
31
+ - - - route.tsx // your actual main page
32
+ - - - the rest of your app
33
+
34
+ For more information on this approach, see Next's documentation on route groups: https://nextjs.org/docs/app/building-your-application/routing/route-groups
35
+
36
+ 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.
37
+
38
+ 5. Your version of Hexclave is too old. Upgrade to the latest version to see if that fixes the issue.
39
+
40
+ See: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout
41
+
42
+ More information on SSR and Suspense boundaries: https://react.dev/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content
43
+ `);
44
+ this.__noSuspenseBoundarySentinel = NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL;
45
+ this.name = "NoSuspenseBoundaryError";
46
+ this.reason = options.caller ?? "suspendIfSsr()";
47
+ this.digest = "BAILOUT_TO_CLIENT_SIDE_RENDERING";
48
+ }
49
+ };
50
+ /**
51
+ * Use this in a component or hook to disable SSR. It must be wrapped in a Suspense boundary.
52
+ */
53
+ function suspendIfSsr(caller) {
54
+ useSearchParams();
55
+ if (!isBrowserLike()) throw new NoSuspenseBoundaryError({ caller });
56
+ }
57
+ //#endregion
58
+ export { NoSuspenseBoundaryError, shouldRethrowRenderingError, suspendIfSsr };
59
+
60
+ //# sourceMappingURL=react.js.map