@hexclave/shared 1.0.3 → 1.0.5

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 (343) hide show
  1. package/dist/ai/unified-prompts/reminders.js +4 -3
  2. package/dist/ai/unified-prompts/reminders.js.map +1 -1
  3. package/dist/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.d.ts +3 -1
  4. package/dist/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.d.ts.map +1 -1
  5. package/dist/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js +216 -29
  6. package/dist/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js.map +1 -1
  7. package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-index.d.ts.map +1 -1
  8. package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-index.js +1 -0
  9. package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-index.js.map +1 -1
  10. package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.d.ts +18 -6
  11. package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js +23 -8
  12. package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js.map +1 -1
  13. package/dist/ai/unified-prompts/skill-site-prompt-parts/openapi-specs.js +1 -1
  14. package/dist/ai/unified-prompts/skill-site-prompt-parts/openapi-specs.js.map +1 -1
  15. package/dist/config/schema.d.ts +71 -71
  16. package/dist/config/schema.d.ts.map +1 -1
  17. package/dist/config/schema.js +5 -5
  18. package/dist/config/schema.js.map +1 -1
  19. package/dist/config-authoring.d.ts +4 -4
  20. package/dist/config-authoring.d.ts.map +1 -1
  21. package/dist/config-authoring.js +2 -2
  22. package/dist/config-authoring.js.map +1 -1
  23. package/dist/config-rendering.d.ts +4 -4
  24. package/dist/config-rendering.d.ts.map +1 -1
  25. package/dist/config-rendering.js +19 -18
  26. package/dist/config-rendering.js.map +1 -1
  27. package/dist/esm/ai/unified-prompts/reminders.js +4 -3
  28. package/dist/esm/ai/unified-prompts/reminders.js.map +1 -1
  29. package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.d.ts +3 -1
  30. package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.d.ts.map +1 -1
  31. package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js +215 -30
  32. package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js.map +1 -1
  33. package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-index.d.ts.map +1 -1
  34. package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-index.js +1 -0
  35. package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-index.js.map +1 -1
  36. package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.d.ts +18 -6
  37. package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js +23 -8
  38. package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js.map +1 -1
  39. package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/openapi-specs.js +1 -1
  40. package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/openapi-specs.js.map +1 -1
  41. package/dist/esm/config/schema.d.ts +71 -71
  42. package/dist/esm/config/schema.d.ts.map +1 -1
  43. package/dist/esm/config/schema.js +5 -5
  44. package/dist/esm/config/schema.js.map +1 -1
  45. package/dist/esm/config-authoring.d.ts +4 -4
  46. package/dist/esm/config-authoring.d.ts.map +1 -1
  47. package/dist/esm/config-authoring.js +2 -2
  48. package/dist/esm/config-authoring.js.map +1 -1
  49. package/dist/esm/config-rendering.d.ts +4 -4
  50. package/dist/esm/config-rendering.d.ts.map +1 -1
  51. package/dist/esm/config-rendering.js +15 -14
  52. package/dist/esm/config-rendering.js.map +1 -1
  53. package/dist/esm/helpers/vault/client-side.d.ts.map +1 -1
  54. package/dist/esm/helpers/vault/client-side.js.map +1 -1
  55. package/dist/esm/helpers/vault/server-side.d.ts.map +1 -1
  56. package/dist/esm/helpers/vault/server-side.js.map +1 -1
  57. package/dist/esm/hexclave-config-file.d.ts +15 -0
  58. package/dist/esm/hexclave-config-file.d.ts.map +1 -0
  59. package/dist/esm/{stack-config-file.js → hexclave-config-file.js} +7 -7
  60. package/dist/esm/hexclave-config-file.js.map +1 -0
  61. package/dist/esm/hooks/use-async-callback.js +1 -1
  62. package/dist/esm/hooks/use-async-external-store.js +1 -1
  63. package/dist/esm/hooks/use-strict-memo.js +1 -1
  64. package/dist/esm/interface/admin-metrics.d.ts +4 -4
  65. package/dist/esm/interface/admin-metrics.js.map +1 -1
  66. package/dist/esm/interface/client-interface.js +2 -2
  67. package/dist/esm/interface/client-interface.js.map +1 -1
  68. package/dist/esm/interface/conversations.d.ts +13 -13
  69. package/dist/esm/interface/crud/current-user.d.ts +9 -9
  70. package/dist/esm/interface/crud/email-outbox.d.ts +112 -112
  71. package/dist/esm/interface/crud/products.d.ts +1 -1
  72. package/dist/esm/interface/crud/project-api-keys.d.ts +2 -2
  73. package/dist/esm/interface/crud/project-permissions.js +5 -5
  74. package/dist/esm/interface/crud/project-permissions.js.map +1 -1
  75. package/dist/esm/interface/crud/projects.d.ts +28 -28
  76. package/dist/esm/interface/crud/team-member-profiles.d.ts +22 -22
  77. package/dist/esm/interface/crud/team-permissions.js +5 -5
  78. package/dist/esm/interface/crud/team-permissions.js.map +1 -1
  79. package/dist/esm/interface/crud/users.d.ts +14 -14
  80. package/dist/esm/interface/page-component-versions.js +107 -107
  81. package/dist/esm/interface/page-component-versions.js.map +1 -1
  82. package/dist/esm/interface/webhooks.d.ts +4 -4
  83. package/dist/esm/known-errors.js +4 -4
  84. package/dist/esm/known-errors.js.map +1 -1
  85. package/dist/esm/schema-fields.d.ts +15 -5
  86. package/dist/esm/schema-fields.d.ts.map +1 -1
  87. package/dist/esm/schema-fields.js +26 -16
  88. package/dist/esm/schema-fields.js.map +1 -1
  89. package/dist/esm/utils/crypto.d.ts.map +1 -1
  90. package/dist/esm/utils/crypto.js.map +1 -1
  91. package/dist/esm/utils/currency-constants.d.ts +6 -0
  92. package/dist/esm/utils/currency-constants.d.ts.map +1 -1
  93. package/dist/esm/utils/currency-constants.js.map +1 -1
  94. package/dist/esm/utils/dev-env-state-path.d.ts +3 -3
  95. package/dist/esm/utils/dev-env-state-path.d.ts.map +1 -1
  96. package/dist/esm/utils/dev-env-state-path.js +4 -4
  97. package/dist/esm/utils/dev-env-state-path.js.map +1 -1
  98. package/dist/esm/utils/errors.js +2 -2
  99. package/dist/esm/utils/errors.js.map +1 -1
  100. package/dist/esm/utils/esbuild.d.ts.map +1 -1
  101. package/dist/esm/utils/esbuild.js +0 -1
  102. package/dist/esm/utils/esbuild.js.map +1 -1
  103. package/dist/esm/utils/featurebase.d.ts +3 -3
  104. package/dist/esm/utils/featurebase.d.ts.map +1 -1
  105. package/dist/esm/utils/featurebase.js +15 -15
  106. package/dist/esm/utils/featurebase.js.map +1 -1
  107. package/dist/esm/utils/fs.js +6 -6
  108. package/dist/esm/utils/fs.js.map +1 -1
  109. package/dist/esm/utils/globals.d.ts.map +1 -1
  110. package/dist/esm/utils/globals.js +10 -10
  111. package/dist/esm/utils/globals.js.map +1 -1
  112. package/dist/esm/utils/jwt.d.ts.map +1 -1
  113. package/dist/esm/utils/jwt.js +3 -3
  114. package/dist/esm/utils/jwt.js.map +1 -1
  115. package/dist/esm/utils/redirect-urls.d.ts +3 -3
  116. package/dist/esm/utils/redirect-urls.d.ts.map +1 -1
  117. package/dist/esm/utils/redirect-urls.js +5 -5
  118. package/dist/esm/utils/redirect-urls.js.map +1 -1
  119. package/dist/esm/utils/urls.js.map +1 -1
  120. package/dist/helpers/vault/client-side.d.ts.map +1 -1
  121. package/dist/helpers/vault/client-side.js.map +1 -1
  122. package/dist/helpers/vault/server-side.d.ts.map +1 -1
  123. package/dist/helpers/vault/server-side.js.map +1 -1
  124. package/dist/hexclave-config-file.d.ts +15 -0
  125. package/dist/hexclave-config-file.d.ts.map +1 -0
  126. package/dist/{stack-config-file.js → hexclave-config-file.js} +8 -8
  127. package/dist/hexclave-config-file.js.map +1 -0
  128. package/dist/hooks/use-async-callback.js +1 -1
  129. package/dist/hooks/use-async-external-store.js +1 -1
  130. package/dist/hooks/use-strict-memo.js +1 -1
  131. package/dist/interface/admin-metrics.d.ts +4 -4
  132. package/dist/interface/admin-metrics.js.map +1 -1
  133. package/dist/interface/client-interface.js +2 -2
  134. package/dist/interface/client-interface.js.map +1 -1
  135. package/dist/interface/conversations.d.ts +13 -13
  136. package/dist/interface/crud/current-user.d.ts +9 -9
  137. package/dist/interface/crud/email-outbox.d.ts +112 -112
  138. package/dist/interface/crud/products.d.ts +1 -1
  139. package/dist/interface/crud/project-api-keys.d.ts +2 -2
  140. package/dist/interface/crud/project-permissions.js +5 -5
  141. package/dist/interface/crud/project-permissions.js.map +1 -1
  142. package/dist/interface/crud/projects.d.ts +28 -28
  143. package/dist/interface/crud/team-member-profiles.d.ts +22 -22
  144. package/dist/interface/crud/team-permissions.js +5 -5
  145. package/dist/interface/crud/team-permissions.js.map +1 -1
  146. package/dist/interface/crud/users.d.ts +14 -14
  147. package/dist/interface/page-component-versions.js +107 -107
  148. package/dist/interface/page-component-versions.js.map +1 -1
  149. package/dist/interface/webhooks.d.ts +4 -4
  150. package/dist/known-errors.js +4 -4
  151. package/dist/known-errors.js.map +1 -1
  152. package/dist/schema-fields.d.ts +15 -5
  153. package/dist/schema-fields.d.ts.map +1 -1
  154. package/dist/schema-fields.js +26 -16
  155. package/dist/schema-fields.js.map +1 -1
  156. package/dist/utils/crypto.d.ts.map +1 -1
  157. package/dist/utils/crypto.js.map +1 -1
  158. package/dist/utils/currency-constants.d.ts +6 -0
  159. package/dist/utils/currency-constants.d.ts.map +1 -1
  160. package/dist/utils/currency-constants.js.map +1 -1
  161. package/dist/utils/dev-env-state-path.d.ts +3 -3
  162. package/dist/utils/dev-env-state-path.d.ts.map +1 -1
  163. package/dist/utils/dev-env-state-path.js +5 -5
  164. package/dist/utils/dev-env-state-path.js.map +1 -1
  165. package/dist/utils/errors.js +2 -2
  166. package/dist/utils/errors.js.map +1 -1
  167. package/dist/utils/esbuild.d.ts.map +1 -1
  168. package/dist/utils/esbuild.js +0 -1
  169. package/dist/utils/esbuild.js.map +1 -1
  170. package/dist/utils/featurebase.d.ts +3 -3
  171. package/dist/utils/featurebase.d.ts.map +1 -1
  172. package/dist/utils/featurebase.js +15 -15
  173. package/dist/utils/featurebase.js.map +1 -1
  174. package/dist/utils/fs.js.map +1 -1
  175. package/dist/utils/globals.d.ts.map +1 -1
  176. package/dist/utils/globals.js +10 -10
  177. package/dist/utils/globals.js.map +1 -1
  178. package/dist/utils/jwt.d.ts.map +1 -1
  179. package/dist/utils/jwt.js +3 -3
  180. package/dist/utils/jwt.js.map +1 -1
  181. package/dist/utils/redirect-urls.d.ts +3 -3
  182. package/dist/utils/redirect-urls.d.ts.map +1 -1
  183. package/dist/utils/redirect-urls.js +5 -5
  184. package/dist/utils/redirect-urls.js.map +1 -1
  185. package/dist/utils/urls.js.map +1 -1
  186. package/package.json +2 -1
  187. package/src/ai/image-limits.ts +62 -0
  188. package/src/ai/llms/llms.ts +36 -0
  189. package/src/ai/unified-prompts/reminders.ts +27 -0
  190. package/src/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.ts +1047 -0
  191. package/src/ai/unified-prompts/skill-site-prompt-parts/brain.ts +5 -0
  192. package/src/ai/unified-prompts/skill-site-prompt-parts/cli-help.ts +5 -0
  193. package/src/ai/unified-prompts/skill-site-prompt-parts/config-docs.ts +5 -0
  194. package/src/ai/unified-prompts/skill-site-prompt-parts/custom-components-instructions.ts +5 -0
  195. package/src/ai/unified-prompts/skill-site-prompt-parts/dashboard-instructions.ts +5 -0
  196. package/src/ai/unified-prompts/skill-site-prompt-parts/docs-index.ts +80 -0
  197. package/src/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.ts +322 -0
  198. package/src/ai/unified-prompts/skill-site-prompt-parts/openapi-specs.ts +5 -0
  199. package/src/ai/unified-prompts/skill-site-prompt-parts/sdk-interface-source.ts +5 -0
  200. package/src/ai/unified-prompts/skill-site-prompt.ts +72 -0
  201. package/src/apps/apps-config.ts +177 -0
  202. package/src/apps/apps-ui.tsx +112 -0
  203. package/src/config/README.md +154 -0
  204. package/src/config/db-sync-mappings.ts +2293 -0
  205. package/src/config/format.ts +388 -0
  206. package/src/config/migrate-catalogs-to-product-lines.ts +475 -0
  207. package/src/config/schema-fuzzer.test.ts +377 -0
  208. package/src/config/schema.ts +1507 -0
  209. package/src/config-authoring.test.ts +52 -0
  210. package/src/config-authoring.ts +37 -0
  211. package/src/config-rendering.ts +142 -0
  212. package/src/crud.tsx +275 -0
  213. package/src/global.d.ts +1 -0
  214. package/src/helpers/emails.ts +250 -0
  215. package/src/helpers/init-prompt.ts +39 -0
  216. package/src/helpers/password.ts +16 -0
  217. package/src/helpers/production-mode.ts +58 -0
  218. package/src/helpers/support-sla.test.ts +124 -0
  219. package/src/helpers/support-sla.ts +93 -0
  220. package/src/helpers/vault/client-side.ts +92 -0
  221. package/src/helpers/vault/server-side.ts +153 -0
  222. package/src/hexclave-config-file.ts +120 -0
  223. package/src/hooks/use-async-callback.tsx +43 -0
  224. package/src/hooks/use-async-external-store.tsx +24 -0
  225. package/src/hooks/use-hash.tsx +14 -0
  226. package/src/hooks/use-hover.tsx +88 -0
  227. package/src/hooks/use-strict-memo.tsx +157 -0
  228. package/src/index.ts +17 -0
  229. package/src/interface/admin-interface.ts +1043 -0
  230. package/src/interface/admin-metrics.ts +214 -0
  231. package/src/interface/client-interface.test.ts +765 -0
  232. package/src/interface/client-interface.ts +2586 -0
  233. package/src/interface/conversations.ts +109 -0
  234. package/src/interface/crud/analytics.ts +11 -0
  235. package/src/interface/crud/connected-accounts.ts +52 -0
  236. package/src/interface/crud/contact-channels.ts +74 -0
  237. package/src/interface/crud/current-user.ts +72 -0
  238. package/src/interface/crud/email-outbox.ts +263 -0
  239. package/src/interface/crud/email-templates.ts +50 -0
  240. package/src/interface/crud/emails.ts +22 -0
  241. package/src/interface/crud/internal-api-keys.ts +69 -0
  242. package/src/interface/crud/invoices.ts +36 -0
  243. package/src/interface/crud/items.ts +24 -0
  244. package/src/interface/crud/notification-preferences.ts +33 -0
  245. package/src/interface/crud/oauth-providers.ts +91 -0
  246. package/src/interface/crud/products.ts +42 -0
  247. package/src/interface/crud/project-api-keys.ts +100 -0
  248. package/src/interface/crud/project-permissions.ts +118 -0
  249. package/src/interface/crud/projects.ts +229 -0
  250. package/src/interface/crud/session-replays.ts +85 -0
  251. package/src/interface/crud/sessions.ts +66 -0
  252. package/src/interface/crud/sign-up-rules.ts +12 -0
  253. package/src/interface/crud/svix-token.ts +20 -0
  254. package/src/interface/crud/team-invitation-details.ts +22 -0
  255. package/src/interface/crud/team-invitation.ts +35 -0
  256. package/src/interface/crud/team-member-profiles.ts +61 -0
  257. package/src/interface/crud/team-memberships.ts +56 -0
  258. package/src/interface/crud/team-permissions.ts +119 -0
  259. package/src/interface/crud/teams.ts +142 -0
  260. package/src/interface/crud/transactions.ts +131 -0
  261. package/src/interface/crud/users.ts +185 -0
  262. package/src/interface/handler-urls.ts +74 -0
  263. package/src/interface/page-component-versions.ts +1702 -0
  264. package/src/interface/server-interface.ts +1146 -0
  265. package/src/interface/webhooks.ts +28 -0
  266. package/src/known-errors.tsx +2031 -0
  267. package/src/local-emulator.ts +5 -0
  268. package/src/payments/payment-methods.ts +167 -0
  269. package/src/payments/stripe-limits.ts +18 -0
  270. package/src/plans.ts +107 -0
  271. package/src/schema-fields.ts +944 -0
  272. package/src/sessions.ts +310 -0
  273. package/src/utils/api-keys.tsx +122 -0
  274. package/src/utils/arrays.tsx +248 -0
  275. package/src/utils/auth-methods.ts +8 -0
  276. package/src/utils/base64.tsx +24 -0
  277. package/src/utils/booleans.tsx +34 -0
  278. package/src/utils/browser-compat.tsx +17 -0
  279. package/src/utils/bytes.tsx +280 -0
  280. package/src/utils/caches.tsx +280 -0
  281. package/src/utils/cel-fields.ts +92 -0
  282. package/src/utils/cloud-hosts.tsx +92 -0
  283. package/src/utils/compile-time.tsx +11 -0
  284. package/src/utils/country-codes.ts +39 -0
  285. package/src/utils/crypto.tsx +174 -0
  286. package/src/utils/currencies.tsx +53 -0
  287. package/src/utils/currency-constants.tsx +51 -0
  288. package/src/utils/dates.tsx +246 -0
  289. package/src/utils/dev-env-state-path.ts +14 -0
  290. package/src/utils/dom.tsx +7 -0
  291. package/src/utils/env.tsx +117 -0
  292. package/src/utils/errors.tsx +284 -0
  293. package/src/utils/esbuild.tsx +235 -0
  294. package/src/utils/featurebase.tsx +247 -0
  295. package/src/utils/fs.tsx +33 -0
  296. package/src/utils/functions.tsx +22 -0
  297. package/src/utils/geo.tsx +16 -0
  298. package/src/utils/globals.tsx +48 -0
  299. package/src/utils/hashes.tsx +52 -0
  300. package/src/utils/html.tsx +60 -0
  301. package/src/utils/http.tsx +91 -0
  302. package/src/utils/ips.tsx +47 -0
  303. package/src/utils/json.tsx +200 -0
  304. package/src/utils/jsx-editable-transpiler.test.ts +156 -0
  305. package/src/utils/jsx-editable-transpiler.tsx +401 -0
  306. package/src/utils/jwt.tsx +170 -0
  307. package/src/utils/locks.tsx +71 -0
  308. package/src/utils/maps.tsx +407 -0
  309. package/src/utils/math.tsx +18 -0
  310. package/src/utils/monkey-patch.tsx +29 -0
  311. package/src/utils/node-http.tsx +47 -0
  312. package/src/utils/numbers.tsx +82 -0
  313. package/src/utils/oauth.tsx +9 -0
  314. package/src/utils/objects.tsx +581 -0
  315. package/src/utils/paginated-lists.test.ts +1039 -0
  316. package/src/utils/paginated-lists.tsx +479 -0
  317. package/src/utils/passkey.tsx +1 -0
  318. package/src/utils/promises.tsx +540 -0
  319. package/src/utils/proxies.tsx +212 -0
  320. package/src/utils/react.tsx +308 -0
  321. package/src/utils/redirect-urls.tsx +299 -0
  322. package/src/utils/regex.tsx +10 -0
  323. package/src/utils/results.tsx +411 -0
  324. package/src/utils/sentry.tsx +20 -0
  325. package/src/utils/stores.tsx +238 -0
  326. package/src/utils/strings.nicify.test.ts +253 -0
  327. package/src/utils/strings.tsx +723 -0
  328. package/src/utils/telemetry.tsx +37 -0
  329. package/src/utils/turnstile-browser.ts +106 -0
  330. package/src/utils/turnstile-flow.test.ts +101 -0
  331. package/src/utils/turnstile-flow.ts +266 -0
  332. package/src/utils/turnstile.ts +34 -0
  333. package/src/utils/typed-arrays.tsx +17 -0
  334. package/src/utils/types.tsx +208 -0
  335. package/src/utils/unicode.tsx +24 -0
  336. package/src/utils/urls.tsx +411 -0
  337. package/src/utils/uuids.tsx +47 -0
  338. package/dist/esm/stack-config-file.d.ts +0 -15
  339. package/dist/esm/stack-config-file.d.ts.map +0 -1
  340. package/dist/esm/stack-config-file.js.map +0 -1
  341. package/dist/stack-config-file.d.ts +0 -15
  342. package/dist/stack-config-file.d.ts.map +0 -1
  343. package/dist/stack-config-file.js.map +0 -1
