@hexclave/js 1.0.123 → 1.0.126

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 (99) hide show
  1. package/dist/{admin-app-impl-Cxer4EIA.d.ts → admin-app-impl-Df-WtGg0.d.ts} +29 -13
  2. package/dist/admin-app-impl-Df-WtGg0.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-DLFxr4f7.d.ts → client-app-C7Tnf0n1.d.ts} +34 -6
  6. package/dist/client-app-C7Tnf0n1.d.ts.map +1 -0
  7. package/dist/{common-BDyEKSM6.d.ts → common-Dii3iHJR.d.ts} +15 -3
  8. package/dist/common-Dii3iHJR.d.ts.map +1 -0
  9. package/dist/{common-CRu2FsO9.d.ts → common-GgG4l8fG.d.ts} +2 -2
  10. package/dist/{common-CRu2FsO9.d.ts.map → common-GgG4l8fG.d.ts.map} +1 -1
  11. package/dist/dev-tool/dev-tool-core.d.ts +1 -1
  12. package/dist/dev-tool/index.d.ts +1 -1
  13. package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +4 -4
  14. package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
  15. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +18 -2
  16. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
  17. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.d.ts +1 -0
  18. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.js +382 -0
  19. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.js.map +1 -0
  20. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.d.ts +1 -0
  21. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.js +66 -0
  22. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.js.map +1 -0
  23. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js +66 -14
  24. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
  25. package/dist/esm/lib/hexclave-app/apps/implementations/common.js +2 -2
  26. package/dist/esm/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.d.ts +31 -0
  27. package/dist/esm/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.d.ts.map +1 -0
  28. package/dist/esm/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.js +115 -0
  29. package/dist/esm/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.js.map +1 -0
  30. package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.d.ts +16 -1
  31. package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.d.ts.map +1 -1
  32. package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.js.map +1 -1
  33. package/dist/esm/lib/hexclave-app/common.d.ts +14 -2
  34. package/dist/esm/lib/hexclave-app/common.d.ts.map +1 -1
  35. package/dist/esm/lib/hexclave-app/common.js +17 -1
  36. package/dist/esm/lib/hexclave-app/common.js.map +1 -1
  37. package/dist/esm/lib/hexclave-app/index.d.ts +2 -2
  38. package/dist/esm/lib/hexclave-app/index.js +2 -2
  39. package/dist/esm/lib/hexclave-app/users/index.d.ts +14 -1
  40. package/dist/esm/lib/hexclave-app/users/index.d.ts.map +1 -1
  41. package/dist/esm/lib/hexclave-app/users/index.js.map +1 -1
  42. package/dist/{index-ZiEXZJBF.d.ts → index-BTa6Yu7N.d.ts} +2 -2
  43. package/dist/{index-ZiEXZJBF.d.ts.map → index-BTa6Yu7N.d.ts.map} +1 -1
  44. package/dist/{index-DLPccxVU.d.ts → index-DlOcbI9W.d.ts} +2 -2
  45. package/dist/{index-DLPccxVU.d.ts.map → index-DlOcbI9W.d.ts.map} +1 -1
  46. package/dist/index.d.ts +4 -4
  47. package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -1
  48. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +1 -1
  49. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.d.ts +1 -0
  50. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.js +381 -0
  51. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.js.map +1 -0
  52. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.d.ts +1 -0
  53. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.js +65 -0
  54. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.js.map +1 -0
  55. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js +65 -13
  56. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
  57. package/dist/lib/hexclave-app/apps/implementations/common.d.ts +1 -1
  58. package/dist/lib/hexclave-app/apps/implementations/common.js +2 -2
  59. package/dist/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.d.ts +31 -0
  60. package/dist/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.d.ts.map +1 -0
  61. package/dist/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.js +117 -0
  62. package/dist/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.js.map +1 -0
  63. package/dist/lib/hexclave-app/apps/implementations/index.d.ts +1 -1
  64. package/dist/lib/hexclave-app/apps/implementations/redirect-page-urls.d.ts +1 -1
  65. package/dist/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
  66. package/dist/lib/hexclave-app/apps/index.d.ts +1 -1
  67. package/dist/lib/hexclave-app/apps/interfaces/admin-app.d.ts +1 -1
  68. package/dist/lib/hexclave-app/apps/interfaces/client-app.d.ts +1 -1
  69. package/dist/lib/hexclave-app/apps/interfaces/client-app.js.map +1 -1
  70. package/dist/lib/hexclave-app/apps/interfaces/server-app.d.ts +1 -1
  71. package/dist/lib/hexclave-app/common.d.ts +2 -2
  72. package/dist/lib/hexclave-app/common.js +19 -0
  73. package/dist/lib/hexclave-app/common.js.map +1 -1
  74. package/dist/lib/hexclave-app/customers/index.d.ts +1 -1
  75. package/dist/lib/hexclave-app/data-vault/index.d.ts +1 -1
  76. package/dist/lib/hexclave-app/index.d.ts +4 -4
  77. package/dist/lib/hexclave-app/index.js +18 -0
  78. package/dist/lib/hexclave-app/projects/index.d.ts +1 -1
  79. package/dist/lib/hexclave-app/teams/index.d.ts +1 -1
  80. package/dist/lib/hexclave-app/url-targets.d.ts +1 -1
  81. package/dist/lib/hexclave-app/users/index.d.ts +1 -1
  82. package/dist/lib/hexclave-app/users/index.js.map +1 -1
  83. package/dist/pushed-config-error-overlay/index.d.ts +1 -1
  84. package/dist/{redirect-page-urls-C2CXwCcv.d.ts → redirect-page-urls-DCSFN6dg.d.ts} +2 -2
  85. package/dist/{redirect-page-urls-C2CXwCcv.d.ts.map → redirect-page-urls-DCSFN6dg.d.ts.map} +1 -1
  86. package/dist/{url-targets-BJgkwPGW.d.ts → url-targets-DYLcHumF.d.ts} +2 -2
  87. package/dist/{url-targets-BJgkwPGW.d.ts.map → url-targets-DYLcHumF.d.ts.map} +1 -1
  88. package/package.json +2 -2
  89. package/src/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.ts +425 -0
  90. package/src/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.ts +79 -0
  91. package/src/lib/hexclave-app/apps/implementations/client-app-impl.ts +124 -13
  92. package/src/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.ts +190 -0
  93. package/src/lib/hexclave-app/apps/interfaces/client-app.ts +17 -0
  94. package/src/lib/hexclave-app/common.ts +33 -0
  95. package/src/lib/hexclave-app/index.ts +4 -0
  96. package/src/lib/hexclave-app/users/index.ts +13 -1
  97. package/dist/admin-app-impl-Cxer4EIA.d.ts.map +0 -1
  98. package/dist/client-app-DLFxr4f7.d.ts.map +0 -1
  99. package/dist/common-BDyEKSM6.d.ts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-app-impl.external-token-store.test.js","names":["StackClientApp","KnownErrors","vi"],"sources":["../../../../../src/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.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 { KnownErrors } from \"@hexclave/shared\";\nimport { describe, expect, it, vi } from \"vitest\";\nimport { clerkTokenStore } from \"../../common\";\nimport { StackClientApp } from \"../interfaces/client-app\";\n\nfunction createAccessTokenString(refreshTokenId: string, issuedAtOffsetSeconds = 0): string {\n const encode = (value: unknown) => Buffer.from(JSON.stringify(value)).toString(\"base64url\");\n const nowSeconds = Math.floor(Date.now() / 1000) + issuedAtOffsetSeconds;\n // The payload must satisfy accessTokenPayloadSchema, otherwise AccessToken.createIfValid rejects it.\n return [\n encode({ alg: \"none\", typ: \"JWT\" }),\n encode({\n sub: \"user-id\",\n exp: nowSeconds + 60,\n iat: nowSeconds,\n iss: \"http://localhost:12345\",\n aud: \"00000000-0000-4000-8000-000000000000\",\n project_id: \"00000000-0000-4000-8000-000000000000\",\n branch_id: \"main\",\n refresh_token_id: refreshTokenId,\n role: \"authenticated\",\n name: \"Test User\",\n email: \"test@example.com\",\n email_verified: true,\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\ndescribe(\"StackClientApp external token stores\", () => {\n it(\"does not reuse a cached token when the provider session has ended\", async () => {\n let providerSessionId: string | null = \"clerk-session\";\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => providerSessionId,\n getToken: async () => \"stale-provider-token\",\n }),\n redirectMethod: \"none\",\n });\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const authenticatedSession = await getSession.call(clientApp);\n providerSessionId = null;\n const signedOutSession = await getSession.call(clientApp);\n\n expect(signedOutSession.sessionKey).toBe(\"not-logged-in\");\n expect(signedOutSession).not.toBe(authenticatedSession);\n expect(signedOutSession.getAccessTokenIfNotExpiredYet(0, null)).toBeNull();\n });\n\n it(\"exchanges provider tokens and preserves the session across token rotations\", async () => {\n let providerSessionId = \"clerk-session\";\n let notifyProviderChange: (() => void) | undefined;\n let exchangeCount = 0;\n const externalTokenStore = clerkTokenStore({\n getSessionId: () => providerSessionId,\n getToken: async () => \"clerk-token\",\n subscribe: (callback) => {\n notifyProviderChange = callback;\n return () => {};\n },\n });\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: externalTokenStore,\n redirectMethod: \"none\",\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n Reflect.set(clientInterface, \"exchangeExternalAuthToken\", async () => {\n exchangeCount += 1;\n return {\n status: \"ok\",\n data: {\n accessToken: createAccessTokenString(\"hexclave-external-session\", exchangeCount),\n sessionId: \"hexclave-external-session\",\n userId: \"user-id\",\n },\n };\n });\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const firstSession = await getSession.call(clientApp);\n expect(firstSession.sessionKey).toBe(\"external-clerk-integration-clerk-session\");\n await firstSession.getOrFetchLikelyValidTokens(20_000, null);\n expect(exchangeCount).toBe(1);\n\n notifyProviderChange?.();\n const refreshedSession = await getSession.call(clientApp);\n expect(refreshedSession).toBe(firstSession);\n await refreshedSession.getOrFetchLikelyValidTokens(20_000, null);\n expect(exchangeCount).toBe(2);\n\n providerSessionId = \"another-clerk-session\";\n notifyProviderChange?.();\n const switchedSession = await getSession.call(clientApp);\n expect(switchedSession).not.toBe(firstSession);\n expect(switchedSession.sessionKey).toBe(\"external-clerk-integration-another-clerk-session\");\n });\n\n it(\"retries a rejected exchange once with a fresh provider token before giving up\", async () => {\n let tokenCounter = 0;\n const exchangedTokens: string[] = [];\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => \"clerk-session\",\n getToken: async () => `clerk-token-${++tokenCounter}`,\n }),\n redirectMethod: \"none\",\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n Reflect.set(clientInterface, \"exchangeExternalAuthToken\", async (_providerId: string, token: string) => {\n exchangedTokens.push(token);\n // The first provider token is treated as expired-in-flight; the retried, freshly fetched one succeeds.\n if (exchangedTokens.length === 1) {\n return { status: \"error\", error: new KnownErrors.InvalidExternalAuthToken(\"malformed_token\") };\n }\n return {\n status: \"ok\",\n data: {\n accessToken: createAccessTokenString(\"hexclave-external-session\"),\n sessionId: \"hexclave-external-session\",\n userId: \"user-id\",\n },\n };\n });\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const session = await getSession.call(clientApp);\n const tokens = await session.getOrFetchLikelyValidTokens(20_000, null);\n expect(tokens).not.toBeNull();\n expect(exchangedTokens).toEqual([\"clerk-token-1\", \"clerk-token-2\"]);\n expect(session.isKnownToBeInvalid()).toBe(false);\n });\n\n it(\"does not retry into a different provider session after an exchange rejection\", async () => {\n let providerSessionId: string | null = \"clerk-session-a\";\n let exchangeCount = 0;\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => providerSessionId,\n getToken: async () => \"clerk-token\",\n }),\n redirectMethod: \"none\",\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n Reflect.set(clientInterface, \"exchangeExternalAuthToken\", async () => {\n exchangeCount += 1;\n providerSessionId = \"clerk-session-b\";\n return { status: \"error\", error: new KnownErrors.InvalidExternalAuthToken(\"malformed_token\") };\n });\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const session = await getSession.call(clientApp);\n await expect(session.getOrFetchLikelyValidTokens(20_000, null)).rejects.toThrow(\"provider session changed\");\n expect(exchangeCount).toBe(1);\n expect(session.isKnownToBeInvalid()).toBe(false);\n expect(session.getAccessTokenIfNotExpiredYet(0, null)).toBeNull();\n });\n\n it(\"does not refresh a session after the provider switches before refresh starts\", async () => {\n let providerSessionId: string | null = \"clerk-session-a\";\n let exchangeCount = 0;\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => providerSessionId,\n getToken: async () => \"clerk-token\",\n }),\n redirectMethod: \"none\",\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n Reflect.set(clientInterface, \"exchangeExternalAuthToken\", async () => {\n exchangeCount += 1;\n return {\n status: \"ok\",\n data: {\n accessToken: createAccessTokenString(\"hexclave-external-session\"),\n sessionId: \"hexclave-external-session\",\n userId: \"user-id\",\n },\n };\n });\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const session = await getSession.call(clientApp);\n providerSessionId = \"clerk-session-b\";\n\n await expect(session.getOrFetchLikelyValidTokens(20_000, null)).rejects.toThrow(\"provider session changed\");\n expect(exchangeCount).toBe(0);\n expect(session.getAccessTokenIfNotExpiredYet(0, null)).toBeNull();\n expect(session.isKnownToBeInvalid()).toBe(false);\n });\n\n it(\"does not install a token when the provider switches accounts mid-exchange\", async () => {\n let providerSessionId: string | null = \"clerk-session-a\";\n let resolveExchange: ((value: string) => void) | undefined;\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => providerSessionId,\n getToken: async () => \"clerk-token\",\n }),\n redirectMethod: \"none\",\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n Reflect.set(clientInterface, \"exchangeExternalAuthToken\", () => new Promise((resolve) => {\n resolveExchange = (accessToken) => resolve({\n status: \"ok\",\n data: {\n accessToken,\n sessionId: \"hexclave-external-session\",\n userId: \"user-id\",\n },\n });\n }));\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const session = await getSession.call(clientApp);\n const pending = session.getOrFetchLikelyValidTokens(20_000, null);\n await vi.waitFor(() => expect(resolveExchange).toBeDefined());\n providerSessionId = \"clerk-session-b\";\n resolveExchange?.(createAccessTokenString(\"hexclave-external-session\"));\n\n await expect(pending).rejects.toThrow(\"provider session changed\");\n expect(session.isKnownToBeInvalid()).toBe(false);\n // The rejected exchange must not have left the other account's token behind on this session.\n expect(session.getAccessTokenIfNotExpiredYet(0, null)).toBeNull();\n });\n\n it(\"does not install a token when the provider switches away and back during token retrieval\", async () => {\n let providerSessionId = \"clerk-session-a\";\n let notifyProviderChange: (() => void) | undefined;\n let resolveProviderToken: ((token: string) => void) | undefined;\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => providerSessionId,\n getToken: () => new Promise(resolve => {\n resolveProviderToken = resolve;\n }),\n subscribe: callback => {\n notifyProviderChange = callback;\n return () => {};\n },\n }),\n redirectMethod: \"none\",\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n Reflect.set(clientInterface, \"exchangeExternalAuthToken\", async () => ({\n status: \"ok\",\n data: {\n accessToken: createAccessTokenString(\"hexclave-external-session\"),\n sessionId: \"hexclave-external-session\",\n userId: \"user-id\",\n },\n }));\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const session = await getSession.call(clientApp);\n const pending = session.getOrFetchLikelyValidTokens(20_000, null);\n await vi.waitFor(() => expect(resolveProviderToken).toBeDefined());\n\n providerSessionId = \"clerk-session-b\";\n notifyProviderChange?.();\n providerSessionId = \"clerk-session-a\";\n notifyProviderChange?.();\n resolveProviderToken?.(\"clerk-token\");\n\n await expect(pending).rejects.toThrow(\"provider session changed\");\n expect(session.getAccessTokenIfNotExpiredYet(0, null)).toBeNull();\n expect(session.isKnownToBeInvalid()).toBe(false);\n });\n\n it(\"surfaces the rejection without invalidating the session when the retried exchange is rejected too\", async () => {\n let exchangeCount = 0;\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => \"clerk-session\",\n getToken: async () => \"clerk-token\",\n }),\n redirectMethod: \"none\",\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n Reflect.set(clientInterface, \"exchangeExternalAuthToken\", async () => {\n exchangeCount += 1;\n return { status: \"error\", error: new KnownErrors.InvalidExternalAuthToken(\"malformed_token\") };\n });\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const session = await getSession.call(clientApp);\n await expect(session.getOrFetchLikelyValidTokens(20_000, null)).rejects.toBeInstanceOf(KnownErrors.InvalidExternalAuthToken);\n expect(exchangeCount).toBe(2);\n // A repeated rejection is a misconfiguration signal, not a sign-out: the session must stay usable\n // so that fixing the configuration recovers it.\n expect(session.isKnownToBeInvalid()).toBe(false);\n });\n\n it(\"rejects first-party sign-in with Hexclave tokens for an app with an external token store\", async () => {\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => \"clerk-session\",\n getToken: async () => \"clerk-token\",\n }),\n redirectMethod: \"none\",\n });\n\n const signInToAccountWithTokens = Reflect.get(clientApp, \"_signInToAccountWithTokens\");\n await expect(signInToAccountWithTokens.call(clientApp, {\n accessToken: createAccessTokenString(\"unrelated-session\"),\n refreshToken: \"unrelated-refresh-token\",\n })).rejects.toThrow(\"cannot sign in with Hexclave tokens\");\n });\n\n it(\"treats a missing provider token as transient while the provider still reports a session\", async () => {\n let providerToken: string | null = null;\n let exchangeCount = 0;\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => \"clerk-session\",\n getToken: async () => providerToken,\n }),\n redirectMethod: \"none\",\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n Reflect.set(clientInterface, \"exchangeExternalAuthToken\", async () => {\n exchangeCount += 1;\n return {\n status: \"ok\",\n data: {\n accessToken: createAccessTokenString(\"hexclave-external-session\"),\n sessionId: \"hexclave-external-session\",\n userId: \"user-id\",\n },\n };\n });\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const session = await getSession.call(clientApp);\n // The provider SDK is still initializing (session reported, but no token yet): the fetch must\n // fail loudly instead of permanently invalidating the session.\n await expect(session.getOrFetchLikelyValidTokens(20_000, null)).rejects.toThrow(\"external token store returned no token\");\n expect(session.isKnownToBeInvalid()).toBe(false);\n expect(exchangeCount).toBe(0);\n\n providerToken = \"clerk-token\";\n const tokens = await session.getOrFetchLikelyValidTokens(20_000, null);\n expect(tokens).not.toBeNull();\n expect(exchangeCount).toBe(1);\n });\n\n it(\"rejects signing out an external session without revoking the Hexclave session\", async () => {\n const clientApp = new StackClientApp({\n automaticSideEffects: false,\n baseUrl: \"http://localhost:12345\",\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"stack-pk-test\",\n tokenStore: clerkTokenStore({\n getSessionId: () => \"clerk-session\",\n getToken: async () => \"clerk-token\",\n }),\n redirectMethod: \"none\",\n });\n const clientInterface = Reflect.get(clientApp, \"_interface\");\n let hexclaveSignOutCount = 0;\n Reflect.set(clientInterface, \"signOut\", async () => {\n hexclaveSignOutCount += 1;\n });\n Reflect.set(clientApp, \"_redirectToDefaultAfterSignOut\", async () => {});\n\n const getSession = Reflect.get(clientApp, \"_getSession\");\n const session = await getSession.call(clientApp);\n const signOut = Reflect.get(clientApp, \"_signOut\");\n await expect(signOut.call(clientApp, session)).rejects.toThrow(\n \"Cannot sign out an externally authenticated session through Hexclave\",\n );\n\n expect(hexclaveSignOutCount).toBe(0);\n });\n});\n"],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ let vitest = require("vitest");
