@hexclave/next 1.0.49 → 1.0.52
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-Cmhx-Gnk.d.ts → admin-app-impl-Bga0o6cL.d.ts} +5 -2
- package/dist/{admin-app-impl-Cmhx-Gnk.d.ts.map → admin-app-impl-Bga0o6cL.d.ts.map} +1 -1
- package/dist/clickmap/clickmap-core.d.ts +1 -1
- package/dist/clickmap/index.d.ts +1 -1
- package/dist/{client-app-Majjw8Xp.d.ts → client-app-B0kBC67Z.d.ts} +2 -1
- package/dist/{client-app-Majjw8Xp.d.ts.map → client-app-B0kBC67Z.d.ts.map} +1 -1
- package/dist/common-BK_sydxF.d.ts.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-page/account-settings/active-sessions/active-sessions-page.js +1 -1
- package/dist/components-page/account-settings/api-keys/api-keys-page.js +2 -2
- package/dist/components-page/account-settings/payments/payments-page.d.ts +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-api-keys-section.js +2 -2
- 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/sign-out.js +11 -2
- package/dist/components-page/sign-out.js.map +1 -1
- package/dist/components-page/sign-out.test.d.ts +1 -0
- package/dist/components-page/sign-out.test.js +157 -0
- package/dist/components-page/sign-out.test.js.map +1 -0
- package/dist/dev-tool/dev-tool-core.d.ts +1 -1
- package/dist/dev-tool/dev-tool-core.d.ts.map +1 -1
- package/dist/dev-tool/dev-tool-core.js +58 -28
- package/dist/dev-tool/dev-tool-core.js.map +1 -1
- package/dist/dev-tool/dev-tool-styles.js +35 -0
- package/dist/dev-tool/dev-tool-styles.js.map +1 -1
- package/dist/dev-tool/index.d.ts +1 -1
- package/dist/esm/components-page/account-settings/active-sessions/active-sessions-page.js +1 -1
- package/dist/esm/components-page/account-settings/api-keys/api-keys-page.js +2 -2
- package/dist/esm/components-page/account-settings/teams/team-api-keys-section.js +2 -2
- package/dist/esm/components-page/sign-out.d.ts.map +1 -1
- package/dist/esm/components-page/sign-out.js +12 -3
- package/dist/esm/components-page/sign-out.js.map +1 -1
- package/dist/esm/components-page/sign-out.test.d.ts +1 -0
- package/dist/esm/components-page/sign-out.test.js +156 -0
- package/dist/esm/components-page/sign-out.test.js.map +1 -0
- package/dist/esm/dev-tool/dev-tool-core.d.ts.map +1 -1
- package/dist/esm/dev-tool/dev-tool-core.js +58 -28
- package/dist/esm/dev-tool/dev-tool-core.js.map +1 -1
- package/dist/esm/dev-tool/dev-tool-styles.js +35 -0
- package/dist/esm/dev-tool/dev-tool-styles.js.map +1 -1
- package/dist/esm/generated/global-css.d.ts +1 -1
- package/dist/esm/generated/global-css.js +1 -1
- package/dist/esm/generated/global-css.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -0
- 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.cross-domain.test.js +97 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +1 -0
- 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.js +26 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/common.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/common.js +21 -4
- package/dist/esm/lib/hexclave-app/apps/implementations/common.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/redirect-back-state.d.ts +37 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/redirect-back-state.d.ts.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/redirect-back-state.js +89 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/redirect-back-state.js.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/redirect-back-state.test.d.ts +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/redirect-back-state.test.js +134 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/redirect-back-state.test.js.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/redirect-loop-breaker.d.ts +8 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/redirect-loop-breaker.d.ts.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/redirect-loop-breaker.js +73 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/redirect-loop-breaker.js.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/redirect-loop-breaker.test.d.ts +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/redirect-loop-breaker.test.js +92 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/redirect-loop-breaker.test.js.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.js +27 -6
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.d.ts +1 -0
- 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/pushed-config-error-overlay/index.js +1 -1
- package/dist/esm/pushed-config-error-overlay/index.js.map +1 -1
- package/dist/esm/utils/session-storage.d.ts +12 -0
- package/dist/esm/utils/session-storage.d.ts.map +1 -0
- package/dist/esm/utils/session-storage.js +30 -0
- package/dist/esm/utils/session-storage.js.map +1 -0
- package/dist/generated/global-css.d.ts +1 -1
- package/dist/generated/global-css.js +1 -1
- package/dist/generated/global-css.js.map +1 -1
- package/dist/{hexclave-provider-IAd7nlbi.d.ts → hexclave-provider-BQ9wrtBt.d.ts} +2 -2
- package/dist/{hexclave-provider-IAd7nlbi.d.ts.map → hexclave-provider-BQ9wrtBt.d.ts.map} +1 -1
- package/dist/{hooks-BKXOF6jh.d.ts → hooks-ykQ7Wsco.d.ts} +2 -2
- package/dist/{hooks-BKXOF6jh.d.ts.map → hooks-ykQ7Wsco.d.ts.map} +1 -1
- package/dist/{index-Ck1qKoin.d.ts → index-DwE1cNLH.d.ts} +2 -2
- package/dist/{index-Ck1qKoin.d.ts.map → index-DwE1cNLH.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.cross-domain.test.js +97 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +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.js +26 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/common.js +21 -4
- package/dist/lib/hexclave-app/apps/implementations/common.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/index.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/redirect-back-state.d.ts +37 -0
- package/dist/lib/hexclave-app/apps/implementations/redirect-back-state.d.ts.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/redirect-back-state.js +92 -0
- package/dist/lib/hexclave-app/apps/implementations/redirect-back-state.js.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/redirect-back-state.test.d.ts +1 -0
- package/dist/lib/hexclave-app/apps/implementations/redirect-back-state.test.js +133 -0
- package/dist/lib/hexclave-app/apps/implementations/redirect-back-state.test.js.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/redirect-loop-breaker.d.ts +8 -0
- package/dist/lib/hexclave-app/apps/implementations/redirect-loop-breaker.d.ts.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/redirect-loop-breaker.js +74 -0
- package/dist/lib/hexclave-app/apps/implementations/redirect-loop-breaker.js.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/redirect-loop-breaker.test.d.ts +1 -0
- package/dist/lib/hexclave-app/apps/implementations/redirect-loop-breaker.test.js +91 -0
- package/dist/lib/hexclave-app/apps/implementations/redirect-loop-breaker.test.js.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.js +27 -6
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.js.map +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/index.d.ts +1 -1
- 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/users/index.d.ts +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/pushed-config-error-overlay/index.d.ts +1 -1
- package/dist/pushed-config-error-overlay/index.js +1 -1
- package/dist/pushed-config-error-overlay/index.js.map +1 -1
- package/dist/sign-out-DBnSkfDz.d.ts.map +1 -1
- package/dist/utils/session-storage.d.ts +12 -0
- package/dist/utils/session-storage.d.ts.map +1 -0
- package/dist/utils/session-storage.js +33 -0
- package/dist/utils/session-storage.js.map +1 -0
- package/package.json +4 -4
- package/src/components-page/sign-out.test.tsx +190 -0
- package/src/components-page/sign-out.tsx +22 -3
- package/src/dev-tool/dev-tool-core.ts +100 -35
- package/src/dev-tool/dev-tool-styles.ts +35 -0
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.ts +127 -0
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.ts +34 -1
- package/src/lib/hexclave-app/apps/implementations/common.ts +18 -1
- package/src/lib/hexclave-app/apps/implementations/redirect-back-state.test.ts +134 -0
- package/src/lib/hexclave-app/apps/implementations/redirect-back-state.ts +140 -0
- package/src/lib/hexclave-app/apps/implementations/redirect-loop-breaker.test.ts +127 -0
- package/src/lib/hexclave-app/apps/implementations/redirect-loop-breaker.ts +117 -0
- package/src/lib/hexclave-app/apps/implementations/server-app-impl.ts +31 -5
- package/src/lib/hexclave-app/apps/interfaces/client-app.ts +1 -0
- package/src/pushed-config-error-overlay/index.ts +1 -1
- package/src/utils/session-storage.ts +45 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
let vitest = require("vitest");
|
|
2
|
+
let __redirect_loop_breaker_js = require("./redirect-loop-breaker.js");
|
|
3
|
+
//#region src/lib/hexclave-app/apps/implementations/redirect-loop-breaker.test.ts
|
|
4
|
+
function createMockSessionStorage() {
|
|
5
|
+
const map = /* @__PURE__ */ new Map();
|
|
6
|
+
return {
|
|
7
|
+
getItem: (key) => map.get(key) ?? null,
|
|
8
|
+
setItem: (key, value) => {
|
|
9
|
+
map.set(key, value);
|
|
10
|
+
},
|
|
11
|
+
removeItem: (key) => {
|
|
12
|
+
map.delete(key);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const previousWindow = Reflect.get(globalThis, "window");
|
|
17
|
+
const hadPreviousWindow = Reflect.has(globalThis, "window");
|
|
18
|
+
function record(from, to) {
|
|
19
|
+
(0, __redirect_loop_breaker_js.recordRedirectAndThrowIfLoopDetected)({
|
|
20
|
+
currentUrl: new URL(from),
|
|
21
|
+
targetUrl: new URL(to)
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
(0, vitest.describe)("recordRedirectAndThrowIfLoopDetected", () => {
|
|
25
|
+
(0, vitest.beforeEach)(() => {
|
|
26
|
+
Reflect.set(globalThis, "window", { sessionStorage: createMockSessionStorage() });
|
|
27
|
+
vitest.vi.useFakeTimers();
|
|
28
|
+
vitest.vi.setSystemTime(/* @__PURE__ */ new Date("2026-01-01T00:00:00Z"));
|
|
29
|
+
});
|
|
30
|
+
(0, vitest.afterEach)(() => {
|
|
31
|
+
vitest.vi.useRealTimers();
|
|
32
|
+
if (hadPreviousWindow) Reflect.set(globalThis, "window", previousWindow);
|
|
33
|
+
else Reflect.deleteProperty(globalThis, "window");
|
|
34
|
+
});
|
|
35
|
+
(0, vitest.it)("allows distinct redirects in quick succession", () => {
|
|
36
|
+
for (let i = 0; i < 20; i++) record(`https://app.example.test/page-${i}`, `https://app.example.test/page-${i + 1}`);
|
|
37
|
+
});
|
|
38
|
+
(0, vitest.it)("throws once the same redirect repeats 5 times within the window", () => {
|
|
39
|
+
for (let i = 0; i < 4; i++) {
|
|
40
|
+
record("https://app.example.test/dashboard", "https://hosted.example.test/handler/sign-in");
|
|
41
|
+
vitest.vi.advanceTimersByTime(1e3);
|
|
42
|
+
}
|
|
43
|
+
(0, vitest.expect)(() => {
|
|
44
|
+
record("https://app.example.test/dashboard", "https://hosted.example.test/handler/sign-in");
|
|
45
|
+
}).toThrowError(/Redirect loop detected/);
|
|
46
|
+
});
|
|
47
|
+
(0, vitest.it)("treats redirects that differ only in query params or hash as identical", () => {
|
|
48
|
+
for (let i = 0; i < 4; i++) record(`https://app.example.test/dashboard?code=code-${i}`, `https://hosted.example.test/handler/sign-in?state=state-${i}#frag-${i}`);
|
|
49
|
+
(0, vitest.expect)(() => {
|
|
50
|
+
record("https://app.example.test/dashboard?code=final", "https://hosted.example.test/handler/sign-in?state=final");
|
|
51
|
+
}).toThrowError(/Redirect loop detected/);
|
|
52
|
+
});
|
|
53
|
+
(0, vitest.it)("does not throw when the identical redirects are spread out beyond the window", () => {
|
|
54
|
+
for (let i = 0; i < 20; i++) {
|
|
55
|
+
record("https://app.example.test/dashboard", "https://hosted.example.test/handler/sign-in");
|
|
56
|
+
vitest.vi.advanceTimersByTime(31e3);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
(0, vitest.it)("allows the same redirect again after a loop was broken", () => {
|
|
60
|
+
for (let i = 0; i < 4; i++) record("https://app.example.test/dashboard", "https://hosted.example.test/handler/sign-in");
|
|
61
|
+
(0, vitest.expect)(() => {
|
|
62
|
+
record("https://app.example.test/dashboard", "https://hosted.example.test/handler/sign-in");
|
|
63
|
+
}).toThrowError(/Redirect loop detected/);
|
|
64
|
+
record("https://app.example.test/dashboard", "https://hosted.example.test/handler/sign-in");
|
|
65
|
+
});
|
|
66
|
+
(0, vitest.it)("counts self-redirects (same origin and path) towards loops", () => {
|
|
67
|
+
for (let i = 0; i < 4; i++) record(`https://hosted.example.test/handler/sign-in?attempt=${i}`, "https://hosted.example.test/handler/sign-in");
|
|
68
|
+
(0, vitest.expect)(() => {
|
|
69
|
+
record("https://hosted.example.test/handler/sign-in?attempt=final", "https://hosted.example.test/handler/sign-in");
|
|
70
|
+
}).toThrowError(/Redirect loop detected/);
|
|
71
|
+
});
|
|
72
|
+
(0, vitest.it)("degrades to a no-op when sessionStorage is unavailable", () => {
|
|
73
|
+
Reflect.set(globalThis, "window", { get sessionStorage() {
|
|
74
|
+
throw new Error("Storage is disabled");
|
|
75
|
+
} });
|
|
76
|
+
for (let i = 0; i < 20; i++) record("https://app.example.test/dashboard", "https://hosted.example.test/handler/sign-in");
|
|
77
|
+
});
|
|
78
|
+
(0, vitest.it)("recovers from corrupted breadcrumb storage", () => {
|
|
79
|
+
const storage = createMockSessionStorage();
|
|
80
|
+
storage.setItem("hexclave-redirect-loop-breadcrumbs", "{not json");
|
|
81
|
+
Reflect.set(globalThis, "window", { sessionStorage: storage });
|
|
82
|
+
record("https://app.example.test/dashboard", "https://hosted.example.test/handler/sign-in");
|
|
83
|
+
for (let i = 0; i < 3; i++) record("https://app.example.test/dashboard", "https://hosted.example.test/handler/sign-in");
|
|
84
|
+
(0, vitest.expect)(() => {
|
|
85
|
+
record("https://app.example.test/dashboard", "https://hosted.example.test/handler/sign-in");
|
|
86
|
+
}).toThrowError(/Redirect loop detected/);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
//#endregion
|
|
90
|
+
|
|
91
|
+
//# sourceMappingURL=redirect-loop-breaker.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redirect-loop-breaker.test.js","names":[],"sources":["../../../../../src/lib/hexclave-app/apps/implementations/redirect-loop-breaker.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 { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { recordRedirectAndThrowIfLoopDetected } from \"./redirect-loop-breaker\";\n\nfunction createMockSessionStorage() {\n const map = new Map<string, string>();\n return {\n getItem: (key: string) => map.get(key) ?? null,\n setItem: (key: string, value: string) => {\n map.set(key, value);\n },\n removeItem: (key: string) => {\n map.delete(key);\n },\n };\n}\n\nconst previousWindow = Reflect.get(globalThis, \"window\");\nconst hadPreviousWindow = Reflect.has(globalThis, \"window\");\n\nfunction record(from: string, to: string) {\n recordRedirectAndThrowIfLoopDetected({\n currentUrl: new URL(from),\n targetUrl: new URL(to),\n });\n}\n\ndescribe(\"recordRedirectAndThrowIfLoopDetected\", () => {\n beforeEach(() => {\n Reflect.set(globalThis, \"window\", { sessionStorage: createMockSessionStorage() });\n vi.useFakeTimers();\n vi.setSystemTime(new Date(\"2026-01-01T00:00:00Z\"));\n });\n\n afterEach(() => {\n vi.useRealTimers();\n if (hadPreviousWindow) {\n Reflect.set(globalThis, \"window\", previousWindow);\n } else {\n Reflect.deleteProperty(globalThis, \"window\");\n }\n });\n\n it(\"allows distinct redirects in quick succession\", () => {\n for (let i = 0; i < 20; i++) {\n record(`https://app.example.test/page-${i}`, `https://app.example.test/page-${i + 1}`);\n }\n });\n\n it(\"throws once the same redirect repeats 5 times within the window\", () => {\n for (let i = 0; i < 4; i++) {\n record(\"https://app.example.test/dashboard\", \"https://hosted.example.test/handler/sign-in\");\n vi.advanceTimersByTime(1000);\n }\n expect(() => {\n record(\"https://app.example.test/dashboard\", \"https://hosted.example.test/handler/sign-in\");\n }).toThrowError(/Redirect loop detected/);\n });\n\n it(\"treats redirects that differ only in query params or hash as identical\", () => {\n // Real loops usually rotate nonce-style params (code, state, after_auth_return_to) each cycle.\n for (let i = 0; i < 4; i++) {\n record(\n `https://app.example.test/dashboard?code=code-${i}`,\n `https://hosted.example.test/handler/sign-in?state=state-${i}#frag-${i}`,\n );\n }\n expect(() => {\n record(\"https://app.example.test/dashboard?code=final\", \"https://hosted.example.test/handler/sign-in?state=final\");\n }).toThrowError(/Redirect loop detected/);\n });\n\n it(\"does not throw when the identical redirects are spread out beyond the window\", () => {\n for (let i = 0; i < 20; i++) {\n record(\"https://app.example.test/dashboard\", \"https://hosted.example.test/handler/sign-in\");\n vi.advanceTimersByTime(31_000);\n }\n });\n\n it(\"allows the same redirect again after a loop was broken\", () => {\n for (let i = 0; i < 4; i++) {\n record(\"https://app.example.test/dashboard\", \"https://hosted.example.test/handler/sign-in\");\n }\n expect(() => {\n record(\"https://app.example.test/dashboard\", \"https://hosted.example.test/handler/sign-in\");\n }).toThrowError(/Redirect loop detected/);\n // The breadcrumbs for the broken loop are dropped, so a manual retry isn't instantly blocked.\n record(\"https://app.example.test/dashboard\", \"https://hosted.example.test/handler/sign-in\");\n });\n\n it(\"counts self-redirects (same origin and path) towards loops\", () => {\n for (let i = 0; i < 4; i++) {\n record(`https://hosted.example.test/handler/sign-in?attempt=${i}`, \"https://hosted.example.test/handler/sign-in\");\n }\n expect(() => {\n record(\"https://hosted.example.test/handler/sign-in?attempt=final\", \"https://hosted.example.test/handler/sign-in\");\n }).toThrowError(/Redirect loop detected/);\n });\n\n it(\"degrades to a no-op when sessionStorage is unavailable\", () => {\n Reflect.set(globalThis, \"window\", {\n get sessionStorage(): Storage {\n throw new Error(\"Storage is disabled\");\n },\n });\n for (let i = 0; i < 20; i++) {\n record(\"https://app.example.test/dashboard\", \"https://hosted.example.test/handler/sign-in\");\n }\n });\n\n it(\"recovers from corrupted breadcrumb storage\", () => {\n const storage = createMockSessionStorage();\n storage.setItem(\"hexclave-redirect-loop-breadcrumbs\", \"{not json\");\n Reflect.set(globalThis, \"window\", { sessionStorage: storage });\n record(\"https://app.example.test/dashboard\", \"https://hosted.example.test/handler/sign-in\");\n // After recovery, loop detection works again.\n for (let i = 0; i < 3; i++) {\n record(\"https://app.example.test/dashboard\", \"https://hosted.example.test/handler/sign-in\");\n }\n expect(() => {\n record(\"https://app.example.test/dashboard\", \"https://hosted.example.test/handler/sign-in\");\n }).toThrowError(/Redirect loop detected/);\n });\n});\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as _HexclaveServerAppImplIncomplete } from "../../../../admin-app-impl-
|
|
1
|
+
import { n as _HexclaveServerAppImplIncomplete } from "../../../../admin-app-impl-Bga0o6cL.js";
|
|
2
2
|
export { _HexclaveServerAppImplIncomplete };
|
|
@@ -7,6 +7,7 @@ let _hexclave_shared_dist_utils_react = require("@hexclave/shared/dist/utils/rea
|
|
|
7
7
|
let _hexclave_shared_dist_utils_results = require("@hexclave/shared/dist/utils/results");
|
|
8
8
|
let __common_js = require("./common.js");
|
|
9
9
|
let _hexclave_shared_dist_sessions = require("@hexclave/shared/dist/sessions");
|
|
10
|
+
let _hexclave_shared_dist_utils_uuids = require("@hexclave/shared/dist/utils/uuids");
|
|
10
11
|
let _simplewebauthn_browser = require("@simplewebauthn/browser");
|
|
11
12
|
let ____________utils_url_js = require("../../../../utils/url.js");
|
|
12
13
|
let ______api_keys_index_js = require("../../api-keys/index.js");
|
|
@@ -226,6 +227,15 @@ var _HexclaveServerAppImplIncomplete = class extends __client_app_impl_js._Hexcl
|
|
|
226
227
|
query
|
|
227
228
|
});
|
|
228
229
|
});
|
|
230
|
+
this._serverTeamCache = (0, __common_js.createCache)(async ([teamId]) => {
|
|
231
|
+
if (!(0, _hexclave_shared_dist_utils_uuids.isUuid)(teamId)) return null;
|
|
232
|
+
try {
|
|
233
|
+
return await this._interface.getServerTeam(teamId);
|
|
234
|
+
} catch (error) {
|
|
235
|
+
if (_hexclave_shared.KnownErrors.TeamNotFound.isInstance(error)) return null;
|
|
236
|
+
throw error;
|
|
237
|
+
}
|
|
238
|
+
});
|
|
229
239
|
this._serverUserTeamInvitationsCache = (0, __common_js.createCache)(async ([userId]) => {
|
|
230
240
|
return await this._interface.listServerUserTeamInvitations(userId);
|
|
231
241
|
});
|
|
@@ -832,11 +842,19 @@ var _HexclaveServerAppImplIncomplete = class extends __client_app_impl_js._Hexcl
|
|
|
832
842
|
serverMetadata: crud.server_metadata,
|
|
833
843
|
async update(update) {
|
|
834
844
|
await app._interface.updateServerTeam(crud.id, (0, ______teams_index_js.serverTeamUpdateOptionsToCrud)(update));
|
|
835
|
-
await Promise.all([
|
|
845
|
+
await Promise.all([
|
|
846
|
+
app._serverTeamCache.refresh([crud.id]),
|
|
847
|
+
app._serverTeamsCache.refreshWhere(() => true),
|
|
848
|
+
app._serverUsersCache.refreshWhere(() => true)
|
|
849
|
+
]);
|
|
836
850
|
},
|
|
837
851
|
async delete() {
|
|
838
852
|
await app._interface.deleteServerTeam(crud.id);
|
|
839
|
-
await Promise.all([
|
|
853
|
+
await Promise.all([
|
|
854
|
+
app._serverTeamCache.refresh([crud.id]),
|
|
855
|
+
app._serverTeamsCache.refreshWhere(() => true),
|
|
856
|
+
app._serverUsersCache.refreshWhere(() => true)
|
|
857
|
+
]);
|
|
840
858
|
},
|
|
841
859
|
async listUsers() {
|
|
842
860
|
return _hexclave_shared_dist_utils_results.Result.orThrow(await app._serverTeamMemberProfilesCache.getOrWait([crud.id], "write-only")).map((u) => app._serverTeamUserFromCrud(u));
|
|
@@ -1255,6 +1273,7 @@ var _HexclaveServerAppImplIncomplete = class extends __client_app_impl_js._Hexcl
|
|
|
1255
1273
|
}
|
|
1256
1274
|
async createTeam(data) {
|
|
1257
1275
|
const team = await this._interface.createServerTeam((0, ______teams_index_js.serverTeamCreateOptionsToCrud)(data));
|
|
1276
|
+
await this._serverTeamCache.refresh([team.id]);
|
|
1258
1277
|
await this._serverTeamsCache.refreshWhere(() => true);
|
|
1259
1278
|
return this._serverTeamFromCrud(team);
|
|
1260
1279
|
}
|
|
@@ -1305,17 +1324,19 @@ var _HexclaveServerAppImplIncomplete = class extends __client_app_impl_js._Hexcl
|
|
|
1305
1324
|
if (typeof options === "object" && "apiKey" in options) return await this._getTeamByApiKey(options.apiKey);
|
|
1306
1325
|
else {
|
|
1307
1326
|
const teamId = options;
|
|
1308
|
-
|
|
1327
|
+
if (teamId == null) return null;
|
|
1328
|
+
const team = _hexclave_shared_dist_utils_results.Result.orThrow(await this._serverTeamCache.getOrWait([teamId], "write-only"));
|
|
1329
|
+
return team == null ? null : this._serverTeamFromCrud(team);
|
|
1309
1330
|
}
|
|
1310
1331
|
}
|
|
1311
1332
|
useTeam(options) {
|
|
1312
1333
|
if (typeof options === "object" && "apiKey" in options) return this._useTeamByApiKey(options.apiKey);
|
|
1313
1334
|
else {
|
|
1314
1335
|
const teamId = options;
|
|
1315
|
-
const
|
|
1336
|
+
const team = (0, __common_js.useAsyncCache)(this._serverTeamCache, [teamId ?? ""], "serverApp.useTeam()");
|
|
1316
1337
|
return (0, react.useMemo)(() => {
|
|
1317
|
-
return
|
|
1318
|
-
}, [
|
|
1338
|
+
return team == null ? null : this._serverTeamFromCrud(team);
|
|
1339
|
+
}, [team]);
|
|
1319
1340
|
}
|
|
1320
1341
|
}
|
|
1321
1342
|
_createServerDataVaultStore(id) {
|