@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.
Files changed (164) hide show
  1. package/dist/{admin-app-impl-Cmhx-Gnk.d.ts → admin-app-impl-Bga0o6cL.d.ts} +5 -2
  2. package/dist/{admin-app-impl-Cmhx-Gnk.d.ts.map → admin-app-impl-Bga0o6cL.d.ts.map} +1 -1
  3. package/dist/clickmap/clickmap-core.d.ts +1 -1
  4. package/dist/clickmap/index.d.ts +1 -1
  5. package/dist/{client-app-Majjw8Xp.d.ts → client-app-B0kBC67Z.d.ts} +2 -1
  6. package/dist/{client-app-Majjw8Xp.d.ts.map → client-app-B0kBC67Z.d.ts.map} +1 -1
  7. package/dist/common-BK_sydxF.d.ts.map +1 -1
  8. package/dist/components/selected-team-switcher.d.ts +1 -1
  9. package/dist/components/team-icon.d.ts +1 -1
  10. package/dist/components/team-switcher.d.ts +1 -1
  11. package/dist/components-page/account-settings/active-sessions/active-sessions-page.js +1 -1
  12. package/dist/components-page/account-settings/api-keys/api-keys-page.js +2 -2
  13. package/dist/components-page/account-settings/payments/payments-page.d.ts +1 -1
  14. package/dist/components-page/account-settings/teams/leave-team-section.d.ts +1 -1
  15. package/dist/components-page/account-settings/teams/team-api-keys-section.d.ts +1 -1
  16. package/dist/components-page/account-settings/teams/team-api-keys-section.js +2 -2
  17. package/dist/components-page/account-settings/teams/team-display-name-section.d.ts +1 -1
  18. package/dist/components-page/account-settings/teams/team-member-invitation-section.d.ts +1 -1
  19. package/dist/components-page/account-settings/teams/team-member-list-section.d.ts +1 -1
  20. package/dist/components-page/account-settings/teams/team-page.d.ts +1 -1
  21. package/dist/components-page/account-settings/teams/team-profile-image-section.d.ts +1 -1
  22. package/dist/components-page/account-settings/teams/team-profile-user-section.d.ts +1 -1
  23. package/dist/components-page/hexclave-handler-client.d.ts +1 -1
  24. package/dist/components-page/hexclave-handler.d.ts +1 -1
  25. package/dist/components-page/sign-out.js +11 -2
  26. package/dist/components-page/sign-out.js.map +1 -1
  27. package/dist/components-page/sign-out.test.d.ts +1 -0
  28. package/dist/components-page/sign-out.test.js +157 -0
  29. package/dist/components-page/sign-out.test.js.map +1 -0
  30. package/dist/dev-tool/dev-tool-core.d.ts +1 -1
  31. package/dist/dev-tool/dev-tool-core.d.ts.map +1 -1
  32. package/dist/dev-tool/dev-tool-core.js +58 -28
  33. package/dist/dev-tool/dev-tool-core.js.map +1 -1
  34. package/dist/dev-tool/dev-tool-styles.js +35 -0
  35. package/dist/dev-tool/dev-tool-styles.js.map +1 -1
  36. package/dist/dev-tool/index.d.ts +1 -1
  37. package/dist/esm/components-page/account-settings/active-sessions/active-sessions-page.js +1 -1
  38. package/dist/esm/components-page/account-settings/api-keys/api-keys-page.js +2 -2
  39. package/dist/esm/components-page/account-settings/teams/team-api-keys-section.js +2 -2
  40. package/dist/esm/components-page/sign-out.d.ts.map +1 -1
  41. package/dist/esm/components-page/sign-out.js +12 -3
  42. package/dist/esm/components-page/sign-out.js.map +1 -1
  43. package/dist/esm/components-page/sign-out.test.d.ts +1 -0
  44. package/dist/esm/components-page/sign-out.test.js +156 -0
  45. package/dist/esm/components-page/sign-out.test.js.map +1 -0
  46. package/dist/esm/dev-tool/dev-tool-core.d.ts.map +1 -1
  47. package/dist/esm/dev-tool/dev-tool-core.js +58 -28
  48. package/dist/esm/dev-tool/dev-tool-core.js.map +1 -1
  49. package/dist/esm/dev-tool/dev-tool-styles.js +35 -0
  50. package/dist/esm/dev-tool/dev-tool-styles.js.map +1 -1
  51. package/dist/esm/generated/global-css.d.ts +1 -1
  52. package/dist/esm/generated/global-css.js +1 -1
  53. package/dist/esm/generated/global-css.js.map +1 -1
  54. package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -0
  55. package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
  56. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js +97 -0
  57. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
  58. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +1 -0
  59. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
  60. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js +26 -1
  61. package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
  62. package/dist/esm/lib/hexclave-app/apps/implementations/common.d.ts.map +1 -1
  63. package/dist/esm/lib/hexclave-app/apps/implementations/common.js +21 -4
  64. package/dist/esm/lib/hexclave-app/apps/implementations/common.js.map +1 -1
  65. package/dist/esm/lib/hexclave-app/apps/implementations/redirect-back-state.d.ts +37 -0
  66. package/dist/esm/lib/hexclave-app/apps/implementations/redirect-back-state.d.ts.map +1 -0
  67. package/dist/esm/lib/hexclave-app/apps/implementations/redirect-back-state.js +89 -0
  68. package/dist/esm/lib/hexclave-app/apps/implementations/redirect-back-state.js.map +1 -0
  69. package/dist/esm/lib/hexclave-app/apps/implementations/redirect-back-state.test.d.ts +1 -0
  70. package/dist/esm/lib/hexclave-app/apps/implementations/redirect-back-state.test.js +134 -0
  71. package/dist/esm/lib/hexclave-app/apps/implementations/redirect-back-state.test.js.map +1 -0
  72. package/dist/esm/lib/hexclave-app/apps/implementations/redirect-loop-breaker.d.ts +8 -0
  73. package/dist/esm/lib/hexclave-app/apps/implementations/redirect-loop-breaker.d.ts.map +1 -0
  74. package/dist/esm/lib/hexclave-app/apps/implementations/redirect-loop-breaker.js +73 -0
  75. package/dist/esm/lib/hexclave-app/apps/implementations/redirect-loop-breaker.js.map +1 -0
  76. package/dist/esm/lib/hexclave-app/apps/implementations/redirect-loop-breaker.test.d.ts +1 -0
  77. package/dist/esm/lib/hexclave-app/apps/implementations/redirect-loop-breaker.test.js +92 -0
  78. package/dist/esm/lib/hexclave-app/apps/implementations/redirect-loop-breaker.test.js.map +1 -0
  79. package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -0
  80. package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.d.ts.map +1 -1
  81. package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.js +27 -6
  82. package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.js.map +1 -1
  83. package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.d.ts +1 -0
  84. package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.d.ts.map +1 -1
  85. package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.js.map +1 -1
  86. package/dist/esm/pushed-config-error-overlay/index.js +1 -1
  87. package/dist/esm/pushed-config-error-overlay/index.js.map +1 -1
  88. package/dist/esm/utils/session-storage.d.ts +12 -0
  89. package/dist/esm/utils/session-storage.d.ts.map +1 -0
  90. package/dist/esm/utils/session-storage.js +30 -0
  91. package/dist/esm/utils/session-storage.js.map +1 -0
  92. package/dist/generated/global-css.d.ts +1 -1
  93. package/dist/generated/global-css.js +1 -1
  94. package/dist/generated/global-css.js.map +1 -1
  95. package/dist/{hexclave-provider-IAd7nlbi.d.ts → hexclave-provider-BQ9wrtBt.d.ts} +2 -2
  96. package/dist/{hexclave-provider-IAd7nlbi.d.ts.map → hexclave-provider-BQ9wrtBt.d.ts.map} +1 -1
  97. package/dist/{hooks-BKXOF6jh.d.ts → hooks-ykQ7Wsco.d.ts} +2 -2
  98. package/dist/{hooks-BKXOF6jh.d.ts.map → hooks-ykQ7Wsco.d.ts.map} +1 -1
  99. package/dist/{index-Ck1qKoin.d.ts → index-DwE1cNLH.d.ts} +2 -2
  100. package/dist/{index-Ck1qKoin.d.ts.map → index-DwE1cNLH.d.ts.map} +1 -1
  101. package/dist/index.d.ts +4 -4
  102. package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -1
  103. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js +97 -0
  104. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
  105. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +1 -1
  106. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js +26 -1
  107. package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
  108. package/dist/lib/hexclave-app/apps/implementations/common.js +21 -4
  109. package/dist/lib/hexclave-app/apps/implementations/common.js.map +1 -1
  110. package/dist/lib/hexclave-app/apps/implementations/index.d.ts +1 -1
  111. package/dist/lib/hexclave-app/apps/implementations/redirect-back-state.d.ts +37 -0
  112. package/dist/lib/hexclave-app/apps/implementations/redirect-back-state.d.ts.map +1 -0
  113. package/dist/lib/hexclave-app/apps/implementations/redirect-back-state.js +92 -0
  114. package/dist/lib/hexclave-app/apps/implementations/redirect-back-state.js.map +1 -0
  115. package/dist/lib/hexclave-app/apps/implementations/redirect-back-state.test.d.ts +1 -0
  116. package/dist/lib/hexclave-app/apps/implementations/redirect-back-state.test.js +133 -0
  117. package/dist/lib/hexclave-app/apps/implementations/redirect-back-state.test.js.map +1 -0
  118. package/dist/lib/hexclave-app/apps/implementations/redirect-loop-breaker.d.ts +8 -0
  119. package/dist/lib/hexclave-app/apps/implementations/redirect-loop-breaker.d.ts.map +1 -0
  120. package/dist/lib/hexclave-app/apps/implementations/redirect-loop-breaker.js +74 -0
  121. package/dist/lib/hexclave-app/apps/implementations/redirect-loop-breaker.js.map +1 -0
  122. package/dist/lib/hexclave-app/apps/implementations/redirect-loop-breaker.test.d.ts +1 -0
  123. package/dist/lib/hexclave-app/apps/implementations/redirect-loop-breaker.test.js +91 -0
  124. package/dist/lib/hexclave-app/apps/implementations/redirect-loop-breaker.test.js.map +1 -0
  125. package/dist/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
  126. package/dist/lib/hexclave-app/apps/implementations/server-app-impl.js +27 -6
  127. package/dist/lib/hexclave-app/apps/implementations/server-app-impl.js.map +1 -1
  128. package/dist/lib/hexclave-app/apps/index.d.ts +1 -1
  129. package/dist/lib/hexclave-app/apps/interfaces/admin-app.d.ts +1 -1
  130. package/dist/lib/hexclave-app/apps/interfaces/client-app.d.ts +1 -1
  131. package/dist/lib/hexclave-app/apps/interfaces/client-app.js.map +1 -1
  132. package/dist/lib/hexclave-app/apps/interfaces/server-app.d.ts +1 -1
  133. package/dist/lib/hexclave-app/index.d.ts +1 -1
  134. package/dist/lib/hexclave-app/projects/index.d.ts +1 -1
  135. package/dist/lib/hexclave-app/teams/index.d.ts +1 -1
  136. package/dist/lib/hexclave-app/users/index.d.ts +1 -1
  137. package/dist/lib/hooks.d.ts +1 -1
  138. package/dist/providers/hexclave-context.d.ts +1 -1
  139. package/dist/providers/hexclave-provider-client.d.ts +1 -1
  140. package/dist/providers/hexclave-provider.d.ts +1 -1
  141. package/dist/pushed-config-error-overlay/index.d.ts +1 -1
  142. package/dist/pushed-config-error-overlay/index.js +1 -1
  143. package/dist/pushed-config-error-overlay/index.js.map +1 -1
  144. package/dist/sign-out-DBnSkfDz.d.ts.map +1 -1
  145. package/dist/utils/session-storage.d.ts +12 -0
  146. package/dist/utils/session-storage.d.ts.map +1 -0
  147. package/dist/utils/session-storage.js +33 -0
  148. package/dist/utils/session-storage.js.map +1 -0
  149. package/package.json +4 -4
  150. package/src/components-page/sign-out.test.tsx +190 -0
  151. package/src/components-page/sign-out.tsx +22 -3
  152. package/src/dev-tool/dev-tool-core.ts +100 -35
  153. package/src/dev-tool/dev-tool-styles.ts +35 -0
  154. package/src/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.ts +127 -0
  155. package/src/lib/hexclave-app/apps/implementations/client-app-impl.ts +34 -1
  156. package/src/lib/hexclave-app/apps/implementations/common.ts +18 -1
  157. package/src/lib/hexclave-app/apps/implementations/redirect-back-state.test.ts +134 -0
  158. package/src/lib/hexclave-app/apps/implementations/redirect-back-state.ts +140 -0
  159. package/src/lib/hexclave-app/apps/implementations/redirect-loop-breaker.test.ts +127 -0
  160. package/src/lib/hexclave-app/apps/implementations/redirect-loop-breaker.ts +117 -0
  161. package/src/lib/hexclave-app/apps/implementations/server-app-impl.ts +31 -5
  162. package/src/lib/hexclave-app/apps/interfaces/client-app.ts +1 -0
  163. package/src/pushed-config-error-overlay/index.ts +1 -1
  164. package/src/utils/session-storage.ts +45 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign-out.test.js","names":[],"sources":["../../../src/components-page/sign-out.test.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n// @vitest-environment jsdom\n\nimport React, { act } from \"react\";\nimport { createRoot, type Root } from \"react-dom/client\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport type { CurrentUser } from \"../lib/hexclave-app\";\nimport type { StackClientApp } from \"../lib/hexclave-app/apps/interfaces/client-app\";\nimport { hexclaveAppInternalsSymbol } from \"../lib/hexclave-app/common\";\nimport { TranslationProviderClient } from \"../providers/translation-provider-client\";\nimport { SignOut } from \"./sign-out\";\n\nconst appMockState = vi.hoisted(() => ({\n app: null as unknown,\n user: null as unknown,\n}));\n\nvi.mock(\"..\", () => ({\n useStackApp: () => {\n if (appMockState.app == null) {\n throw new Error(\"Expected test app to be set before rendering.\");\n }\n return appMockState.app;\n },\n useUser: () => appMockState.user,\n}));\n\n// The real `use` delegates to React.use, which breaks in this test setup because the shared\n// package and react-dom resolve different React instances. Replace it with an equivalent\n// Suspense-compatible implementation that doesn't depend on the React dispatcher.\nvi.mock(\"@hexclave/shared/dist/utils/react\", async (importOriginal) => {\n const original = await importOriginal<typeof import(\"@hexclave/shared/dist/utils/react\")>();\n type PromiseState = { status: \"pending\" } | { status: \"ok\", value: unknown } | { status: \"error\", error: unknown };\n const promiseStates = new WeakMap<Promise<unknown>, PromiseState>();\n return {\n ...original,\n use: <T,>(promise: Promise<T>): T => {\n const state = promiseStates.get(promise);\n if (state == null) {\n promiseStates.set(promise, { status: \"pending\" });\n promise.then(\n (value) => promiseStates.set(promise, { status: \"ok\", value }),\n (error) => promiseStates.set(promise, { status: \"error\", error }),\n );\n throw promise;\n }\n switch (state.status) {\n case \"pending\": {\n throw promise;\n }\n case \"error\": {\n throw state.error;\n }\n case \"ok\": {\n // This cast is safe because the value was stored from this exact promise's resolution;\n // the WeakMap just can't express the per-key type relationship.\n return state.value as T;\n }\n }\n },\n };\n});\n\nvi.mock(\"@hexclave/ui\", async () => {\n const React = await import(\"react\");\n return {\n Button: (props: { children: React.ReactNode, onClick?: () => void }) => (\n <button type=\"button\" onClick={props.onClick}>{props.children}</button>\n ),\n Typography: (props: { children: React.ReactNode }) => <div>{props.children}</div>,\n cn: (...classes: (string | false | null | undefined)[]) => classes.filter(Boolean).join(\" \"),\n };\n});\n\nconst previousActEnvironment = Reflect.get(globalThis, \"IS_REACT_ACT_ENVIRONMENT\");\n\nfunction createAppTestDouble(options: {\n trustedUrls: string[],\n}) {\n const app = {\n redirectToSignIn: vi.fn(async () => {}),\n [hexclaveAppInternalsSymbol]: {\n isTrustedRedirectUrl: vi.fn(async (url: string) => options.trustedUrls.includes(url)),\n },\n };\n\n // This test double intentionally implements only the StackClientApp surface that SignOut and\n // the rendered signed-out card touch.\n return app as unknown as StackClientApp<true>;\n}\n\nfunction createUserTestDouble() {\n const user = {\n signOut: vi.fn(async () => {}),\n };\n // Same as the app test double: only the surface SignOut touches.\n return user as unknown as CurrentUser & { signOut: ReturnType<typeof vi.fn> };\n}\n\n// jsdom's window.location is not configurable, so it can't be spied on; replace it wholesale.\nconst originalLocation = window.location;\nlet locationReplaceMock = vi.fn();\n\nlet root: Root | null = null;\nlet container: HTMLDivElement | null = null;\n\nasync function renderSignOut(options: {\n app: StackClientApp<true>,\n user: unknown,\n searchParams?: Record<string, string>,\n}) {\n appMockState.app = options.app;\n appMockState.user = options.user;\n container = document.createElement(\"div\");\n document.body.append(container);\n root = createRoot(container);\n await act(async () => {\n root?.render(\n <TranslationProviderClient quetzalKeys={new Map()} quetzalLocale={new Map()}>\n <React.Suspense fallback={<div data-testid=\"suspense-fallback\" />}>\n <SignOut searchParams={options.searchParams} />\n </React.Suspense>\n </TranslationProviderClient>\n );\n });\n await act(async () => {\n await new Promise((resolve) => setTimeout(resolve, 0));\n });\n}\n\ndescribe(\"SignOut\", () => {\n beforeEach(() => {\n Reflect.set(globalThis, \"IS_REACT_ACT_ENVIRONMENT\", true);\n locationReplaceMock = vi.fn();\n Reflect.deleteProperty(window, \"location\");\n Reflect.set(window, \"location\", { href: \"https://demo.example.test/handler/sign-out\", replace: locationReplaceMock });\n });\n\n afterEach(() => {\n act(() => {\n root?.unmount();\n });\n container?.remove();\n root = null;\n container = null;\n appMockState.app = null;\n appMockState.user = null;\n Reflect.set(window, \"location\", originalLocation);\n vi.restoreAllMocks();\n Reflect.set(globalThis, \"IS_REACT_ACT_ENVIRONMENT\", previousActEnvironment);\n });\n\n it(\"signs out with a trusted after_auth_return_to redirect URL\", async () => {\n const app = createAppTestDouble({ trustedUrls: [\"/dashboard\"] });\n const user = createUserTestDouble();\n\n await renderSignOut({ app, user, searchParams: { after_auth_return_to: \"/dashboard\" } });\n\n expect(user.signOut).toHaveBeenCalledWith({ redirectUrl: \"/dashboard\" });\n });\n\n it(\"ignores an untrusted after_auth_return_to and falls back to the default sign-out redirect\", async () => {\n const app = createAppTestDouble({ trustedUrls: [] });\n const user = createUserTestDouble();\n\n await renderSignOut({ app, user, searchParams: { after_auth_return_to: \"https://evil.example.test/phishing\" } });\n\n // The untrusted URL is dropped entirely; sign-out proceeds with the default destination.\n expect(user.signOut).toHaveBeenCalledWith({ redirectUrl: undefined });\n });\n\n it(\"does not follow an untrusted after_auth_return_to when already signed out\", async () => {\n const app = createAppTestDouble({ trustedUrls: [] });\n\n await renderSignOut({ app, user: null, searchParams: { after_auth_return_to: \"https://evil.example.test/phishing-signed-out\" } });\n\n expect(locationReplaceMock).not.toHaveBeenCalled();\n });\n\n it(\"follows a trusted after_auth_return_to when already signed out\", async () => {\n const app = createAppTestDouble({ trustedUrls: [\"/trusted-dashboard\"] });\n\n await renderSignOut({ app, user: null, searchParams: { after_auth_return_to: \"/trusted-dashboard\" } });\n\n expect(locationReplaceMock).toHaveBeenCalledWith(\"/trusted-dashboard\");\n });\n});\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"dev-tool-core.d.ts","names":[],"sources":["../../../src/dev-tool/dev-tool-core.ts"],"mappings":";;;iBAiuEgB,aAAA,CAAc,GAAyB,EAApB,cAAc"}