2
+ let _hexclave_shared = require("@hexclave/shared");
3
+ let _hexclave_shared_dist_utils_results = require("@hexclave/shared/dist/utils/results");
4
+ let ___interfaces_client_app_js = require("../interfaces/client-app.js");
5
+ //#region src/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.ts
6
+ const baseOptions = {
7
+ baseUrl: "http://localhost:12345",
8
+ projectId: "00000000-0000-4000-8000-000000000000",
9
+ publishableClientKey: "stack-pk-test",
10
+ tokenStore: "memory",
11
+ redirectMethod: "none",
12
+ noAutomaticPrefetch: true
13
+ };
14
+ const analyticsAppDisabledMessage = "initAnalytics() was called, but the Analytics app is not enabled on this Hexclave project";
15
+ function simulateAnalyticsUploadResult(app, result) {
16
+ const handler = Reflect.get(app, "_handleAnalyticsUploadResult");
17
+ if (typeof handler !== "function") throw new Error("Expected HexclaveClientApp to have a _handleAnalyticsUploadResult method");
18
+ handler.call(app, result);
19
+ }
20
+ (0, vitest.describe)("HexclaveClientApp.initAnalytics", () => {
21
+ (0, vitest.afterEach)(() => {
22
+ vitest.vi.restoreAllMocks();
23
+ });
24
+ (0, vitest.it)("does not throw when analytics are configured", () => {
25
+ const app = new ___interfaces_client_app_js.HexclaveClientApp(baseOptions);
26
+ (0, vitest.expect)(() => app.initAnalytics()).not.toThrow();
27
+ });
28
+ (0, vitest.it)("throws when analytics are disabled with the analytics option", () => {
29
+ const app = new ___interfaces_client_app_js.HexclaveClientApp({
30
+ ...baseOptions,
31
+ analytics: { enabled: false }
32
+ });
33
+ (0, vitest.expect)(() => app.initAnalytics()).toThrow("`analytics.enabled` constructor option is set to false");
34
+ });
35
+ (0, vitest.it)("does not throw when only session replays are disabled", () => {
36
+ const app = new ___interfaces_client_app_js.HexclaveClientApp({
37
+ ...baseOptions,
38
+ analytics: { replays: { enabled: false } }
39
+ });
40
+ (0, vitest.expect)(() => app.initAnalytics()).not.toThrow();
41
+ });
42
+ (0, vitest.it)("reports an error when the backend rejects uploads after initAnalytics() was called", () => {
43
+ const consoleError = vitest.vi.spyOn(console, "error").mockImplementation(() => {});
44
+ const app = new ___interfaces_client_app_js.HexclaveClientApp(baseOptions);
45
+ app.initAnalytics();
46
+ simulateAnalyticsUploadResult(app, _hexclave_shared_dist_utils_results.Result.error(new _hexclave_shared.KnownErrors.AnalyticsNotEnabled()));
47
+ simulateAnalyticsUploadResult(app, _hexclave_shared_dist_utils_results.Result.error(new _hexclave_shared.KnownErrors.AnalyticsNotEnabled()));
48
+ (0, vitest.expect)(consoleError.mock.calls.filter((args) => args.some((arg) => String(arg).includes(analyticsAppDisabledMessage)))).toHaveLength(1);
49
+ });
50
+ (0, vitest.it)("throws from initAnalytics() when the backend already rejected uploads before it was called", () => {
51
+ const consoleError = vitest.vi.spyOn(console, "error").mockImplementation(() => {});
52
+ const app = new ___interfaces_client_app_js.HexclaveClientApp(baseOptions);
53
+ simulateAnalyticsUploadResult(app, _hexclave_shared_dist_utils_results.Result.error(new _hexclave_shared.KnownErrors.AnalyticsNotEnabled()));
54
+ (0, vitest.expect)(consoleError.mock.calls.some((args) => args.some((arg) => String(arg).includes(analyticsAppDisabledMessage)))).toBe(false);
55
+ (0, vitest.expect)(() => app.initAnalytics()).toThrow(analyticsAppDisabledMessage);
56
+ });
57
+ (0, vitest.it)("ignores upload failures unrelated to the Analytics app being disabled", () => {
58
+ const app = new ___interfaces_client_app_js.HexclaveClientApp(baseOptions);
59
+ simulateAnalyticsUploadResult(app, _hexclave_shared_dist_utils_results.Result.error(/* @__PURE__ */ new Error("network error")));
60
+ (0, vitest.expect)(() => app.initAnalytics()).not.toThrow();
61
+ });
62
+ });
63
+ //#endregion
64
+
65
+ //# sourceMappingURL=client-app-impl.init-analytics.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-app-impl.init-analytics.test.js","names":["HexclaveClientApp","vi","Result","KnownErrors"],"sources":["../../../../../src/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.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 { KnownErrors } from \"@hexclave/shared\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\nimport { afterEach, describe, expect, it, vi } from \"vitest\";\nimport { HexclaveClientApp, HexclaveClientAppConstructorOptions } from \"../interfaces/client-app\";\n\nconst baseOptions = {\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} satisfies HexclaveClientAppConstructorOptions<true, string>;\n\nconst analyticsAppDisabledMessage = \"initAnalytics() was called, but the Analytics app is not enabled on this Hexclave project\";\n\n// The backend's AnalyticsNotEnabled rejection only reaches the client through the event tracker's and session\n// recorder's upload callbacks, which only exist in a browser; this simulates such a rejection arriving.\nfunction simulateAnalyticsUploadResult(app: HexclaveClientApp, result: Result<Response, Error>) {\n const handler: unknown = Reflect.get(app, \"_handleAnalyticsUploadResult\");\n if (typeof handler !== \"function\") {\n throw new Error(\"Expected HexclaveClientApp to have a _handleAnalyticsUploadResult method\");\n }\n handler.call(app, result);\n}\n\ndescribe(\"HexclaveClientApp.initAnalytics\", () => {\n afterEach(() => {\n vi.restoreAllMocks();\n });\n\n it(\"does not throw when analytics are configured\", () => {\n const app = new HexclaveClientApp(baseOptions);\n expect(() => app.initAnalytics()).not.toThrow();\n });\n\n it(\"throws when analytics are disabled with the analytics option\", () => {\n const app = new HexclaveClientApp({ ...baseOptions, analytics: { enabled: false } });\n expect(() => app.initAnalytics()).toThrow(\"`analytics.enabled` constructor option is set to false\");\n });\n\n it(\"does not throw when only session replays are disabled\", () => {\n const app = new HexclaveClientApp({ ...baseOptions, analytics: { replays: { enabled: false } } });\n expect(() => app.initAnalytics()).not.toThrow();\n });\n\n it(\"reports an error when the backend rejects uploads after initAnalytics() was called\", () => {\n const consoleError = vi.spyOn(console, \"error\").mockImplementation(() => {});\n const app = new HexclaveClientApp(baseOptions);\n app.initAnalytics();\n\n simulateAnalyticsUploadResult(app, Result.error(new KnownErrors.AnalyticsNotEnabled()));\n // Repeated rejections must not report the error again.\n simulateAnalyticsUploadResult(app, Result.error(new KnownErrors.AnalyticsNotEnabled()));\n\n const reports = consoleError.mock.calls.filter((args) => args.some((arg) => String(arg).includes(analyticsAppDisabledMessage)));\n expect(reports).toHaveLength(1);\n });\n\n it(\"throws from initAnalytics() when the backend already rejected uploads before it was called\", () => {\n const consoleError = vi.spyOn(console, \"error\").mockImplementation(() => {});\n const app = new HexclaveClientApp(baseOptions);\n\n simulateAnalyticsUploadResult(app, Result.error(new KnownErrors.AnalyticsNotEnabled()));\n expect(consoleError.mock.calls.some((args) => args.some((arg) => String(arg).includes(analyticsAppDisabledMessage)))).toBe(false);\n\n expect(() => app.initAnalytics()).toThrow(analyticsAppDisabledMessage);\n });\n\n it(\"ignores upload failures unrelated to the Analytics app being disabled\", () => {\n const app = new HexclaveClientApp(baseOptions);\n simulateAnalyticsUploadResult(app, Result.error(new Error(\"network error\")));\n expect(() => app.initAnalytics()).not.toThrow();\n });\n});\n"],"mappings":";;;;;AASA,MAAM,cAAc;CAClB,SAAS;CACT,WAAW;CACX,sBAAsB;CACtB,YAAY;CACZ,gBAAgB;CAChB,qBAAqB;AACvB;AAEA,MAAM,8BAA8B;AAIpC,SAAS,8BAA8B,KAAwB,QAAiC;CAC9F,MAAM,UAAmB,QAAQ,IAAI,KAAK,8BAA8B;CACxE,IAAI,OAAO,YAAY,YACrB,MAAM,IAAI,MAAM,0EAA0E;CAE5F,QAAQ,KAAK,KAAK,MAAM;AAC1B;qBAES,yCAAyC;CAChD,CAAA,GAAA,OAAA,UAAA,OAAgB;EACd,OAAA,GAAG,gBAAgB;CACrB,CAAC;CAED,CAAA,GAAA,OAAA,GAAA,CAAG,sDAAsD;EACvD,MAAM,MAAM,IAAIA,4BAAAA,kBAAkB,WAAW;EAC7C,CAAA,GAAA,OAAA,OAAA,OAAa,IAAI,cAAc,CAAC,CAAC,CAAC,IAAI,QAAQ;CAChD,CAAC;CAED,CAAA,GAAA,OAAA,GAAA,CAAG,sEAAsE;EACvE,MAAM,MAAM,IAAIA,4BAAAA,kBAAkB;GAAE,GAAG;GAAa,WAAW,EAAE,SAAS,MAAM;EAAE,CAAC;EACnF,CAAA,GAAA,OAAA,OAAA,OAAa,IAAI,cAAc,CAAC,CAAC,CAAC,QAAQ,wDAAwD;CACpG,CAAC;CAED,CAAA,GAAA,OAAA,GAAA,CAAG,+DAA+D;EAChE,MAAM,MAAM,IAAIA,4BAAAA,kBAAkB;GAAE,GAAG;GAAa,WAAW,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE;EAAE,CAAC;EAChG,CAAA,GAAA,OAAA,OAAA,OAAa,IAAI,cAAc,CAAC,CAAC,CAAC,IAAI,QAAQ;CAChD,CAAC;CAED,CAAA,GAAA,OAAA,GAAA,CAAG,4FAA4F;EAC7F,MAAM,eAAeC,OAAAA,GAAG,MAAM,SAAS,OAAO,CAAC,CAAC,yBAAyB,CAAC,CAAC;EAC3E,MAAM,MAAM,IAAID,4BAAAA,kBAAkB,WAAW;EAC7C,IAAI,cAAc;EAElB,8BAA8B,KAAKE,oCAAAA,OAAO,MAAM,IAAIC,iBAAAA,YAAY,oBAAoB,CAAC,CAAC;EAEtF,8BAA8B,KAAKD,oCAAAA,OAAO,MAAM,IAAIC,iBAAAA,YAAY,oBAAoB,CAAC,CAAC;EAGtF,CAAA,GAAA,OAAA,OAAA,CADgB,aAAa,KAAK,MAAM,QAAQ,SAAS,KAAK,MAAM,QAAQ,OAAO,GAAG,CAAC,CAAC,SAAS,2BAA2B,CAAC,CAChH,CAAC,CAAC,CAAC,aAAa,CAAC;CAChC,CAAC;CAED,CAAA,GAAA,OAAA,GAAA,CAAG,oGAAoG;EACrG,MAAM,eAAeF,OAAAA,GAAG,MAAM,SAAS,OAAO,CAAC,CAAC,yBAAyB,CAAC,CAAC;EAC3E,MAAM,MAAM,IAAID,4BAAAA,kBAAkB,WAAW;EAE7C,8BAA8B,KAAKE,oCAAAA,OAAO,MAAM,IAAIC,iBAAAA,YAAY,oBAAoB,CAAC,CAAC;EACtF,CAAA,GAAA,OAAA,OAAA,CAAO,aAAa,KAAK,MAAM,MAAM,SAAS,KAAK,MAAM,QAAQ,OAAO,GAAG,CAAC,CAAC,SAAS,2BAA2B,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK;EAEhI,CAAA,GAAA,OAAA,OAAA,OAAa,IAAI,cAAc,CAAC,CAAC,CAAC,QAAQ,2BAA2B;CACvE,CAAC;CAED,CAAA,GAAA,OAAA,GAAA,CAAG,+EAA+E;EAChF,MAAM,MAAM,IAAIH,4BAAAA,kBAAkB,WAAW;EAC7C,8BAA8B,KAAKE,oCAAAA,OAAO,sBAAM,IAAI,MAAM,eAAe,CAAC,CAAC;EAC3E,CAAA,GAAA,OAAA,OAAA,OAAa,IAAI,cAAc,CAAC,CAAC,CAAC,IAAI,QAAQ;CAChD,CAAC;AACH,CAAC"}
@@ -13,7 +13,6 @@ let _hexclave_shared_dist_utils_objects = require("@hexclave/shared/dist/utils/o
13
13
  let _hexclave_shared_dist_utils_bytes = require("@hexclave/shared/dist/utils/bytes");
14
14
  let ______common_js = require("../../common.js");
15
15
  let ______projects_index_js = require("../../projects/index.js");
16
- let _hexclave_shared_dist_sessions = require("@hexclave/shared/dist/sessions");
17
16
  let _hexclave_shared_dist_utils_stores = require("@hexclave/shared/dist/utils/stores");
18
17
  let __redirect_page_urls_js = require("./redirect-page-urls.js");
19
18
  let _hexclave_shared_dist_utils_browser_action_snippets = require("@hexclave/shared/dist/utils/browser-action-snippets");
@@ -35,6 +34,7 @@ let ______teams_index_js = require("../../teams/index.js");
35
34
  let ______url_targets_js = require("../../url-targets.js");
36
35
  let ______users_index_js = require("../../users/index.js");
37
36
  let __event_tracker_js = require("./event-tracker.js");
37
+ let __external_token_store_session_adapter_js = require("./external-token-store-session-adapter.js");
38
38
  let __redirect_back_state_js = require("./redirect-back-state.js");
39
39
  let __redirect_loop_breaker_js = require("./redirect-loop-breaker.js");
40
40
  let __session_replay_js = require("./session-replay.js");
@@ -137,6 +137,9 @@ function throwSetupError(error) {
137
137
  (0, ____________setup_error_overlay_index_js.showSetupErrorOverlay)(error);
138
138
  throw error;
139
139
  }
140
+ function createAnalyticsAppDisabledOnProjectError() {
141
+ return /* @__PURE__ */ new Error("initAnalytics() was called, but the Analytics app is not enabled on this Hexclave project, so the backend rejects all analytics uploads. Enable the Analytics app in the Hexclave dashboard (or in hexclave.config.ts), or remove the initAnalytics() call if this is intentional.");
142
+ }
140
143
  function getRedirectHelperInstruction(handlerName) {
141
144
  if (handlerName === "handler") return "Use a page-specific redirect helper such as app.redirectToSignIn() instead.";
142
145
  return `Use app.${`redirectTo${handlerName.slice(0, 1).toUpperCase()}${handlerName.slice(1)}`}() instead.`;
@@ -278,6 +281,8 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
278
281
  this._uniqueIdentifier = void 0;
279
282
  this._sessionRecorder = null;
280
283
  this._eventTracker = null;
284
+ this._initAnalyticsCalled = false;
285
+ this._analyticsAppDisabledOnProject = false;
281
286
  this._pendingSignOut = null;
282
287
  this.__DEMO_ENABLE_SLIGHT_FETCH_DELAY = false;
283
288
  this._ownedAdminApps = new _hexclave_shared_dist_utils_maps.DependenciesMap();
@@ -477,6 +482,10 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
477
482
  prepareRequest: async () => {}
478
483
  });
