@hexclave/next 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-MncU_Oxy.d.ts → admin-app-impl-CjDjJ3PS.d.ts} +29 -13
- package/dist/admin-app-impl-CjDjJ3PS.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-B0cTytS5.d.ts → client-app-jAjrgwSh.d.ts} +34 -6
- package/dist/client-app-jAjrgwSh.d.ts.map +1 -0
- package/dist/{common-B2gkWlXX.d.ts → common-BjAI7WW6.d.ts} +15 -3
- package/dist/common-BjAI7WW6.d.ts.map +1 -0
- package/dist/{common-BK_sydxF.d.ts → common-DWfeER0s.d.ts} +2 -2
- package/dist/{common-BK_sydxF.d.ts.map → common-DWfeER0s.d.ts.map} +1 -1
- package/dist/components/message-cards/predefined-message-card.js +4 -2
- package/dist/components/message-cards/predefined-message-card.js.map +1 -1
- package/dist/components/selected-team-switcher.d.ts +1 -1
- package/dist/components/team-icon.d.ts +1 -1
- package/dist/components/team-switcher.d.ts +1 -1
- package/dist/components/user-button.js +9 -9
- package/dist/components/user-button.js.map +1 -1
- package/dist/components/user-button.test.d.ts +1 -0
- package/dist/components/user-button.test.js +71 -0
- package/dist/components/user-button.test.js.map +1 -0
- package/dist/components-page/account-settings/payments/payments-page.d.ts +1 -1
- package/dist/components-page/account-settings/payments/payments-panel.d.ts +1 -1
- package/dist/components-page/account-settings/settings/sign-out-section.d.ts +1 -1
- package/dist/components-page/account-settings/settings/sign-out-section.d.ts.map +1 -1
- package/dist/components-page/account-settings/settings/sign-out-section.js +4 -1
- package/dist/components-page/account-settings/settings/sign-out-section.js.map +1 -1
- package/dist/components-page/account-settings/teams/leave-team-section.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-api-keys-section.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-display-name-section.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-member-invitation-section.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-member-list-section.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-page.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-profile-image-section.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-profile-user-section.d.ts +1 -1
- package/dist/components-page/hexclave-handler-client.d.ts +1 -1
- package/dist/components-page/hexclave-handler.d.ts +1 -1
- package/dist/components-page/onboarding.js +15 -13
- package/dist/components-page/onboarding.js.map +1 -1
- package/dist/components-page/onboarding.test.js +2 -1
- package/dist/components-page/onboarding.test.js.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/components/message-cards/predefined-message-card.js +4 -2
- package/dist/esm/components/message-cards/predefined-message-card.js.map +1 -1
- package/dist/esm/components/user-button.js +9 -9
- package/dist/esm/components/user-button.js.map +1 -1
- package/dist/esm/components/user-button.test.d.ts +1 -0
- package/dist/esm/components/user-button.test.js +70 -0
- package/dist/esm/components/user-button.test.js.map +1 -0
- package/dist/esm/components-page/account-settings/settings/sign-out-section.d.ts +1 -1
- package/dist/esm/components-page/account-settings/settings/sign-out-section.d.ts.map +1 -1
- package/dist/esm/components-page/account-settings/settings/sign-out-section.js +4 -1
- package/dist/esm/components-page/account-settings/settings/sign-out-section.js.map +1 -1
- package/dist/esm/components-page/onboarding.js +15 -13
- package/dist/esm/components-page/onboarding.js.map +1 -1
- package/dist/esm/components-page/onboarding.test.js +2 -1
- package/dist/esm/components-page/onboarding.test.js.map +1 -1
- package/dist/esm/generated/quetzal-translations.d.ts +2 -2
- 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/generated/quetzal-translations.d.ts +1 -1
- package/dist/{hexclave-provider-XVO5TbFD.d.ts → hexclave-provider-Br8j6WHN.d.ts} +3 -3
- package/dist/{hexclave-provider-XVO5TbFD.d.ts.map → hexclave-provider-Br8j6WHN.d.ts.map} +1 -1
- package/dist/{hooks-nqzRtl9S.d.ts → hooks-DyC8t1Sh.d.ts} +3 -3
- package/dist/{hooks-nqzRtl9S.d.ts.map → hooks-DyC8t1Sh.d.ts.map} +1 -1
- package/dist/{index-DbiyXho6.d.ts → index-CQ4kC6Cu.d.ts} +2 -2
- package/dist/{index-DbiyXho6.d.ts.map → index-CQ4kC6Cu.d.ts.map} +1 -1
- package/dist/{index-BDzsJvkD.d.ts → index-CWaynElx.d.ts} +3 -3
- package/dist/{index-BDzsJvkD.d.ts.map → index-CWaynElx.d.ts.map} +1 -1
- package/dist/{index-BSr2pd22.d.ts → index-FsS-NA9c.d.ts} +2 -2
- package/dist/{index-BSr2pd22.d.ts.map → index-FsS-NA9c.d.ts.map} +1 -1
- package/dist/index.d.ts +7 -7
- 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/lib/hooks.d.ts +1 -1
- package/dist/providers/hexclave-context.d.ts +1 -1
- package/dist/providers/hexclave-provider-client.d.ts +1 -1
- package/dist/providers/hexclave-provider.d.ts +1 -1
- package/dist/providers/translation-provider.d.ts +1 -1
- package/dist/pushed-config-error-overlay/index.d.ts +1 -1
- package/dist/{quetzal-translations-CwNB-iql.d.ts → quetzal-translations-XeH3zQ9l.d.ts} +3 -3
- package/dist/{quetzal-translations-CwNB-iql.d.ts.map → quetzal-translations-XeH3zQ9l.d.ts.map} +1 -1
- package/dist/{redirect-page-urls-ofcg3AYr.d.ts → redirect-page-urls-CnGvn0UJ.d.ts} +2 -2
- package/dist/{redirect-page-urls-ofcg3AYr.d.ts.map → redirect-page-urls-CnGvn0UJ.d.ts.map} +1 -1
- package/dist/tanstack-start-server-context.d.ts +804 -485
- package/dist/tanstack-start-server-context.d.ts.map +1 -1
- package/dist/{translation-provider-C0uPBd41.d.ts → translation-provider-WOaZXqAz.d.ts} +2 -2
- package/dist/{translation-provider-C0uPBd41.d.ts.map → translation-provider-WOaZXqAz.d.ts.map} +1 -1
- package/dist/{url-targets-CWO0mjTr.d.ts → url-targets-DbhzuQtY.d.ts} +2 -2
- package/dist/{url-targets-CWO0mjTr.d.ts.map → url-targets-DbhzuQtY.d.ts.map} +1 -1
- package/package.json +4 -4
- package/src/components/message-cards/predefined-message-card.tsx +4 -2
- package/src/components/user-button.test.tsx +87 -0
- package/src/components/user-button.tsx +9 -9
- package/src/components-page/account-settings/settings/sign-out-section.tsx +9 -1
- package/src/components-page/onboarding.test.tsx +1 -0
- package/src/components-page/onboarding.tsx +16 -13
- 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-MncU_Oxy.d.ts.map +0 -1
- package/dist/client-app-B0cTytS5.d.ts.map +0 -1
- package/dist/common-B2gkWlXX.d.ts.map +0 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { KnownErrors } from "@hexclave/shared";
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { Result } from "@hexclave/shared/dist/utils/results";
|
|
4
|
+
import { HexclaveClientApp } from "../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
|
+
describe("HexclaveClientApp.initAnalytics", () => {
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
vi.restoreAllMocks();
|
|
23
|
+
});
|
|
24
|
+
it("does not throw when analytics are configured", () => {
|
|
25
|
+
const app = new HexclaveClientApp(baseOptions);
|
|
26
|
+
expect(() => app.initAnalytics()).not.toThrow();
|
|
27
|
+
});
|
|
28
|
+
it("throws when analytics are disabled with the analytics option", () => {
|
|
29
|
+
const app = new HexclaveClientApp({
|
|
30
|
+
...baseOptions,
|
|
31
|
+
analytics: { enabled: false }
|
|
32
|
+
});
|
|
33
|
+
expect(() => app.initAnalytics()).toThrow("`analytics.enabled` constructor option is set to false");
|
|
34
|
+
});
|
|
35
|
+
it("does not throw when only session replays are disabled", () => {
|
|
36
|
+
const app = new HexclaveClientApp({
|
|
37
|
+
...baseOptions,
|
|
38
|
+
analytics: { replays: { enabled: false } }
|
|
39
|
+
});
|
|
40
|
+
expect(() => app.initAnalytics()).not.toThrow();
|
|
41
|
+
});
|
|
42
|
+
it("reports an error when the backend rejects uploads after initAnalytics() was called", () => {
|
|
43
|
+
const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
44
|
+
const app = new HexclaveClientApp(baseOptions);
|
|
45
|
+
app.initAnalytics();
|
|
46
|
+
simulateAnalyticsUploadResult(app, Result.error(new KnownErrors.AnalyticsNotEnabled()));
|
|
47
|
+
simulateAnalyticsUploadResult(app, Result.error(new KnownErrors.AnalyticsNotEnabled()));
|
|
48
|
+
expect(consoleError.mock.calls.filter((args) => args.some((arg) => String(arg).includes(analyticsAppDisabledMessage)))).toHaveLength(1);
|
|
49
|
+
});
|
|
50
|
+
it("throws from initAnalytics() when the backend already rejected uploads before it was called", () => {
|
|
51
|
+
const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
52
|
+
const app = new HexclaveClientApp(baseOptions);
|
|
53
|
+
simulateAnalyticsUploadResult(app, Result.error(new KnownErrors.AnalyticsNotEnabled()));
|
|
54
|
+
expect(consoleError.mock.calls.some((args) => args.some((arg) => String(arg).includes(analyticsAppDisabledMessage)))).toBe(false);
|
|
55
|
+
expect(() => app.initAnalytics()).toThrow(analyticsAppDisabledMessage);
|
|
56
|
+
});
|
|
57
|
+
it("ignores upload failures unrelated to the Analytics app being disabled", () => {
|
|
58
|
+
const app = new HexclaveClientApp(baseOptions);
|
|
59
|
+
simulateAnalyticsUploadResult(app, Result.error(/* @__PURE__ */ new Error("network error")));
|
|
60
|
+
expect(() => app.initAnalytics()).not.toThrow();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
//#endregion
|
|
64
|
+
export {};
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=client-app-impl.init-analytics.test.js.map
|
package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.init-analytics.test.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-app-impl.init-analytics.test.js","names":[],"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;AAEA,SAAS,yCAAyC;CAChD,gBAAgB;EACd,GAAG,gBAAgB;CACrB,CAAC;CAED,GAAG,sDAAsD;EACvD,MAAM,MAAM,IAAI,kBAAkB,WAAW;EAC7C,aAAa,IAAI,cAAc,CAAC,CAAC,CAAC,IAAI,QAAQ;CAChD,CAAC;CAED,GAAG,sEAAsE;EACvE,MAAM,MAAM,IAAI,kBAAkB;GAAE,GAAG;GAAa,WAAW,EAAE,SAAS,MAAM;EAAE,CAAC;EACnF,aAAa,IAAI,cAAc,CAAC,CAAC,CAAC,QAAQ,wDAAwD;CACpG,CAAC;CAED,GAAG,+DAA+D;EAChE,MAAM,MAAM,IAAI,kBAAkB;GAAE,GAAG;GAAa,WAAW,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE;EAAE,CAAC;EAChG,aAAa,IAAI,cAAc,CAAC,CAAC,CAAC,IAAI,QAAQ;CAChD,CAAC;CAED,GAAG,4FAA4F;EAC7F,MAAM,eAAe,GAAG,MAAM,SAAS,OAAO,CAAC,CAAC,yBAAyB,CAAC,CAAC;EAC3E,MAAM,MAAM,IAAI,kBAAkB,WAAW;EAC7C,IAAI,cAAc;EAElB,8BAA8B,KAAK,OAAO,MAAM,IAAI,YAAY,oBAAoB,CAAC,CAAC;EAEtF,8BAA8B,KAAK,OAAO,MAAM,IAAI,YAAY,oBAAoB,CAAC,CAAC;EAGtF,OADgB,aAAa,KAAK,MAAM,QAAQ,SAAS,KAAK,MAAM,QAAQ,OAAO,GAAG,CAAC,CAAC,SAAS,2BAA2B,CAAC,CAChH,CAAC,CAAC,CAAC,aAAa,CAAC;CAChC,CAAC;CAED,GAAG,oGAAoG;EACrG,MAAM,eAAe,GAAG,MAAM,SAAS,OAAO,CAAC,CAAC,yBAAyB,CAAC,CAAC;EAC3E,MAAM,MAAM,IAAI,kBAAkB,WAAW;EAE7C,8BAA8B,KAAK,OAAO,MAAM,IAAI,YAAY,oBAAoB,CAAC,CAAC;EACtF,OAAO,aAAa,KAAK,MAAM,MAAM,SAAS,KAAK,MAAM,QAAQ,OAAO,GAAG,CAAC,CAAC,SAAS,2BAA2B,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK;EAEhI,aAAa,IAAI,cAAc,CAAC,CAAC,CAAC,QAAQ,2BAA2B;CACvE,CAAC;CAED,GAAG,+EAA+E;EAChF,MAAM,MAAM,IAAI,kBAAkB,WAAW;EAC7C,8BAA8B,KAAK,OAAO,sBAAM,IAAI,MAAM,eAAe,CAAC,CAAC;EAC3E,aAAa,IAAI,cAAc,CAAC,CAAC,CAAC,IAAI,QAAQ;CAChD,CAAC;AACH,CAAC"}
|
|
@@ -14,7 +14,6 @@ import { getTrustedParentDomain, validateRedirectUrl } from "@hexclave/shared/di
|
|
|
14
14
|
import { decodeBase32, decodeBase64, encodeBase32, encodeBase64 } from "@hexclave/shared/dist/utils/bytes";
|
|
15
15
|
import { hexclaveAppInternalsSymbol } from "../../common.js";
|
|
16
16
|
import { adminProjectCreateOptionsToCrud } from "../../projects/index.js";
|
|
17
|
-
import { InternalSession } from "@hexclave/shared/dist/sessions";
|
|
18
17
|
import { Store } from "@hexclave/shared/dist/utils/stores";
|
|
19
18
|
import { crossDomainAuthQueryParams, getCrossDomainHandoffParamsFromCurrentUrl, planRedirectToHandler } from "./redirect-page-urls.js";
|
|
20
19
|
import { BROWSER_ACTION_QUERY_PARAM } from "@hexclave/shared/dist/utils/browser-action-snippets";
|
|
@@ -37,10 +36,11 @@ import { teamCreateOptionsToCrud, teamUpdateOptionsToCrud } from "../../teams/in
|
|
|
37
36
|
import { buildCliAuthConfirmUrl, getHostedHandlerUrl, isHostedHandlerUrlForProject, resolveHandlerUrls } from "../../url-targets.js";
|
|
38
37
|
import { userUpdateOptionsToCrud, withUserDestructureGuard } from "../../users/index.js";
|
|
39
38
|
import { EventTracker } from "./event-tracker.js";
|
|
39
|
+
import { ExternalTokenStoreSessionAdapter, isExternalTokenStore } from "./external-token-store-session-adapter.js";
|
|
40
40
|
import { augmentUrlWithPersistedRedirectBackState, getRawAfterAuthReturnTo, saveRedirectBackStateFromUrl } from "./redirect-back-state.js";
|
|
41
41
|
import { recordRedirectAndThrowIfLoopDetected } from "./redirect-loop-breaker.js";
|
|
42
42
|
import { subscribeSessionRefresh } from "./session-refresh-subscription.js";
|
|
43
|
-
import { SessionRecorder, analyticsOptionsFromJson, analyticsOptionsToJson, getSessionReplayOptions } from "./session-replay.js";
|
|
43
|
+
import { SessionRecorder, analyticsOptionsFromJson, analyticsOptionsToJson, getSessionReplayOptions, isAnalyticsNotEnabledError } from "./session-replay.js";
|
|
44
44
|
import { mountClickmapOverlay } from "../../../../clickmap/index.js";
|
|
45
45
|
import { mountDevTool } from "../../../../dev-tool/index.js";
|
|
46
46
|
import { mountPushedConfigErrorOverlay } from "../../../../pushed-config-error-overlay/index.js";
|
|
@@ -145,6 +145,9 @@ function throwSetupError(error) {
|
|
|
145
145
|
showSetupErrorOverlay(error);
|
|
146
146
|
throw error;
|
|
147
147
|
}
|
|
148
|
+
function createAnalyticsAppDisabledOnProjectError() {
|
|
149
|
+
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.");
|
|
150
|
+
}
|
|
148
151
|
function getRedirectHelperInstruction(handlerName) {
|
|
149
152
|
if (handlerName === "handler") return "Use a page-specific redirect helper such as app.redirectToSignIn() instead.";
|
|
150
153
|
return `Use app.${`redirectTo${handlerName.slice(0, 1).toUpperCase()}${handlerName.slice(1)}`}() instead.`;
|
|
@@ -305,6 +308,8 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
305
308
|
this._uniqueIdentifier = void 0;
|
|
306
309
|
this._sessionRecorder = null;
|
|
307
310
|
this._eventTracker = null;
|
|
311
|
+
this._initAnalyticsCalled = false;
|
|
312
|
+
this._analyticsAppDisabledOnProject = false;
|
|
308
313
|
this._pendingSignOut = null;
|
|
309
314
|
this.__DEMO_ENABLE_SLIGHT_FETCH_DELAY = false;
|
|
310
315
|
this._ownedAdminApps = new DependenciesMap();
|
|
@@ -511,6 +516,10 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
511
516
|
}
|
|
512
517
|
});
|
|
513
518
|
}
|
|
519
|
+
this._externalTokenStoreSessionAdapter = new ExternalTokenStoreSessionAdapter((sessionOptions) => this._interface.createSession(sessionOptions), async (providerId, token) => {
|
|
520
|
+
const exchange = await this._interface.exchangeExternalAuthToken(providerId, token);
|
|
521
|
+
return Result.orThrow(exchange).accessToken;
|
|
522
|
+
});
|
|
514
523
|
this._tokenStoreInit = resolvedOptions.tokenStore;
|
|
515
524
|
this._redirectMethod = resolvedOptions.redirectMethod || (isBrowserLike() ? "window" : "none");
|
|
516
525
|
this._redirectMethod = resolvedOptions.redirectMethod || "nextjs";
|
|
@@ -535,22 +544,22 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
535
544
|
})) return await this._getSession();
|
|
536
545
|
return (await this.getUser({ or: "anonymous" }))._internalSession;
|
|
537
546
|
};
|
|
538
|
-
const
|
|
547
|
+
const recordsAnalytics = this._getAnalyticsDisabledReason() == null && isBrowserLike();
|
|
539
548
|
const sessionReplayOptions = getSessionReplayOptions(this._analyticsOptions);
|
|
540
|
-
if (
|
|
549
|
+
if (recordsAnalytics && sessionReplayOptions.enabled) {
|
|
541
550
|
this._sessionRecorder = new SessionRecorder({
|
|
542
551
|
projectId: this.projectId,
|
|
543
552
|
sendBatch: async (body, opts) => {
|
|
544
|
-
return await this._interface.sendSessionReplayBatch(body, await getAnalyticsSession(), opts);
|
|
553
|
+
return this._handleAnalyticsUploadResult(await this._interface.sendSessionReplayBatch(body, await getAnalyticsSession(), opts));
|
|
545
554
|
}
|
|
546
555
|
}, sessionReplayOptions);
|
|
547
556
|
this._sessionRecorder.start();
|
|
548
557
|
}
|
|
549
|
-
if (
|
|
558
|
+
if (recordsAnalytics) {
|
|
550
559
|
this._eventTracker = new EventTracker({
|
|
551
560
|
projectId: this.projectId,
|
|
552
561
|
sendBatch: async (body, opts) => {
|
|
553
|
-
return await this._interface.sendAnalyticsEventBatch(body, await getAnalyticsSession(), opts);
|
|
562
|
+
return this._handleAnalyticsUploadResult(await this._interface.sendAnalyticsEventBatch(body, await getAnalyticsSession(), opts));
|
|
554
563
|
}
|
|
555
564
|
});
|
|
556
565
|
this._eventTracker.start();
|
|
@@ -1125,6 +1134,9 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
1125
1134
|
case "memory": return this._memoryTokenStore;
|
|
1126
1135
|
default:
|
|
1127
1136
|
if (tokenStoreInit === null) return createEmptyTokenStore();
|
|
1137
|
+
else if (typeof tokenStoreInit === "object" && isExternalTokenStore(tokenStoreInit)) return this._externalTokenStoreSessionAdapter.getOrCreateTokenStore(tokenStoreInit, (store, previousSessionKey) => {
|
|
1138
|
+
this._sessionsByTokenStoreAndSessionKey.get(store)?.get(previousSessionKey)?.suggestAccessTokenExpired();
|
|
1139
|
+
});
|
|
1128
1140
|
else if (typeof tokenStoreInit === "object" && "headers" in tokenStoreInit) {
|
|
1129
1141
|
if (this._requestTokenStores.has(tokenStoreInit)) return this._requestTokenStores.get(tokenStoreInit);
|
|
1130
1142
|
const authorizationHeader = getHeaderValueFromRequestLikeHeaders(tokenStoreInit.headers, "authorization");
|
|
@@ -1171,27 +1183,27 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
1171
1183
|
const cookieHelper = createBrowserCookieHelper();
|
|
1172
1184
|
return this._getOrCreateTokenStore(cookieHelper, overrideTokenStoreInit);
|
|
1173
1185
|
}
|
|
1186
|
+
_getSessionKeyForTokenStore(tokenStore, tokenObj) {
|
|
1187
|
+
return this._externalTokenStoreSessionAdapter.getSessionKey(tokenStore, tokenObj);
|
|
1188
|
+
}
|
|
1174
1189
|
/**
|
|
1175
1190
|
* @param overrideTokenObj The tokens to build the session for, if they haven't been written to the token store yet
|
|
1176
1191
|
* (so we can warm a sign-in's session before the token store publishes it).
|
|
1177
1192
|
*/
|
|
1178
1193
|
_getSessionFromTokenStore(tokenStore, overrideTokenObj) {
|
|
1179
1194
|
const tokenObj = overrideTokenObj ?? tokenStore.get();
|
|
1180
|
-
const sessionKey =
|
|
1195
|
+
const sessionKey = this._getSessionKeyForTokenStore(tokenStore, tokenObj);
|
|
1181
1196
|
const existing = sessionKey ? this._sessionsByTokenStoreAndSessionKey.get(tokenStore)?.get(sessionKey) : null;
|
|
1182
1197
|
if (existing) return existing;
|
|
1183
|
-
const session = this.
|
|
1184
|
-
refreshToken: tokenObj.refreshToken,
|
|
1185
|
-
accessToken: tokenObj.accessToken
|
|
1186
|
-
});
|
|
1198
|
+
const session = this._externalTokenStoreSessionAdapter.createSessionForTokenStore(tokenStore, tokenObj, sessionKey);
|
|
1187
1199
|
session.onAccessTokenChange((newAccessToken) => {
|
|
1188
|
-
tokenStore.update((old) =>
|
|
1200
|
+
tokenStore.update((old) => this._getSessionKeyForTokenStore(tokenStore, old) === sessionKey ? {
|
|
1189
1201
|
...old,
|
|
1190
1202
|
accessToken: newAccessToken?.token ?? null
|
|
1191
1203
|
} : old);
|
|
1192
1204
|
});
|
|
1193
1205
|
session.onInvalidate(() => {
|
|
1194
|
-
tokenStore.update((old) =>
|
|
1206
|
+
tokenStore.update((old) => this._getSessionKeyForTokenStore(tokenStore, old) === sessionKey ? {
|
|
1195
1207
|
...old,
|
|
1196
1208
|
accessToken: null,
|
|
1197
1209
|
refreshToken: null
|
|
@@ -1222,6 +1234,7 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
1222
1234
|
}
|
|
1223
1235
|
async _signInToAccountWithTokens(tokens) {
|
|
1224
1236
|
if (!("accessToken" in tokens) || !("refreshToken" in tokens)) throw new HexclaveAssertionError("Invalid tokens object; can't sign in with this", { tokens });
|
|
1237
|
+
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.");
|
|
1225
1238
|
const attemptId = ++this._signInAttemptCounter;
|
|
1226
1239
|
const tokenStore = this._getOrCreateTokenStore(await this._createCookieHelper());
|
|
1227
1240
|
const session = this._getSessionFromTokenStore(tokenStore, tokens);
|
|
@@ -1615,6 +1628,7 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
1615
1628
|
emailAuthEnabled: crud.auth_with_email,
|
|
1616
1629
|
otpAuthEnabled: crud.otp_auth_enabled,
|
|
1617
1630
|
oauthProviders: crud.oauth_providers,
|
|
1631
|
+
externalAuthProviders: crud.external_auth_providers,
|
|
1618
1632
|
passkeyAuthEnabled: crud.passkey_auth_enabled,
|
|
1619
1633
|
isMultiFactorRequired: crud.requires_totp_mfa,
|
|
1620
1634
|
isAnonymous: crud.is_anonymous,
|
|
@@ -2577,6 +2591,37 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
2577
2591
|
}
|
|
2578
2592
|
return crud && this._currentUserFromCrud(crud, session);
|
|
2579
2593
|
}
|
|
2594
|
+
isExternalAuthApp() {
|
|
2595
|
+
return typeof this._tokenStoreInit === "object" && this._tokenStoreInit != null && isExternalTokenStore(this._tokenStoreInit);
|
|
2596
|
+
}
|
|
2597
|
+
initAnalytics() {
|
|
2598
|
+
this._initAnalyticsCalled = true;
|
|
2599
|
+
const disabledReason = this._getAnalyticsDisabledReason();
|
|
2600
|
+
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.`);
|
|
2601
|
+
if (this._analyticsAppDisabledOnProject) throw createAnalyticsAppDisabledOnProjectError();
|
|
2602
|
+
if (isBrowserLike() && this._eventTracker == null) throw new 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.");
|
|
2603
|
+
}
|
|
2604
|
+
/**
|
|
2605
|
+
* Returns why this app will never record analytics, or null if it records them whenever it runs in a browser. The
|
|
2606
|
+
* constructor and initAnalytics() both use this, so that the conditions under which analytics start and the
|
|
2607
|
+
* conditions under which initAnalytics() throws can't drift apart.
|
|
2608
|
+
*
|
|
2609
|
+
* Whether the Analytics app is enabled on the project is not part of this, as the client only learns that once the
|
|
2610
|
+
* backend rejects an upload; see _handleAnalyticsUploadResult.
|
|
2611
|
+
*/
|
|
2612
|
+
_getAnalyticsDisabledReason() {
|
|
2613
|
+
if (this._analyticsOptions?.enabled === false) return "the `analytics.enabled` constructor option is set to false";
|
|
2614
|
+
if (this._options.automaticSideEffects === false) return "the `automaticSideEffects` constructor option is set to false, which also disables analytics";
|
|
2615
|
+
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)";
|
|
2616
|
+
return null;
|
|
2617
|
+
}
|
|
2618
|
+
_handleAnalyticsUploadResult(result) {
|
|
2619
|
+
if (result.status === "error" && isAnalyticsNotEnabledError(result.error) && !this._analyticsAppDisabledOnProject) {
|
|
2620
|
+
this._analyticsAppDisabledOnProject = true;
|
|
2621
|
+
if (this._initAnalyticsCalled) captureError("initAnalytics", createAnalyticsAppDisabledOnProjectError());
|
|
2622
|
+
}
|
|
2623
|
+
return result;
|
|
2624
|
+
}
|
|
2580
2625
|
useUser(options) {
|
|
2581
2626
|
if (options?.or === "anonymous" && options.includeRestricted === false) throw new Error("Cannot use { or: 'anonymous' } with { includeRestricted: false }. Anonymous users implicitly include restricted users.");
|
|
2582
2627
|
this._ensurePersistentTokenStore(options?.tokenStore);
|
|
@@ -2626,6 +2671,7 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
2626
2671
|
primaryEmail: accessToken.payload.email,
|
|
2627
2672
|
displayName: accessToken.payload.name,
|
|
2628
2673
|
primaryEmailVerified: accessToken.payload.email_verified,
|
|
2674
|
+
selectedTeamId: accessToken.payload.selected_team_id,
|
|
2629
2675
|
isAnonymous,
|
|
2630
2676
|
isMultiFactorRequired: accessToken.payload.requires_totp_mfa,
|
|
2631
2677
|
isRestricted: accessToken.payload.is_restricted,
|
|
@@ -2635,11 +2681,14 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
2635
2681
|
async _getPartialUserFromConvex(ctx) {
|
|
2636
2682
|
const auth = await ctx.auth.getUserIdentity();
|
|
2637
2683
|
if (!auth) return null;
|
|
2684
|
+
const selectedTeamId = auth.selected_team_id ?? null;
|
|
2685
|
+
if (selectedTeamId !== null && typeof selectedTeamId !== "string") throw new HexclaveAssertionError("Expected the Convex selected_team_id claim to be a string or null.");
|
|
2638
2686
|
return {
|
|
2639
2687
|
id: auth.subject,
|
|
2640
2688
|
displayName: auth.name ?? null,
|
|
2641
2689
|
primaryEmail: auth.email ?? null,
|
|
2642
2690
|
primaryEmailVerified: auth.email_verified,
|
|
2691
|
+
selectedTeamId,
|
|
2643
2692
|
isAnonymous: auth.is_anonymous,
|
|
2644
2693
|
isMultiFactorRequired: auth.requires_totp_mfa,
|
|
2645
2694
|
isRestricted: auth.is_restricted,
|
|
@@ -2990,6 +3039,8 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
2990
3039
|
const previousSignOut = this._pendingSignOut;
|
|
2991
3040
|
const signOutOperation = (async () => {
|
|
2992
3041
|
await previousSignOut;
|
|
3042
|
+
const externalTokenStore = this._externalTokenStoreSessionAdapter.getExternalTokenStoreForSession(session);
|
|
3043
|
+
if (externalTokenStore != null) throw new HexclaveAssertionError(`Cannot sign out an externally authenticated session through Hexclave. Sign out through the ${externalTokenStore.providerId} provider instead.`);
|
|
2993
3044
|
await this._interface.signOut(session);
|
|
2994
3045
|
if (options?.redirectUrl) await this._redirectTo({
|
|
2995
3046
|
url: options.redirectUrl,
|
|
@@ -3143,6 +3194,7 @@ var _HexclaveClientAppImplIncomplete = class _HexclaveClientAppImplIncomplete {
|
|
|
3143
3194
|
return {
|
|
3144
3195
|
toClientJson: () => {
|
|
3145
3196
|
if (typeof this._redirectMethod !== "string") throw new HexclaveAssertionError("Cannot serialize to JSON from an application with a non-string redirect method");
|
|
3197
|
+
if (typeof this._tokenStoreInit === "object" && this._tokenStoreInit != null && isExternalTokenStore(this._tokenStoreInit)) throw new HexclaveAssertionError("Cannot serialize an app with an external token store because its callbacks are not serializable. Construct this app in the client runtime instead.");
|
|
3146
3198
|
const publishableClientKey = "publishableClientKey" in this._interface.options ? this._interface.options.publishableClientKey : void 0;
|
|
3147
3199
|
return {
|
|
3148
3200
|
baseUrl: this._options.baseUrl,
|