1
+ {"version":3,"file":"dev-tool-core.d.ts","names":[],"sources":["../../../src/dev-tool/dev-tool-core.ts"],"mappings":";;;iBAkyEgB,aAAA,CAAc,GAAyB,EAApB,cAAc"}
@@ -4,7 +4,6 @@ import { getGlobalUiInstance, h, hasAppendChild, setGlobalUiInstance, setHtml }
4
4
  import { hexclaveAppInternalsSymbol } from "../lib/hexclave-app/common.js";
5
5
  import { isLocalhost } from "@hexclave/shared/dist/utils/urls";
6
6
  import { getPagePrompt } from "../lib/hexclave-app/url-targets.js";
7
- import { envVars } from "../generated/env.js";
8
7
  import { getBaseUrl } from "../lib/hexclave-app/apps/implementations/common.js";
9
8
  import { devToolCSS } from "./dev-tool-styles.js";
10
9
  import { clampTriggerPosition, getSnappedTriggerPlacement, resolveTriggerPosition } from "./dev-tool-trigger-position.js";
@@ -17,6 +16,8 @@ const MAX_LOG_ENTRIES = 500;
17
16
  const CONSOLE_LOG_BATCH_SIZE = 100;
18
17
  const DRAG_THRESHOLD = 5;
19
18
  const DOCS_URL = "https://docs.hexclave.com";