479
484
  }
485
+ this._externalTokenStoreSessionAdapter = new __external_token_store_session_adapter_js.ExternalTokenStoreSessionAdapter((sessionOptions) => this._interface.createSession(sessionOptions), async (providerId, token) => {
486
+ const exchange = await this._interface.exchangeExternalAuthToken(providerId, token);
487
+ return _hexclave_shared_dist_utils_results.Result.orThrow(exchange).accessToken;
488
+ });
480
489
  this._tokenStoreInit = resolvedOptions.tokenStore;
481
490
  this._redirectMethod = resolvedOptions.redirectMethod || ((0, _hexclave_shared_dist_utils_env.isBrowserLike)() ? "window" : "none");
482
491
  this._urlOptions = resolvedOptions.urls ?? {};
@@ -500,22 +509,22 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
500
509
  })) return await this._getSession();
501
510
  return (await this.getUser({ or: "anonymous" }))._internalSession;
502
511
  };
503
- const analyticsEnabled = this._analyticsOptions?.enabled !== false;
512
+ const recordsAnalytics = this._getAnalyticsDisabledReason() == null && (0, _hexclave_shared_dist_utils_env.isBrowserLike)();
504
513
  const sessionReplayOptions = (0, __session_replay_js.getSessionReplayOptions)(this._analyticsOptions);
