@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.
- package/dist/{admin-app-impl-Cxer4EIA.d.ts → admin-app-impl-Df-WtGg0.d.ts} +29 -13
- package/dist/admin-app-impl-Df-WtGg0.d.ts.map +1 -0
- package/dist/clickmap/clickmap-core.d.ts +1 -1
- package/dist/clickmap/index.d.ts +1 -1
- package/dist/{client-app-DLFxr4f7.d.ts → client-app-C7Tnf0n1.d.ts} +34 -6
- package/dist/client-app-C7Tnf0n1.d.ts.map +1 -0
- package/dist/{common-BDyEKSM6.d.ts → common-Dii3iHJR.d.ts} +15 -3
- package/dist/common-Dii3iHJR.d.ts.map +1 -0
- package/dist/{common-CRu2FsO9.d.ts → common-GgG4l8fG.d.ts} +2 -2
- package/dist/{common-CRu2FsO9.d.ts.map → common-GgG4l8fG.d.ts.map} +1 -1
- package/dist/dev-tool/dev-tool-core.d.ts +1 -1
- package/dist/dev-tool/index.d.ts +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +4 -4
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +18 -2
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.d.ts +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.js +382 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.js.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.d.ts +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.js +66 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.js.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js +66 -14
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/common.js +2 -2
- package/dist/esm/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.d.ts +31 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.d.ts.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.js +115 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.js.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.d.ts +16 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.js.map +1 -1
- package/dist/esm/lib/hexclave-app/common.d.ts +14 -2
- package/dist/esm/lib/hexclave-app/common.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/common.js +17 -1
- package/dist/esm/lib/hexclave-app/common.js.map +1 -1
- package/dist/esm/lib/hexclave-app/index.d.ts +2 -2
- package/dist/esm/lib/hexclave-app/index.js +2 -2
- package/dist/esm/lib/hexclave-app/users/index.d.ts +14 -1
- package/dist/esm/lib/hexclave-app/users/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/users/index.js.map +1 -1
- package/dist/{index-ZiEXZJBF.d.ts → index-BTa6Yu7N.d.ts} +2 -2
- package/dist/{index-ZiEXZJBF.d.ts.map → index-BTa6Yu7N.d.ts.map} +1 -1
- package/dist/{index-DLPccxVU.d.ts → index-DlOcbI9W.d.ts} +2 -2
- package/dist/{index-DLPccxVU.d.ts.map → index-DlOcbI9W.d.ts.map} +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.d.ts +1 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.js +381 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.js.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.d.ts +1 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.js +65 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.js.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js +65 -13
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/common.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/common.js +2 -2
- package/dist/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.d.ts +31 -0
- package/dist/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.d.ts.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.js +117 -0
- package/dist/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.js.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/index.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/redirect-page-urls.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/index.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/admin-app.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/client-app.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/client-app.js.map +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/server-app.d.ts +1 -1
- package/dist/lib/hexclave-app/common.d.ts +2 -2
- package/dist/lib/hexclave-app/common.js +19 -0
- package/dist/lib/hexclave-app/common.js.map +1 -1
- package/dist/lib/hexclave-app/customers/index.d.ts +1 -1
- package/dist/lib/hexclave-app/data-vault/index.d.ts +1 -1
- package/dist/lib/hexclave-app/index.d.ts +4 -4
- package/dist/lib/hexclave-app/index.js +18 -0
- package/dist/lib/hexclave-app/projects/index.d.ts +1 -1
- package/dist/lib/hexclave-app/teams/index.d.ts +1 -1
- package/dist/lib/hexclave-app/url-targets.d.ts +1 -1
- package/dist/lib/hexclave-app/users/index.d.ts +1 -1
- package/dist/lib/hexclave-app/users/index.js.map +1 -1
- package/dist/pushed-config-error-overlay/index.d.ts +1 -1
- package/dist/{redirect-page-urls-C2CXwCcv.d.ts → redirect-page-urls-DCSFN6dg.d.ts} +2 -2
- package/dist/{redirect-page-urls-C2CXwCcv.d.ts.map → redirect-page-urls-DCSFN6dg.d.ts.map} +1 -1
- package/dist/{url-targets-BJgkwPGW.d.ts → url-targets-DYLcHumF.d.ts} +2 -2
- package/dist/{url-targets-BJgkwPGW.d.ts.map → url-targets-DYLcHumF.d.ts.map} +1 -1
- package/package.json +2 -2
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.ts +425 -0
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.ts +79 -0
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.ts +124 -13
- package/src/lib/hexclave-app/apps/implementations/external-token-store-session-adapter.ts +190 -0
- package/src/lib/hexclave-app/apps/interfaces/client-app.ts +17 -0
- package/src/lib/hexclave-app/common.ts +33 -0
- package/src/lib/hexclave-app/index.ts +4 -0
- package/src/lib/hexclave-app/users/index.ts +13 -1
- package/dist/admin-app-impl-Cxer4EIA.d.ts.map +0 -1
- package/dist/client-app-DLFxr4f7.d.ts.map +0 -1
- package/dist/common-BDyEKSM6.d.ts.map +0 -1
package/dist/lib/hexclave-app/apps/implementations/client-app-impl.external-token-store.test.js.map
ADDED
|
@@ -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 @@
|
|
|
1
|
+
export { };
|
|
@@ -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":"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"}
|
|
@@ -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
|
|
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 (
|
|
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 (
|
|
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 =
|
|
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.
|
|
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) =>
|
|
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) =>
|
|
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,
|