19
+ const LOCAL_DASHBOARD_ORIGIN = "http://localhost:26700";
20
+ const HOSTED_DASHBOARD_URL = "https://app.hexclave.com";
20
21
  const TABS = [
21
22
  {
22
23
  id: "overview",
@@ -136,33 +137,35 @@ function nextId() {
136
137
  function resolveApiBaseUrl(app) {
137
138
  return getBaseUrl(app[hexclaveAppInternalsSymbol].getConstructorOptions().baseUrl);
138
139
  }
139
- function shouldShowDashboardTab(app) {
140
- return envVars.HEXCLAVE_IS_LOCAL_EMULATOR === "true" && isLocalhost(resolveApiBaseUrl(app));
140
+ function localDashboardProjectUrl(app) {
141
+ return `${LOCAL_DASHBOARD_ORIGIN}/projects/${encodeURIComponent(app.projectId)}`;
141
142
  }
142
- function getTabsForApp(app) {
143
- if (shouldShowDashboardTab(app)) return TABS;
144
- return TABS.filter((tab) => tab.id !== "dashboard");
143
+ function parseProjectAvailabilityResponse(body) {
144
+ if (typeof body !== "object" || body === null) return false;
145
+ return "project_available" in body && body.project_available === true;
145
146
  }
146
- function deriveDashboardBaseUrl(apiBaseUrl) {
147
+ const LOCAL_DASHBOARD_PROBE_TIMEOUT_MS = 3e3;
148
+ function canReachLocalDashboard() {
149
+ return isLocalhost(window.location.href) && window.location.protocol === "http:";
150
+ }
151
+ async function isLocalDashboardProjectAvailable(app) {
152
+ if (!canReachLocalDashboard()) return false;
153
+ const url = `${LOCAL_DASHBOARD_ORIGIN}/api/development-environment/project-availability?project_id=${encodeURIComponent(app.projectId)}`;
154
+ const controller = new AbortController();
155
+ const timeout = setTimeout(() => controller.abort(), LOCAL_DASHBOARD_PROBE_TIMEOUT_MS);
147
156
  try {
148
- const url = new URL(apiBaseUrl);
149
- if (url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]") {
150
- const port = url.port;
151
- if (port && port.endsWith("02")) url.port = port.slice(0, -2) + "01";
152
- return url.origin;
153
- }
154
- if (url.hostname.startsWith("api.")) {
155
- url.hostname = "app." + url.hostname.slice(4);
156
- return url.origin;
157
- }
158
- return url.origin;
157
+ const response = await fetch(url, {
158
+ method: "GET",
159
+ signal: controller.signal
160
+ });
161
+ if (!response.ok) return false;
162
+ return parseProjectAvailabilityResponse(await response.json());
159
163
  } catch {
160
- return "https://app.hexclave.com";
164
+ return false;
165
+ } finally {
166
+ clearTimeout(timeout);
161
167
  }
162
168
  }
163
- function resolveDashboardUrl(app) {
164
- return `${deriveDashboardBaseUrl(resolveApiBaseUrl(app))}/projects/${encodeURIComponent(app.projectId)}`;
165
- }
166
169
  function formatTimestamp(ts) {
167
170
  return new Date(ts).toLocaleTimeString([], {
168
171
  hour: "2-digit",
@@ -427,8 +430,8 @@ function createTabBar(tabs, activeTab, onTabChange, opts) {
427
430
  setActive
428
431
  };
429
432
  }
430
- function createIframeTab(src, title, loadingMsg = "Loading…", errorMsg = "Unable to load content", errorDetail, openExternallyLabel) {
431
- const container = h("div", { className: "sdt-iframe-container" });
433
+ function populateIframeTab(container, src, title, loadingMsg = "Loading…", errorMsg = "Unable to load content", errorDetail, openExternallyLabel) {
434
+ container.innerHTML = "";
432
435
  if (openExternallyLabel != null) container.appendChild(h("div", { className: "sdt-iframe-toolbar" }, h("a", {
433
436
  href: src,
434
437
  target: "_blank",
@@ -457,7 +460,7 @@ function createIframeTab(src, title, loadingMsg = "Loading…", errorMsg = "Unab
457
460
  } }, errorDetail));
458
461
  const retryBtn = h("button", { className: "sdt-iframe-error-btn" }, "Retry");
459
462
  retryBtn.addEventListener("click", () => {
460
- container.replaceWith(createIframeTab(src, title, loadingMsg, errorMsg, errorDetail, openExternallyLabel));
463
+ populateIframeTab(container, src, title, loadingMsg, errorMsg, errorDetail, openExternallyLabel);
461
464
  });
462
465
  errDiv.appendChild(retryBtn);
463
466
  const link = h("a", {
@@ -474,7 +477,6 @@ function createIframeTab(src, title, loadingMsg = "Loading…", errorMsg = "Unab
474
477
  container.appendChild(errDiv);
475
478
  });
476
479
  container.appendChild(iframe);
477
- return container;
478
480
  }
479
481
  function hasPersistentTokenStoreForDevTool(app) {
480
482
  return app[hexclaveAppInternalsSymbol].getConstructorOptions().tokenStore !== null;
@@ -1420,7 +1422,35 @@ function createAITab(app) {
1420
1422
  return container;
1421
1423
  }
1422
1424
  function createDashboardTab(app) {
1423
- return createIframeTab(resolveDashboardUrl(app), "Hexclave Dashboard", "Loading dashboard…", "Unable to load dashboard", "The dashboard may require authentication or block framing", "Open in New Tab");
1425
+ const container = h("div", { className: "sdt-iframe-container" });
1426
+ function showChecking() {
1427
+ container.innerHTML = "";
1428
+ container.appendChild(h("div", { className: "sdt-iframe-loading" }, "Checking development environment…"));
1429
+ }
1430
+ function showUnavailable() {
1431
+ container.innerHTML = "";
1432
+ const message = h("div", { className: "sdt-dashboard-unavailable" });
1433
+ const text = h("div", { className: "sdt-dashboard-unavailable-text" });
1434
+ text.appendChild(document.createTextNode("Navigate to "));
1435
+ text.appendChild(h("a", {
1436
+ className: "sdt-dashboard-unavailable-link",
1437
+ href: HOSTED_DASHBOARD_URL,
1438
+ target: "_blank",
1439
+ rel: "noopener noreferrer"
1440
+ }, HOSTED_DASHBOARD_URL));
1441
+ text.appendChild(document.createTextNode(" to view this project's dashboard"));
1442
+ message.appendChild(text);
1443
+ container.appendChild(message);
1444
+ }
1445
+ async function check() {
1446
+ showChecking();
1447
+ const available = await isLocalDashboardProjectAvailable(app);
1448
+ if (!container.isConnected) return;
1449
+ if (available) populateIframeTab(container, localDashboardProjectUrl(app), "Hexclave Dashboard", "Loading dashboard…", "Unable to load dashboard", "The dashboard may require authentication or block framing", "Open in New Tab");
1450
+ else showUnavailable();
1451
+ }
1452
+ runAsynchronously(check);
1453
+ return container;
1424
1454
  }
1425
1455
  function createSupportTab(app) {
1426
1456
  const container = h("div", { className: "sdt-support-tab" });
@@ -1816,7 +1846,7 @@ function createPanel(app, state, logStore, onClose) {
1816
1846
  panel.style.width = state.get().panelWidth + "px";
1817
1847
  panel.style.height = state.get().panelHeight + "px";
1818
1848
  }
1819
- const tabs = getTabsForApp(app);
1849
+ const tabs = TABS;
1820
1850
  const storedActiveTab = state.get().activeTab;
1821
1851
  const activeTab = tabs.some((tab) => tab.id === storedActiveTab) ? storedActiveTab : DEFAULT_STATE.activeTab;
1822
1852
  applyPanelMode(activeTab);