@@ -0,0 +1,299 @@
1
+ import { HexclaveAssertionError, captureError } from "./errors";
2
+ import { createUrlIfValid, isLocalhost, matchHostnamePattern } from "./urls";
3
+
4
+ type TrustedDomainConfig = {
5
+ allowLocalhost?: boolean,
6
+ trustedDomains: readonly (string | null | undefined)[],
7
+ };
8
+
9
+ const defaultHostedHandlerDomainSuffix = ".built-with-stack-auth.com";
10
+ const hostedHandlerProjectIdPlaceholder = "{projectId}";
11
+ const hostedHandlerPathPlaceholder = "{hostedPath}";
12
+ const defaultPorts = new Map<string, string>([['https:', '443'], ['http:', '80']]);
13
+ const hostedHandlerTemplateProjectIdA = "00000000-0000-4000-8000-000000000000";
14
+ const hostedHandlerTemplateProjectIdB = "11111111-1111-4111-8111-111111111111";
15
+
16
+ function replaceHexclavePortPrefix(input: string | undefined, hexclavePortPrefix: string | undefined): string | undefined {
17
+ if (input == null) return undefined;
18
+ return hexclavePortPrefix ? input.replace(/\$\{NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81\}/g, hexclavePortPrefix) : input;
19
+ }
20
+
21
+ function getHostedHandlerUrlFromTemplate(template: string, projectId: string, hostedPath: string): string {
22
+ return template
23
+ .replaceAll(hostedHandlerProjectIdPlaceholder, projectId)
24
+ .replaceAll(hostedHandlerPathPlaceholder, hostedPath);
25
+ }
26
+
27
+ function assertHostedHandlerTemplateHasProjectOrigin(template: string): void {
28
+ const projectUrlA = new URL(getHostedHandlerUrlFromTemplate(template, hostedHandlerTemplateProjectIdA, "handler"));
29
+ const projectUrlB = new URL(getHostedHandlerUrlFromTemplate(template, hostedHandlerTemplateProjectIdB, "handler"));
30
+ if (projectUrlA.origin === projectUrlB.origin || !projectUrlA.hostname.includes(hostedHandlerTemplateProjectIdA)) {
31
+ throw new HexclaveAssertionError("The hosted handler URL template must put {projectId} in the hostname.", {
32
+ hostedHandlerUrlTemplate: template,
33
+ hint: "Use a project-specific origin like 'https://{projectId}.built-with-stack-auth.com/{hostedPath}', not a shared-origin path like 'https://example.com/{projectId}/{hostedPath}'.",
34
+ });
35
+ }
36
+ }
37
+
38
+ export function getHostedHandlerTrustedDomain(options: {
39
+ projectId: string,
40
+ hostedHandlerDomainSuffix?: string,
41
+ hostedHandlerUrlTemplate?: string,
42
+ hexclavePortPrefix?: string,
43
+ }): string {
44
+ return new URL(getHostedHandlerUrlFromConfig({
45
+ ...options,
46
+ hostedPath: "handler",
47
+ })).origin;
48
+ }
49
+
50
+ export function getHostedHandlerUrlFromConfig(options: {
51
+ projectId: string,
52
+ hostedPath: string,
53
+ hostedHandlerDomainSuffix?: string,
54
+ hostedHandlerUrlTemplate?: string,
55
+ hexclavePortPrefix?: string,
56
+ }): string {
57
+ const configuredTemplate = replaceHexclavePortPrefix(options.hostedHandlerUrlTemplate, options.hexclavePortPrefix);
58
+ return configuredTemplate == null
59
+ ? (() => {
60
+ const domainSuffix = replaceHexclavePortPrefix(options.hostedHandlerDomainSuffix, options.hexclavePortPrefix) ?? defaultHostedHandlerDomainSuffix;
61
+ if (!domainSuffix.startsWith(".")) {
62
+ throw new HexclaveAssertionError("The hosted handler domain suffix must start with a dot.", {
63
+ domainSuffix,
64
+ hint: "Set NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX to a value like '.built-with-stack-auth.com'.",
65
+ });
66
+ }
67
+ return `https://${options.projectId}${domainSuffix}/${options.hostedPath}`;
68
+ })()
69
+ : (() => {
70
+ if (!configuredTemplate.includes(hostedHandlerProjectIdPlaceholder) || !configuredTemplate.includes(hostedHandlerPathPlaceholder)) {
71
+ throw new HexclaveAssertionError("The hosted handler URL template must contain {projectId} and {hostedPath}.", {
72
+ hostedHandlerUrlTemplate: configuredTemplate,
73
+ hint: "Set NEXT_PUBLIC_STACK_HOSTED_HANDLER_URL_TEMPLATE to a value like 'https://{projectId}.built-with-stack-auth.com/{hostedPath}'.",
74
+ });
75
+ }
76
+ assertHostedHandlerTemplateHasProjectOrigin(configuredTemplate);
77
+ return getHostedHandlerUrlFromTemplate(configuredTemplate, options.projectId, options.hostedPath);
78
+ })();
79
+ }
80
+
81
+ export function getImplicitlyTrustedDomainsForProject(options: {
82
+ projectId: string,
83
+ hostedHandlerDomainSuffix?: string,
84
+ hostedHandlerUrlTemplate?: string,
85
+ hexclavePortPrefix?: string,
86
+ }): string[] {
87
+ return [getHostedHandlerTrustedDomain(options)];
88
+ }
89
+
90
+ function normalizePort(url: URL): string {
91
+ const port = url.port || defaultPorts.get(url.protocol) || '';
92
+ return port ? `${url.hostname}:${port}` : url.hostname;
93
+ }
94
+
95
+ function isDefaultPort(url: URL): boolean {
96
+ return !url.port ||
97
+ (url.protocol === 'https:' && url.port === '443') ||
98
+ (url.protocol === 'http:' && url.port === '80');
99
+ }
100
+
101
+ function portsMatch(url1: URL, url2: URL): boolean {
102
+ return normalizePort(url1) === normalizePort(url2);
103
+ }
104
+
105
+ function parseWildcardUrlPattern(pattern: string): { protocol: string, hostPattern: string } | null {
106
+ const protocolSeparatorIndex = pattern.indexOf("://");
107
+ if (protocolSeparatorIndex === -1) return null;
108
+
109
+ const protocol = `${pattern.slice(0, protocolSeparatorIndex)}:`;
110
+ const hostAndPath = pattern.slice(protocolSeparatorIndex + "://".length);
111
+ const pathStartIndex = hostAndPath.indexOf("/");
112
+ const hostPattern = pathStartIndex === -1 ? hostAndPath : hostAndPath.slice(0, pathStartIndex);
113
+ if (hostPattern === "") return null;
114
+ return { protocol, hostPattern };
115
+ }
116
+
117
+ function hostPatternWithoutPort(hostPattern: string): string {
118
+ if (!hostPatternHasExplicitPort(hostPattern)) {
119
+ return hostPattern;
120
+ }
121
+ const portSeparatorIndex = hostPattern.lastIndexOf(":");
122
+ return hostPattern.slice(0, portSeparatorIndex);
123
+ }
124
+
125
+ function hostPatternHasExplicitPort(hostPattern: string): boolean {
126
+ const portSeparatorIndex = hostPattern.lastIndexOf(":");
127
+ if (portSeparatorIndex === -1) {
128
+ return false;
129
+ }
130
+ const port = hostPattern.slice(portSeparatorIndex + 1);
131
+ return port === "*" || (port !== "" && [...port].every(char => char >= "0" && char <= "9"));
132
+ }
133
+
134
+ function matchesTrustedDomain(testUrl: URL, pattern: string): boolean {
135
+ const baseUrl = createUrlIfValid(pattern);
136
+
137
+ if (baseUrl != null && !pattern.includes('*')) {
138
+ return baseUrl.protocol === testUrl.protocol &&
139
+ baseUrl.hostname === testUrl.hostname &&
140
+ portsMatch(baseUrl, testUrl);
141
+ }
142
+
143
+ const parsedPattern = parseWildcardUrlPattern(pattern);
144
+ if (parsedPattern == null) {
145
+ captureError("invalid-redirect-domain", new HexclaveAssertionError("Invalid domain pattern", { pattern }));
146
+ return false;
147
+ }
148
+
149
+ if (testUrl.protocol !== parsedPattern.protocol) {
150
+ return false;
151
+ }
152
+
153
+ const hasPortInPattern = hostPatternHasExplicitPort(parsedPattern.hostPattern);
154
+ return hasPortInPattern
155
+ ? matchHostnamePattern(parsedPattern.hostPattern, normalizePort(testUrl))
156
+ : matchHostnamePattern(parsedPattern.hostPattern, testUrl.hostname) && isDefaultPort(testUrl);
157
+ }
158
+
159
+ export function isAcceptedNativeAppUrl(urlOrString: string): boolean {
160
+ const url = createUrlIfValid(urlOrString);
161
+ if (!url) return false;
162
+
163
+ // Legacy scheme accepted indefinitely; baked into already-shipped Swift SDK binaries.
164
+ return url.protocol === 'stack-auth-mobile-oauth-url:'
165
+ || url.protocol === 'hexclave-mobile-oauth-url:';
166
+ }
167
+
168
+ export function validateRedirectUrl(
169
+ urlOrString: string | URL,
170
+ config: TrustedDomainConfig,
171
+ ): boolean {
172
+ const url = createUrlIfValid(urlOrString);
173
+ if (!url) return false;
174
+
175
+ if (config.allowLocalhost === true && isLocalhost(url)) {
176
+ return true;
177
+ }
178
+
179
+ return config.trustedDomains.some(domain => domain != null && matchesTrustedDomain(url, domain));
180
+ }
181
+
182
+ export function getTrustedParentDomain(currentDomain: string, trustedDomains: readonly (string | null | undefined)[]): string | null {
183
+ const hostPatterns = trustedDomains
184
+ .filter((domain): domain is string => domain != null)
185
+ .map((domain) => {
186
+ const url = createUrlIfValid(domain);
187
+ if (url != null && !domain.includes("*")) {
188
+ return url.hostname.toLowerCase();
189
+ }
190
+ const parsedPattern = parseWildcardUrlPattern(domain);
191
+ return parsedPattern == null ? null : hostPatternWithoutPort(parsedPattern.hostPattern).toLowerCase();
192
+ })
193
+ .filter((domain): domain is string => domain != null);
194
+
195
+ const parts = currentDomain.toLowerCase().split('.');
196
+ for (let i = parts.length - 2; i >= 0; i--) {
197
+ const parentDomain = parts.slice(i).join('.');
198
+ if (hostPatterns.includes(parentDomain) && hostPatterns.includes(`**.${parentDomain}`)) {
199
+ return parentDomain;
200
+ }
201
+ }
202
+
203
+ return null;
204
+ }
205
+
206
+ import.meta.vitest?.test("validateRedirectUrl matches exact and wildcard trusted domains", ({ expect }) => {
207
+ expect(validateRedirectUrl("https://example.com", {
208
+ allowLocalhost: false,
209
+ trustedDomains: ["https://example.com"],
210
+ })).toBe(true);
211
+ expect(validateRedirectUrl("https://api.example.com", {
212
+ allowLocalhost: false,
213
+ trustedDomains: ["https://*.example.com"],
214
+ })).toBe(true);
215
+ expect(validateRedirectUrl("https://api.v2.example.com", {
216
+ allowLocalhost: false,
217
+ trustedDomains: ["https://*.example.com"],
218
+ })).toBe(false);
219
+ });
220
+
221
+ import.meta.vitest?.test("validateRedirectUrl trusts implicit hosted handler domains", ({ expect }) => {
222
+ const projectId = "12345678-1234-4234-8234-123456789abc";
223
+ expect(validateRedirectUrl(`https://${projectId}.built-with-stack-auth.com/anything`, {
224
+ allowLocalhost: false,
225
+ trustedDomains: getImplicitlyTrustedDomainsForProject({ projectId }),
226
+ })).toBe(true);
227
+ expect(validateRedirectUrl("https://other-project.built-with-stack-auth.com/anything", {
228
+ allowLocalhost: false,
229
+ trustedDomains: getImplicitlyTrustedDomainsForProject({ projectId }),
230
+ })).toBe(false);
231
+ expect(validateRedirectUrl(`http://${projectId}.localhost:9209/anything`, {
232
+ allowLocalhost: false,
233
+ trustedDomains: getImplicitlyTrustedDomainsForProject({
234
+ projectId,
235
+ hostedHandlerUrlTemplate: "http://{projectId}.localhost:${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}09/{hostedPath}",
236
+ hexclavePortPrefix: "92",
237
+ }),
238
+ })).toBe(true);
239
+ });
240
+
241
+ import.meta.vitest?.test("getImplicitlyTrustedDomainsForProject rejects shared-origin hosted templates", ({ expect }) => {
242
+ expect(() => getImplicitlyTrustedDomainsForProject({
243
+ projectId: "12345678-1234-4234-8234-123456789abc",
244
+ hostedHandlerUrlTemplate: "https://host.example.com/{projectId}/{hostedPath}",
245
+ })).toThrowErrorMatchingInlineSnapshot(`
246
+ [HexclaveAssertionError: The hosted handler URL template must put {projectId} in the hostname.
247
+
248
+ This is likely an error in Hexclave. Please make sure you are running the newest version and report it.]
249
+ `);
250
+ });
251
+
252
+ import.meta.vitest?.test("validateRedirectUrl respects default and explicit ports", ({ expect }) => {
253
+ expect(validateRedirectUrl("https://example.com:443/path", {
254
+ allowLocalhost: false,
255
+ trustedDomains: ["https://example.com"],
256
+ })).toBe(true);
257
+ expect(validateRedirectUrl("http://api.example.com:3000", {
258
+ allowLocalhost: false,
259
+ trustedDomains: ["http://*.example.com:3000"],
260
+ })).toBe(true);
261
+ expect(validateRedirectUrl("http://api.example.com", {
262
+ allowLocalhost: false,
263
+ trustedDomains: ["http://*.example.com:3000"],
264
+ })).toBe(false);
265
+ expect(validateRedirectUrl("http://api.example.com:1234", {
266
+ allowLocalhost: false,
267
+ trustedDomains: ["http://*.example.com:*"],
268
+ })).toBe(true);
269
+ });
270
+
271
+ import.meta.vitest?.test("validateRedirectUrl respects localhost allowance and invalid patterns", ({ expect }) => {
272
+ const originalConsoleError = console.error;
273
+ console.error = () => {};
274
+ try {
275
+ expect(validateRedirectUrl("http://localhost:3000", {
276
+ allowLocalhost: true,
277
+ trustedDomains: [],
278
+ })).toBe(true);
279
+ expect(validateRedirectUrl("http://localhost:3000", {
280
+ allowLocalhost: false,
281
+ trustedDomains: [],
282
+ })).toBe(false);
283
+ expect(validateRedirectUrl("https://example.com", {
284
+ allowLocalhost: false,
285
+ trustedDomains: ["not a url"],
286
+ })).toBe(false);
287
+ } finally {
288
+ console.error = originalConsoleError;
289
+ }
290
+ });
291
+
292
+ import.meta.vitest?.test("getTrustedParentDomain ignores empty entries and strips ports", ({ expect }) => {
293
+ expect(getTrustedParentDomain("app.example.com", [
294
+ null,
295
+ undefined,
296
+ "https://example.com",
297
+ "https://**.example.com:*",
298
+ ])).toBe("example.com");
299
+ });
@@ -0,0 +1,10 @@
1
+ const cachedRegexes = new Map<string, RegExp>();
2
+
3
+ export function createCachedRegex(pattern: string) {
4
+ const cached = cachedRegexes.get(pattern);
5
+ if (cached) return cached;
6
+
7
+ const regex = new RegExp(pattern);
8
+ cachedRegexes.set(pattern, regex);
9
+ return regex;
10
+ }