505
- if (analyticsEnabled && (0, _hexclave_shared_dist_utils_env.isBrowserLike)() && this._hasPersistentTokenStore() && sessionReplayOptions.enabled) {
514
+ if (recordsAnalytics && sessionReplayOptions.enabled) {
506
515
  this._sessionRecorder = new __session_replay_js.SessionRecorder({
507
516
  projectId: this.projectId,
508
517
  sendBatch: async (body, opts) => {
509
- return await this._interface.sendSessionReplayBatch(body, await getAnalyticsSession(), opts);
518
+ return this._handleAnalyticsUploadResult(await this._interface.sendSessionReplayBatch(body, await getAnalyticsSession(), opts));
510
519
  }
511
520
  }, sessionReplayOptions);
512
521
  this._sessionRecorder.start();
513
522
  }
514
- if (analyticsEnabled && (0, _hexclave_shared_dist_utils_env.isBrowserLike)() && this._hasPersistentTokenStore()) {
523
+ if (recordsAnalytics) {
515
524
  this._eventTracker = new __event_tracker_js.EventTracker({
516
525
  projectId: this.projectId,
517
526
  sendBatch: async (body, opts) => {
518
- return await this._interface.sendAnalyticsEventBatch(body, await getAnalyticsSession(), opts);
527
+ return this._handleAnalyticsUploadResult(await this._interface.sendAnalyticsEventBatch(body, await getAnalyticsSession(), opts));
519
528
  }
520
529
  });
521
530
  this._eventTracker.start();
@@ -1083,6 +1092,9 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
1083
1092
  case "memory": return this._memoryTokenStore;
1084
1093
  default:
1085
1094
  if (tokenStoreInit === null) return (0, __common_js.createEmptyTokenStore)();
1095
+ else if (typeof tokenStoreInit === "object" && (0, __external_token_store_session_adapter_js.isExternalTokenStore)(tokenStoreInit)) return this._externalTokenStoreSessionAdapter.getOrCreateTokenStore(tokenStoreInit, (store, previousSessionKey) => {
1096
+ this._sessionsByTokenStoreAndSessionKey.get(store)?.get(previousSessionKey)?.suggestAccessTokenExpired();
1097
+ });
1086
1098
  else if (typeof tokenStoreInit === "object" && "headers" in tokenStoreInit) {
1087
1099
  if (this._requestTokenStores.has(tokenStoreInit)) return this._requestTokenStores.get(tokenStoreInit);
1088
1100
  const authorizationHeader = getHeaderValueFromRequestLikeHeaders(tokenStoreInit.headers, "authorization");
@@ -1124,27 +1136,27 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
1124
1136
  throw new Error(`Invalid token store ${tokenStoreInit}`);
1125
1137
  }
1126
1138
  }
1139
+ _getSessionKeyForTokenStore(tokenStore, tokenObj) {
1140
+ return this._externalTokenStoreSessionAdapter.getSessionKey(tokenStore, tokenObj);
1141
+ }
1127
1142
  /**
1128
1143
  * @param overrideTokenObj The tokens to build the session for, if they haven't been written to the token store yet
1129
1144
  * (so we can warm a sign-in's session before the token store publishes it).
1130
1145
  */
1131
1146
  _getSessionFromTokenStore(tokenStore, overrideTokenObj) {
1132
1147
  const tokenObj = overrideTokenObj ?? tokenStore.get();
1133
- const sessionKey = _hexclave_shared_dist_sessions.InternalSession.calculateSessionKey(tokenObj);
1148
+ const sessionKey = this._getSessionKeyForTokenStore(tokenStore, tokenObj);
1134
1149
  const existing = sessionKey ? this._sessionsByTokenStoreAndSessionKey.get(tokenStore)?.get(sessionKey) : null;
1135
1150
  if (existing) return existing;
1136
- const session = this._interface.createSession({
1137
- refreshToken: tokenObj.refreshToken,
1138
- accessToken: tokenObj.accessToken
1139
- });
1151
+ const session = this._externalTokenStoreSessionAdapter.createSessionForTokenStore(tokenStore, tokenObj, sessionKey);
1140
1152
  session.onAccessTokenChange((newAccessToken) => {
1141
- tokenStore.update((old) => _hexclave_shared_dist_sessions.InternalSession.calculateSessionKey(old) === sessionKey ? {
1153
+ tokenStore.update((old) => this._getSessionKeyForTokenStore(tokenStore, old) === sessionKey ? {
1142
1154
  ...old,
1143
1155
  accessToken: newAccessToken?.token ?? null
1144
1156
  } : old);
1145
1157
  });
1146
1158
  session.onInvalidate(() => {
1147
- tokenStore.update((old) => _hexclave_shared_dist_sessions.InternalSession.calculateSessionKey(old) === sessionKey ? {
1159
+ tokenStore.update((old) => this._getSessionKeyForTokenStore(tokenStore, old) === sessionKey ? {
1148
1160
  ...old,
1149
1161
  accessToken: null,
1150
1162
  refreshToken: null
@@ -1162,6 +1174,7 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
1162
1174
  }
1163
1175
  async _signInToAccountWithTokens(tokens) {
1164
1176
  if (!("accessToken" in tokens) || !("refreshToken" in tokens)) throw new _hexclave_shared_dist_utils_errors.HexclaveAssertionError("Invalid tokens object; can't sign in with this", { tokens });
1177
+ if (this.isExternalAuthApp()) throw new Error("This app authenticates through an external provider and cannot sign in with Hexclave tokens. Sign in through the provider instead.");
1165
1178
  const attemptId = ++this._signInAttemptCounter;
1166
1179
  const tokenStore = this._getOrCreateTokenStore(await this._createCookieHelper());
1167
1180
  const session = this._getSessionFromTokenStore(tokenStore, tokens);
@@ -1510,6 +1523,7 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
1510
1523
  emailAuthEnabled: crud.auth_with_email,
1511
1524
  otpAuthEnabled: crud.otp_auth_enabled,
1512
1525
  oauthProviders: crud.oauth_providers,
1526
+ externalAuthProviders: crud.external_auth_providers,
1513
1527
  passkeyAuthEnabled: crud.passkey_auth_enabled,
1514
1528
  isMultiFactorRequired: crud.requires_totp_mfa,
1515
1529
  isAnonymous: crud.is_anonymous,
@@ -2318,6 +2332,37 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
2318
2332
  }
2319
2333
  return crud && this._currentUserFromCrud(crud, session);
2320
2334
  }
2335
+ isExternalAuthApp() {
2336
+ return typeof this._tokenStoreInit === "object" && this._tokenStoreInit != null && (0, __external_token_store_session_adapter_js.isExternalTokenStore)(this._tokenStoreInit);
2337
+ }
2338
+ initAnalytics() {
2339
+ this._initAnalyticsCalled = true;
2340
+ const disabledReason = this._getAnalyticsDisabledReason();
2341
+ if (disabledReason != null) throw new Error(`initAnalytics() was called, but this Hexclave app will not record analytics because ${disabledReason}. Remove the initAnalytics() call if this is intentional.`);
2342
+ if (this._analyticsAppDisabledOnProject) throw createAnalyticsAppDisabledOnProjectError();
2343
+ if ((0, _hexclave_shared_dist_utils_env.isBrowserLike)() && this._eventTracker == null) throw new _hexclave_shared_dist_utils_errors.HexclaveAssertionError("initAnalytics() was called in the browser and _getAnalyticsDisabledReason() returned null, but the constructor did not start the event tracker. The two must agree on when analytics are recorded.");
2344
+ }
2345
+ /**
2346
+ * Returns why this app will never record analytics, or null if it records them whenever it runs in a browser. The
2347
+ * constructor and initAnalytics() both use this, so that the conditions under which analytics start and the
2348
+ * conditions under which initAnalytics() throws can't drift apart.
2349
+ *
2350
+ * Whether the Analytics app is enabled on the project is not part of this, as the client only learns that once the
2351
+ * backend rejects an upload; see _handleAnalyticsUploadResult.
2352
+ */
2353
+ _getAnalyticsDisabledReason() {
2354
+ if (this._analyticsOptions?.enabled === false) return "the `analytics.enabled` constructor option is set to false";
2355
+ if (this._options.automaticSideEffects === false) return "the `automaticSideEffects` constructor option is set to false, which also disables analytics";
2356
+ if (!this._hasPersistentTokenStore()) return "the `tokenStore` constructor option is null; analytics require a persistent token store such as \"cookie\" (or \"nextjs-cookie\" in Next.js)";
2357
+ return null;
2358
+ }
2359
+ _handleAnalyticsUploadResult(result) {
2360
+ if (result.status === "error" && (0, __session_replay_js.isAnalyticsNotEnabledError)(result.error) && !this._analyticsAppDisabledOnProject) {
2361
+ this._analyticsAppDisabledOnProject = true;
2362
+ if (this._initAnalyticsCalled) (0, _hexclave_shared_dist_utils_errors.captureError)("initAnalytics", createAnalyticsAppDisabledOnProjectError());
2363
+ }
2364
+ return result;
2365
+ }
2321
2366
  _getTokenPartialUserFromSession(session, options) {
2322
2367
  const accessToken = session.getAccessTokenIfNotExpiredYet(0, null);
2323
2368
  if (!accessToken) return null;
@@ -2328,6 +2373,7 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
2328
2373
  primaryEmail: accessToken.payload.email,
2329
2374
  displayName: accessToken.payload.name,
2330
2375
  primaryEmailVerified: accessToken.payload.email_verified,
2376
+ selectedTeamId: accessToken.payload.selected_team_id,
2331
2377
  isAnonymous,
2332
2378
  isMultiFactorRequired: accessToken.payload.requires_totp_mfa,
2333
2379
  isRestricted: accessToken.payload.is_restricted,
@@ -2337,11 +2383,14 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
2337
2383
  async _getPartialUserFromConvex(ctx) {
2338
2384
  const auth = await ctx.auth.getUserIdentity();
2339
2385
  if (!auth) return null;
2386
+ const selectedTeamId = auth.selected_team_id ?? null;
2387
+ if (selectedTeamId !== null && typeof selectedTeamId !== "string") throw new _hexclave_shared_dist_utils_errors.HexclaveAssertionError("Expected the Convex selected_team_id claim to be a string or null.");
2340
2388
  return {
2341
2389
  id: auth.subject,
2342
2390
  displayName: auth.name ?? null,
2343
2391
  primaryEmail: auth.email ?? null,
2344
2392
  primaryEmailVerified: auth.email_verified,
2393
+ selectedTeamId,
2345
2394
  isAnonymous: auth.is_anonymous,
2346
2395
  isMultiFactorRequired: auth.requires_totp_mfa,
2347
2396
  isRestricted: auth.is_restricted,
@@ -2681,6 +2730,8 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
2681
2730
  const previousSignOut = this._pendingSignOut;
2682
2731
  const signOutOperation = (async () => {
2683
2732
  await previousSignOut;
2733
+ const externalTokenStore = this._externalTokenStoreSessionAdapter.getExternalTokenStoreForSession(session);
2734
+ if (externalTokenStore != null) throw new _hexclave_shared_dist_utils_errors.HexclaveAssertionError(`Cannot sign out an externally authenticated session through Hexclave. Sign out through the ${externalTokenStore.providerId} provider instead.`);
2684
2735
  await this._interface.signOut(session);
2685
2736
  if (options?.redirectUrl) await this._redirectTo({
2686
2737
  url: options.redirectUrl,
@@ -2801,6 +2852,7 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
2801
2852
  return {
2802
2853
  toClientJson: () => {
2803
2854
  if (typeof this._redirectMethod !== "string") throw new _hexclave_shared_dist_utils_errors.HexclaveAssertionError("Cannot serialize to JSON from an application with a non-string redirect method");
2855
+ if (typeof this._tokenStoreInit === "object" && this._tokenStoreInit != null && (0, __external_token_store_session_adapter_js.isExternalTokenStore)(this._tokenStoreInit)) throw new _hexclave_shared_dist_utils_errors.HexclaveAssertionError("Cannot serialize an app with an external token store because its callbacks are not serializable. Construct this app in the client runtime instead.");
2804
2856
  const publishableClientKey = "publishableClientKey" in this._interface.options ? this._interface.options.publishableClientKey : void 0;
2805
2857
  return {
2806
2858
  baseUrl: this._options.baseUrl,