@hexclave/shared 1.0.3 → 1.0.6

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 (335) 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 +1 -1
  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 +1 -1
  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/interface/admin-metrics.d.ts +2 -2
  62. package/dist/esm/interface/admin-metrics.js.map +1 -1
  63. package/dist/esm/interface/client-interface.js +2 -2
  64. package/dist/esm/interface/client-interface.js.map +1 -1
  65. package/dist/esm/interface/conversations.d.ts +32 -32
  66. package/dist/esm/interface/crud/current-user.d.ts +7 -7
  67. package/dist/esm/interface/crud/email-outbox.d.ts +42 -42
  68. package/dist/esm/interface/crud/project-permissions.js +5 -5
  69. package/dist/esm/interface/crud/project-permissions.js.map +1 -1
  70. package/dist/esm/interface/crud/team-member-profiles.d.ts +18 -18
  71. package/dist/esm/interface/crud/team-permissions.js +5 -5
  72. package/dist/esm/interface/crud/team-permissions.js.map +1 -1
  73. package/dist/esm/interface/crud/users.d.ts +10 -10
  74. package/dist/esm/interface/page-component-versions.js +107 -107
  75. package/dist/esm/interface/page-component-versions.js.map +1 -1
  76. package/dist/esm/interface/webhooks.d.ts +2 -2
  77. package/dist/esm/known-errors.d.ts +1 -1
  78. package/dist/esm/known-errors.d.ts.map +1 -1
  79. package/dist/esm/known-errors.js +9 -5
  80. package/dist/esm/known-errors.js.map +1 -1
  81. package/dist/esm/schema-fields.d.ts +15 -5
  82. package/dist/esm/schema-fields.d.ts.map +1 -1
  83. package/dist/esm/schema-fields.js +26 -16
  84. package/dist/esm/schema-fields.js.map +1 -1
  85. package/dist/esm/utils/crypto.d.ts.map +1 -1
  86. package/dist/esm/utils/crypto.js.map +1 -1
  87. package/dist/esm/utils/currency-constants.d.ts +6 -0
  88. package/dist/esm/utils/currency-constants.d.ts.map +1 -1
  89. package/dist/esm/utils/currency-constants.js.map +1 -1
  90. package/dist/esm/utils/dev-env-state-path.d.ts +3 -3
  91. package/dist/esm/utils/dev-env-state-path.d.ts.map +1 -1
  92. package/dist/esm/utils/dev-env-state-path.js +4 -4
  93. package/dist/esm/utils/dev-env-state-path.js.map +1 -1
  94. package/dist/esm/utils/errors.js +2 -2
  95. package/dist/esm/utils/errors.js.map +1 -1
  96. package/dist/esm/utils/esbuild.d.ts.map +1 -1
  97. package/dist/esm/utils/esbuild.js +0 -1
  98. package/dist/esm/utils/esbuild.js.map +1 -1
  99. package/dist/esm/utils/featurebase.d.ts +3 -3
  100. package/dist/esm/utils/featurebase.d.ts.map +1 -1
  101. package/dist/esm/utils/featurebase.js +15 -15
  102. package/dist/esm/utils/featurebase.js.map +1 -1
  103. package/dist/esm/utils/fs.js +6 -6
  104. package/dist/esm/utils/fs.js.map +1 -1
  105. package/dist/esm/utils/globals.d.ts.map +1 -1
  106. package/dist/esm/utils/globals.js +10 -10
  107. package/dist/esm/utils/globals.js.map +1 -1
  108. package/dist/esm/utils/jwt.d.ts.map +1 -1
  109. package/dist/esm/utils/jwt.js +3 -3
  110. package/dist/esm/utils/jwt.js.map +1 -1
  111. package/dist/esm/utils/redirect-urls.d.ts +3 -3
  112. package/dist/esm/utils/redirect-urls.d.ts.map +1 -1
  113. package/dist/esm/utils/redirect-urls.js +5 -5
  114. package/dist/esm/utils/redirect-urls.js.map +1 -1
  115. package/dist/esm/utils/urls.js.map +1 -1
  116. package/dist/helpers/vault/client-side.d.ts.map +1 -1
  117. package/dist/helpers/vault/client-side.js.map +1 -1
  118. package/dist/helpers/vault/server-side.d.ts.map +1 -1
  119. package/dist/helpers/vault/server-side.js.map +1 -1
  120. package/dist/hexclave-config-file.d.ts +15 -0
  121. package/dist/hexclave-config-file.d.ts.map +1 -0
  122. package/dist/{stack-config-file.js → hexclave-config-file.js} +8 -8
  123. package/dist/hexclave-config-file.js.map +1 -0
  124. package/dist/interface/admin-metrics.d.ts +12 -12
  125. package/dist/interface/admin-metrics.js.map +1 -1
  126. package/dist/interface/client-interface.js +2 -2
  127. package/dist/interface/client-interface.js.map +1 -1
  128. package/dist/interface/conversations.d.ts +32 -32
  129. package/dist/interface/crud/current-user.d.ts +7 -7
  130. package/dist/interface/crud/email-outbox.d.ts +42 -42
  131. package/dist/interface/crud/project-permissions.js +5 -5
  132. package/dist/interface/crud/project-permissions.js.map +1 -1
  133. package/dist/interface/crud/team-member-profiles.d.ts +18 -18
  134. package/dist/interface/crud/team-permissions.js +5 -5
  135. package/dist/interface/crud/team-permissions.js.map +1 -1
  136. package/dist/interface/crud/users.d.ts +10 -10
  137. package/dist/interface/page-component-versions.js +107 -107
  138. package/dist/interface/page-component-versions.js.map +1 -1
  139. package/dist/interface/webhooks.d.ts +2 -2
  140. package/dist/known-errors.d.ts +1 -1
  141. package/dist/known-errors.d.ts.map +1 -1
  142. package/dist/known-errors.js +9 -5
  143. package/dist/known-errors.js.map +1 -1
  144. package/dist/schema-fields.d.ts +15 -5
  145. package/dist/schema-fields.d.ts.map +1 -1
  146. package/dist/schema-fields.js +26 -16
  147. package/dist/schema-fields.js.map +1 -1
  148. package/dist/utils/crypto.d.ts.map +1 -1
  149. package/dist/utils/crypto.js.map +1 -1
  150. package/dist/utils/currency-constants.d.ts +6 -0
  151. package/dist/utils/currency-constants.d.ts.map +1 -1
  152. package/dist/utils/currency-constants.js.map +1 -1
  153. package/dist/utils/dev-env-state-path.d.ts +3 -3
  154. package/dist/utils/dev-env-state-path.d.ts.map +1 -1
  155. package/dist/utils/dev-env-state-path.js +5 -5
  156. package/dist/utils/dev-env-state-path.js.map +1 -1
  157. package/dist/utils/errors.js +2 -2
  158. package/dist/utils/errors.js.map +1 -1
  159. package/dist/utils/esbuild.d.ts.map +1 -1
  160. package/dist/utils/esbuild.js +0 -1
  161. package/dist/utils/esbuild.js.map +1 -1
  162. package/dist/utils/featurebase.d.ts +3 -3
  163. package/dist/utils/featurebase.d.ts.map +1 -1
  164. package/dist/utils/featurebase.js +15 -15
  165. package/dist/utils/featurebase.js.map +1 -1
  166. package/dist/utils/fs.js.map +1 -1
  167. package/dist/utils/globals.d.ts.map +1 -1
  168. package/dist/utils/globals.js +10 -10
  169. package/dist/utils/globals.js.map +1 -1
  170. package/dist/utils/jwt.d.ts.map +1 -1
  171. package/dist/utils/jwt.js +3 -3
  172. package/dist/utils/jwt.js.map +1 -1
  173. package/dist/utils/redirect-urls.d.ts +3 -3
  174. package/dist/utils/redirect-urls.d.ts.map +1 -1
  175. package/dist/utils/redirect-urls.js +5 -5
  176. package/dist/utils/redirect-urls.js.map +1 -1
  177. package/dist/utils/urls.js.map +1 -1
  178. package/package.json +2 -1
  179. package/src/ai/image-limits.ts +62 -0
  180. package/src/ai/llms/llms.ts +36 -0
  181. package/src/ai/unified-prompts/reminders.ts +27 -0
  182. package/src/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.ts +1047 -0
  183. package/src/ai/unified-prompts/skill-site-prompt-parts/brain.ts +5 -0
  184. package/src/ai/unified-prompts/skill-site-prompt-parts/cli-help.ts +5 -0
  185. package/src/ai/unified-prompts/skill-site-prompt-parts/config-docs.ts +5 -0
  186. package/src/ai/unified-prompts/skill-site-prompt-parts/custom-components-instructions.ts +5 -0
  187. package/src/ai/unified-prompts/skill-site-prompt-parts/dashboard-instructions.ts +5 -0
  188. package/src/ai/unified-prompts/skill-site-prompt-parts/docs-index.ts +80 -0
  189. package/src/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.ts +322 -0
  190. package/src/ai/unified-prompts/skill-site-prompt-parts/openapi-specs.ts +5 -0
  191. package/src/ai/unified-prompts/skill-site-prompt-parts/sdk-interface-source.ts +5 -0
  192. package/src/ai/unified-prompts/skill-site-prompt.ts +72 -0
  193. package/src/apps/apps-config.ts +177 -0
  194. package/src/apps/apps-ui.tsx +112 -0
  195. package/src/config/README.md +154 -0
  196. package/src/config/db-sync-mappings.ts +2293 -0
  197. package/src/config/format.ts +388 -0
  198. package/src/config/migrate-catalogs-to-product-lines.ts +475 -0
  199. package/src/config/schema-fuzzer.test.ts +377 -0
  200. package/src/config/schema.ts +1507 -0
  201. package/src/config-authoring.test.ts +52 -0
  202. package/src/config-authoring.ts +37 -0
  203. package/src/config-rendering.ts +142 -0
  204. package/src/crud.tsx +275 -0
  205. package/src/global.d.ts +1 -0
  206. package/src/helpers/emails.ts +250 -0
  207. package/src/helpers/init-prompt.ts +39 -0
  208. package/src/helpers/password.ts +16 -0
  209. package/src/helpers/production-mode.ts +58 -0
  210. package/src/helpers/support-sla.test.ts +124 -0
  211. package/src/helpers/support-sla.ts +93 -0
  212. package/src/helpers/vault/client-side.ts +92 -0
  213. package/src/helpers/vault/server-side.ts +153 -0
  214. package/src/hexclave-config-file.ts +120 -0
  215. package/src/hooks/use-async-callback.tsx +43 -0
  216. package/src/hooks/use-async-external-store.tsx +24 -0
  217. package/src/hooks/use-hash.tsx +14 -0
  218. package/src/hooks/use-hover.tsx +88 -0
  219. package/src/hooks/use-strict-memo.tsx +157 -0
  220. package/src/index.ts +17 -0
  221. package/src/interface/admin-interface.ts +1043 -0
  222. package/src/interface/admin-metrics.ts +214 -0
  223. package/src/interface/client-interface.test.ts +765 -0
  224. package/src/interface/client-interface.ts +2586 -0
  225. package/src/interface/conversations.ts +109 -0
  226. package/src/interface/crud/analytics.ts +11 -0
  227. package/src/interface/crud/connected-accounts.ts +52 -0
  228. package/src/interface/crud/contact-channels.ts +74 -0
  229. package/src/interface/crud/current-user.ts +72 -0
  230. package/src/interface/crud/email-outbox.ts +263 -0
  231. package/src/interface/crud/email-templates.ts +50 -0
  232. package/src/interface/crud/emails.ts +22 -0
  233. package/src/interface/crud/internal-api-keys.ts +69 -0
  234. package/src/interface/crud/invoices.ts +36 -0
  235. package/src/interface/crud/items.ts +24 -0
  236. package/src/interface/crud/notification-preferences.ts +33 -0
  237. package/src/interface/crud/oauth-providers.ts +91 -0
  238. package/src/interface/crud/products.ts +42 -0
  239. package/src/interface/crud/project-api-keys.ts +100 -0
  240. package/src/interface/crud/project-permissions.ts +118 -0
  241. package/src/interface/crud/projects.ts +229 -0
  242. package/src/interface/crud/session-replays.ts +85 -0
  243. package/src/interface/crud/sessions.ts +66 -0
  244. package/src/interface/crud/sign-up-rules.ts +12 -0
  245. package/src/interface/crud/svix-token.ts +20 -0
  246. package/src/interface/crud/team-invitation-details.ts +22 -0
  247. package/src/interface/crud/team-invitation.ts +35 -0
  248. package/src/interface/crud/team-member-profiles.ts +61 -0
  249. package/src/interface/crud/team-memberships.ts +56 -0
  250. package/src/interface/crud/team-permissions.ts +119 -0
  251. package/src/interface/crud/teams.ts +142 -0
  252. package/src/interface/crud/transactions.ts +131 -0
  253. package/src/interface/crud/users.ts +185 -0
  254. package/src/interface/handler-urls.ts +74 -0
  255. package/src/interface/page-component-versions.ts +1702 -0
  256. package/src/interface/server-interface.ts +1146 -0
  257. package/src/interface/webhooks.ts +28 -0
  258. package/src/known-errors.tsx +2034 -0
  259. package/src/local-emulator.ts +5 -0
  260. package/src/payments/payment-methods.ts +167 -0
  261. package/src/payments/stripe-limits.ts +18 -0
  262. package/src/plans.ts +107 -0
  263. package/src/schema-fields.ts +944 -0
  264. package/src/sessions.ts +310 -0
  265. package/src/utils/api-keys.tsx +122 -0
  266. package/src/utils/arrays.tsx +248 -0
  267. package/src/utils/auth-methods.ts +8 -0
  268. package/src/utils/base64.tsx +24 -0
  269. package/src/utils/booleans.tsx +34 -0
  270. package/src/utils/browser-compat.tsx +17 -0
  271. package/src/utils/bytes.tsx +280 -0
  272. package/src/utils/caches.tsx +280 -0
  273. package/src/utils/cel-fields.ts +92 -0
  274. package/src/utils/cloud-hosts.tsx +92 -0
  275. package/src/utils/compile-time.tsx +11 -0
  276. package/src/utils/country-codes.ts +39 -0
  277. package/src/utils/crypto.tsx +174 -0
  278. package/src/utils/currencies.tsx +53 -0
  279. package/src/utils/currency-constants.tsx +51 -0
  280. package/src/utils/dates.tsx +246 -0
  281. package/src/utils/dev-env-state-path.ts +14 -0
  282. package/src/utils/dom.tsx +7 -0
  283. package/src/utils/env.tsx +117 -0
  284. package/src/utils/errors.tsx +284 -0
  285. package/src/utils/esbuild.tsx +235 -0
  286. package/src/utils/featurebase.tsx +247 -0
  287. package/src/utils/fs.tsx +33 -0
  288. package/src/utils/functions.tsx +22 -0
  289. package/src/utils/geo.tsx +16 -0
  290. package/src/utils/globals.tsx +48 -0
  291. package/src/utils/hashes.tsx +52 -0
  292. package/src/utils/html.tsx +60 -0
  293. package/src/utils/http.tsx +91 -0
  294. package/src/utils/ips.tsx +47 -0
  295. package/src/utils/json.tsx +200 -0
  296. package/src/utils/jsx-editable-transpiler.test.ts +156 -0
  297. package/src/utils/jsx-editable-transpiler.tsx +401 -0
  298. package/src/utils/jwt.tsx +170 -0
  299. package/src/utils/locks.tsx +71 -0
  300. package/src/utils/maps.tsx +407 -0
  301. package/src/utils/math.tsx +18 -0
  302. package/src/utils/monkey-patch.tsx +29 -0
  303. package/src/utils/node-http.tsx +47 -0
  304. package/src/utils/numbers.tsx +82 -0
  305. package/src/utils/oauth.tsx +9 -0
  306. package/src/utils/objects.tsx +581 -0
  307. package/src/utils/paginated-lists.test.ts +1039 -0
  308. package/src/utils/paginated-lists.tsx +479 -0
  309. package/src/utils/passkey.tsx +1 -0
  310. package/src/utils/promises.tsx +540 -0
  311. package/src/utils/proxies.tsx +212 -0
  312. package/src/utils/react.tsx +308 -0
  313. package/src/utils/redirect-urls.tsx +299 -0
  314. package/src/utils/regex.tsx +10 -0
  315. package/src/utils/results.tsx +411 -0
  316. package/src/utils/sentry.tsx +20 -0
  317. package/src/utils/stores.tsx +238 -0
  318. package/src/utils/strings.nicify.test.ts +253 -0
  319. package/src/utils/strings.tsx +723 -0
  320. package/src/utils/telemetry.tsx +37 -0
  321. package/src/utils/turnstile-browser.ts +106 -0
  322. package/src/utils/turnstile-flow.test.ts +101 -0
  323. package/src/utils/turnstile-flow.ts +266 -0
  324. package/src/utils/turnstile.ts +34 -0
  325. package/src/utils/typed-arrays.tsx +17 -0
  326. package/src/utils/types.tsx +208 -0
  327. package/src/utils/unicode.tsx +24 -0
  328. package/src/utils/urls.tsx +411 -0
  329. package/src/utils/uuids.tsx +47 -0
  330. package/dist/esm/stack-config-file.d.ts +0 -15
  331. package/dist/esm/stack-config-file.d.ts.map +0 -1
  332. package/dist/esm/stack-config-file.js.map +0 -1
  333. package/dist/stack-config-file.d.ts +0 -15
  334. package/dist/stack-config-file.d.ts.map +0 -1
  335. package/dist/stack-config-file.js.map +0 -1
@@ -0,0 +1,208 @@
1
+ import { DeepPartial } from "./objects";
2
+ import { Join } from "./strings";
3
+
4
+ export type IsAny<T> = 0 extends (1 & T) ? true : false;
5
+ export type IsNever<T> = [T] extends [never] ? true : false;
6
+ export type IsNullish<T> = [T] extends [null | undefined] ? true : false;
7
+ export type IsUnion<T, U = T> =
8
+ IsNever<T> extends true ? false
9
+ : IsAny<T> extends true ? false
10
+ : T extends U // distributive conditional https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types
11
+ ? /* if the *whole* original type (`U`) still fits inside the current variant, then `T` wasn’t a union */ ([U] extends [T] ? false : true)
12
+ : never;
13
+
14
+ export type NullishCoalesce<T, U> = T extends null | undefined ? U : T;
15
+
16
+ export type LastUnionElement<U> = UnionToIntersection<U extends any ? (x: U) => 0 : never> extends (x: infer L) => 0 ? L & U : never;
17
+
18
+ type primitive = string | number | boolean | bigint | symbol | null | undefined;
19
+
20
+ /**
21
+ * Makes a type prettier by recursively expanding all object types. For example, `Omit<{ a: 1 }, "a">` becomes just `{}`.
22
+ */
23
+ export type Expand<T> = T extends (...args: infer A) => infer R
24
+ ? ((...args: A) => R) extends T
25
+ ? (...args: Expand<A>) => Expand<R>
26
+ : ((...args: Expand<A>) => Expand<R>) & { [K in keyof T]: Expand<T[K]> }
27
+ : T extends object
28
+ ? T extends primitive
29
+ ? T
30
+ : T extends infer O
31
+ ? { [K in keyof O]: Expand<O[K]> }
32
+ : never
33
+ : T;
34
+
35
+
36
+ /**
37
+ * Removes all optional undefined/never keys from an object.
38
+ */
39
+ export type DeepRemoveOptionalUndefined<T> = T extends object ? { [K in keyof T]: DeepRemoveOptionalUndefined<T[K]> } : T;
40
+
41
+ // why this works: https://stackoverflow.com/a/50375286
42
+ export type UnionToIntersection<U> =
43
+ (U extends any ? (x: U) => void : never) extends ((x: infer I) => void) ? I : never
44
+
45
+ type _UnionToTupleInner<U, R extends any[], Last> = UnionToTuple<Exclude<U, Last>, [...R, Last]>
46
+ export type UnionToTuple<U, R extends any[] = []> = [U] extends [never] ? R : _UnionToTupleInner<U, R, LastUnionElement<U>>;
47
+
48
+ export type CollapseObjectUnion<T extends object> = {
49
+ [K in AllUnionKeys<T>]?: T extends Record<K, infer V> ? V : never;
50
+ };
51
+ typeAssertIs<CollapseObjectUnion<{ a: string } | { b: number }>, { a?: string, b?: number }>()();
52
+ typeAssertIs<CollapseObjectUnion<{ a: string } | { a: number }>, { a?: string | number }>()();
53
+
54
+ export type IntersectAll<T extends any[]> = UnionToIntersection<T[number]>;
55
+
56
+ export type OptionalKeys<T> = {
57
+ [K in keyof T]: {} extends Pick<T, K> ? K : never;
58
+ }[keyof T];
59
+ export type RequiredKeys<T> = {
60
+ [K in keyof T]: {} extends Pick<T, K> ? never : K;
61
+ }[keyof T];
62
+
63
+ /**
64
+ * Returns a type whose keys are the intersection of the keys of T and U, deeply.
65
+ */
66
+ export type KeyIntersect<T, U> =
67
+ & { [K in keyof T & keyof U]?: T[K] & U[K] }
68
+ & { [K in RequiredKeys<T> & keyof U]: T[K] & U[K] }
69
+ & { [K in RequiredKeys<U> & keyof T]: U[K] & T[K] }
70
+
71
+ /**
72
+ * Returns ALL keys of all union elements.
73
+ */
74
+ export type AllUnionKeys<T extends object> = T extends T ? keyof T : never;
75
+ typeAssertIs<AllUnionKeys<{ a: string } | { b: number }>, "a" | "b">()();
76
+
77
+ export type SubtractType<T, U> = T extends object ? { [K in keyof T]: K extends keyof U ? SubtractType<T[K], U[K]> : T[K] } : (T extends U ? never : T); // note: this only works due to the distributive property of conditional types https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types
78
+
79
+ export type XOR<T extends readonly any[]> = T extends readonly [infer A, infer B, ...infer Rest]
80
+ ? Rest extends []
81
+ ? (A & { [K in keyof B]?: never }) | (B & { [K in keyof A]?: never })
82
+ : XOR<[(A & { [K in keyof B]?: never }) | (B & { [K in keyof A]?: never }), ...Rest]>
83
+ : T[0];
84
+
85
+
86
+ type _AntiIntersectInner<T, U> = T extends object ? (
87
+ & Omit<U, keyof T>
88
+ & { [K in keyof Pick<U, { [K in keyof T & keyof U]: U[K] extends T[K] ? (T[K] extends U[K] ? never : K) : never }[keyof T & keyof U]>]: PseudoAntiIntersect<T[K], U[K]> }
89
+ & { [K in keyof Pick<U, keyof T & keyof U>]?: PseudoAntiIntersect<T[K], U[K]> }
90
+ ) : U;
91
+ /**
92
+ * Returns a type R such that T & R = U.
93
+ */
94
+ export type AntiIntersect<T, U> = U extends T ? _AntiIntersectInner<T, U> : "Cannot anti-intersect a type with a type that is not a subtype of it"; // NOTE: This type is mostly untested — not sure how well it works on the edge cases
95
+ export type PseudoAntiIntersect<T, U> = _AntiIntersectInner<T, T & U>;
96
+
97
+ /**
98
+ * A variation of TypeScript's conditionals with slightly different semantics. It is the perfect type for cases where:
99
+ *
100
+ * - If all possible values are contained in `Extends`, then it will be mapped to `Then`.
101
+ * - If all possible values are not contained in `Extends`, then it will be mapped to `Otherwise`.
102
+ * - If some possible values are contained in `Extends` and some are not, then it will be mapped to `Then | Otherwise`.
103
+ *
104
+ * This is different from TypeScript's built-in conditional types (`Value extends Extends ? Then : Otherwise`), which
105
+ * returns `Otherwise` for the third case (causing unsoundness in many real-world cases).
106
+ */
107
+ export type IfAndOnlyIf<Value, Extends, Then, Otherwise> =
108
+ | (Value extends Extends ? never : Otherwise)
109
+ | (Value & Extends extends never ? never : Then);
110
+
111
+
112
+ /**
113
+ * Can be used to prettify a type in the IDE; for example, some complicated intersected types can be flattened into a single type.
114
+ */
115
+ export type PrettifyType<T> = T extends object ? { [K in keyof T]: T[K] } & {} : T;
116
+
117
+ type _ToStringAndJoin<T extends any[], Separator extends string> =
118
+ T extends [infer U, ...infer Rest extends any[]]
119
+ ? `${TypeToString<U>}${Rest extends [any, ...any[]] ? `${Separator}${_ToStringAndJoin<Rest, Separator>}` : ""}`
120
+ : "<error-joining-tuple-elements>";
121
+ type _TypeToStringInner<T> =
122
+ IsAny<T> extends true ? "any"
123
+ : IsNever<T> extends true ? "never"
124
+ : IsUnion<T> extends true ? _ToStringAndJoin<UnionToTuple<T>, " | ">
125
+ : [T] extends [number] ? (number extends T ? "number" : `${T}`)
126
+ : [T] extends [boolean] ? `${T}`
127
+ : [T] extends [undefined] ? "undefined"
128
+ : [T] extends [null] ? "null"
129
+ : [T] extends [string] ? (string extends T ? "string" : `'${T}'`)
130
+ : [T] extends [[]] ? "[]"
131
+ : [T] extends [[any, ...any[]]] ? `[${_ToStringAndJoin<T, ", ">}]`
132
+ : [T] extends [(infer E)[]] ? `${TypeToString<E>}[]`
133
+ : [T] extends [Function] ? "function"
134
+ : [T] extends [symbol] ? `symbol(${T['description']})`
135
+ : [T] extends [object] ? `{ ${Join<UnionToTuple<{ [K in keyof T]: `${TypeToString<K>}: ${TypeToString<T[K]>}` }[keyof T]>, ", ">} }`
136
+ : "<unknown-type>"
137
+ export type TypeToString<T> = _TypeToStringInner<T> extends `${infer S}` ? S : never;
138
+
139
+ /**
140
+ * Can be used to create assertions on types. For example, if passed any T other than `true`, the following will
141
+ * show a type error:
142
+ *
143
+ * ```ts
144
+ * typeAssert<T>()(); // the second pair of braces is important!
145
+ * ```
146
+ */
147
+ export function typeAssert<T>(): (
148
+ IsAny<T> extends true ? TypeAssertionError<`Type assertion failed. Expected true, but got any.`>
149
+ : IsNever<T> extends true ? TypeAssertionError<`Type assertion failed. Expected true, but got never.`>
150
+ : T extends true ? (() => undefined)
151
+ : TypeAssertionError<`Type assertion failed. Expected true, but got: ${TypeToString<T>}`>
152
+ ) {
153
+ return (() => undefined) as any;
154
+ }
155
+ type TypeAssertionError<T> =
156
+ & [T]
157
+ & /* this promise makes sure that if we accidentally forget the second pair of braces, eslint will complain (if we have no-floating-promises enabled) */ Promise<any>;
158
+
159
+
160
+ typeAssertExtends<ReturnType<typeof typeAssert<true>>, () => undefined>()();
161
+ typeAssertExtends<ReturnType<typeof typeAssert<false>>, TypeAssertionError<`Type assertion failed. Expected true, but got: false`>>()();
162
+ typeAssertExtends<ReturnType<typeof typeAssert<never>>, TypeAssertionError<`Type assertion failed. Expected true, but got never.`>>()();
163
+ typeAssertExtends<ReturnType<typeof typeAssert<any>>, TypeAssertionError<`Type assertion failed. Expected true, but got any.`>>()();
164
+
165
+ /**
166
+ * Functionally equivalent to `typeAssert<T extends S ? true : false>()()`, but with better error messages.
167
+ */
168
+ export function typeAssertExtends<T, S>(): (
169
+ [T] extends [S] ? (() => undefined) : TypeAssertionError<`Type assertion failed. Expected ${TypeToString<T>} to extend ${TypeToString<S>}`>
170
+ ) {
171
+ return (() => undefined) as any;
172
+ }
173
+
174
+ typeAssertExtends<ReturnType<typeof typeAssertExtends<never, true>>, () => undefined>()();
175
+ typeAssertExtends<ReturnType<typeof typeAssertExtends<any, true>>, () => undefined>()();
176
+ typeAssertExtends<ReturnType<typeof typeAssertExtends<false, false>>, () => undefined>()();
177
+ typeAssertExtends<ReturnType<typeof typeAssertExtends<"abc", string>>, () => undefined>()();
178
+ typeAssertExtends<ReturnType<typeof typeAssertExtends<{a: 1, b: 123}, {a: number}>>, () => undefined>()();
179
+ typeAssertExtends<ReturnType<typeof typeAssertExtends<never, never>>, () => undefined>()();
180
+ typeAssertExtends<ReturnType<typeof typeAssertExtends<true, any>>, () => undefined>()();
181
+
182
+ typeAssertExtends<ReturnType<typeof typeAssertExtends<{a: number}, {a: 1}>>, ["Type assertion failed. Expected { 'a': number } to extend { 'a': 1 }"]>()();
183
+ typeAssertExtends<ReturnType<typeof typeAssertExtends<any, never>>, ["Type assertion failed. Expected any to extend never"]>()();
184
+ typeAssertExtends<ReturnType<typeof typeAssertExtends<false, true>>, ["Type assertion failed. Expected false to extend true"]>()();
185
+ typeAssertExtends<ReturnType<typeof typeAssertExtends<false, never>>, ["Type assertion failed. Expected false to extend never"]>()();
186
+
187
+
188
+ export function typeAssertIs<T, U>(): (
189
+ IsAny<T> extends true ? (IsAny<U> extends true ? (() => undefined) : TypeAssertionError<`Type assertion failed. Expected ${TypeToString<T>} to be ${TypeToString<U>}`>)
190
+ : IsAny<U> extends true ? TypeAssertionError<`Type assertion failed. Expected ${TypeToString<T>} to be ${TypeToString<U>}`>
191
+ : [T] extends [U] ? ([U] extends [T] ? (() => undefined) : TypeAssertionError<`Type assertion failed. Expected ${TypeToString<T>} to be ${TypeToString<U>}`>)
192
+ : TypeAssertionError<`Type assertion failed. Expected ${TypeToString<T>} to be ${TypeToString<U>}`>
193
+ ) {
194
+ return (() => undefined) as any;
195
+ }
196
+
197
+ typeAssertExtends<ReturnType<typeof typeAssertIs<"123", "123">>, () => undefined>()();
198
+ typeAssertExtends<ReturnType<typeof typeAssertIs<{a: 1}, {a: 1}>>, () => undefined>()();
199
+ typeAssertExtends<ReturnType<typeof typeAssertIs<DeepPartial<{a: 1}>, {a?: 1}>>, () => undefined>()();
200
+ typeAssertExtends<ReturnType<typeof typeAssertIs<any, any>>, () => undefined>()();
201
+ typeAssertExtends<ReturnType<typeof typeAssertIs<never, never>>, () => undefined>()();
202
+ typeAssertExtends<ReturnType<typeof typeAssertIs<1, any>>, ["Type assertion failed. Expected 1 to be any"]>()();
203
+ typeAssertExtends<ReturnType<typeof typeAssertIs<any, 1>>, ["Type assertion failed. Expected any to be 1"]>()();
204
+ typeAssertExtends<ReturnType<typeof typeAssertIs<false, true>>, ["Type assertion failed. Expected false to be true"]>()();
205
+ typeAssertExtends<ReturnType<typeof typeAssertIs<{a: number}, {a: 1}>>, ["Type assertion failed. Expected { 'a': number } to be { 'a': 1 }"]>()();
206
+ typeAssertExtends<ReturnType<typeof typeAssertIs<any, never>>, ["Type assertion failed. Expected any to be never"]>()();
207
+ typeAssertExtends<ReturnType<typeof typeAssertIs<false, true>>, ["Type assertion failed. Expected false to be true"]>()();
208
+ typeAssertExtends<ReturnType<typeof typeAssertIs<false, never>>, ["Type assertion failed. Expected false to be never"]>()();
@@ -0,0 +1,24 @@
1
+ import { isValidCountryCode, normalizeCountryCode } from "./country-codes";
2
+ import { HexclaveAssertionError } from "./errors";
3
+
4
+ export function getFlagEmoji(twoLetterCountryCode: string) {
5
+ if (!isValidCountryCode(twoLetterCountryCode)) throw new HexclaveAssertionError("Country code must be two alphabetical letters");
6
+ const codePoints = normalizeCountryCode(twoLetterCountryCode)
7
+ .split('')
8
+ .map(char => 127397 + char.charCodeAt(0));
9
+ return String.fromCodePoint(...codePoints);
10
+ }
11
+ import.meta.vitest?.test("getFlagEmoji", ({ expect }) => {
12
+ // Test with valid country codes
13
+ expect(getFlagEmoji("US")).toBe("🇺🇸");
14
+ expect(getFlagEmoji("us")).toBe("🇺🇸");
15
+ expect(getFlagEmoji("GB")).toBe("🇬🇧");
16
+ expect(getFlagEmoji("JP")).toBe("🇯🇵");
17
+
18
+ // Test with invalid country codes
19
+ expect(() => getFlagEmoji("")).toThrow("Country code must be two alphabetical letters");
20
+ expect(() => getFlagEmoji("A")).toThrow("Country code must be two alphabetical letters");
21
+ expect(() => getFlagEmoji("ABC")).toThrow("Country code must be two alphabetical letters");
22
+ expect(() => getFlagEmoji("12")).toThrow("Country code must be two alphabetical letters");
23
+ expect(() => getFlagEmoji("A1")).toThrow("Country code must be two alphabetical letters");
24
+ });
@@ -0,0 +1,411 @@
1
+ import { generateSecureRandomString } from "./crypto";
2
+ import { templateIdentity } from "./strings";
3
+
4
+ export function createUrlIfValid(...args: ConstructorParameters<typeof URL>) {
5
+ try {
6
+ return new URL(...args);
7
+ } catch (e) {
8
+ return null;
9
+ }
10
+ }
11
+ import.meta.vitest?.test("createUrlIfValid", ({ expect }) => {
12
+ // Test with valid URLs
13
+ expect(createUrlIfValid("https://example.com")).toBeInstanceOf(URL);
14
+ expect(createUrlIfValid("https://example.com/path?query=value#hash")).toBeInstanceOf(URL);
15
+ expect(createUrlIfValid("/path", "https://example.com")).toBeInstanceOf(URL);
16
+
17
+ // Test with invalid URLs
18
+ expect(createUrlIfValid("")).toBeNull();
19
+ expect(createUrlIfValid("not a url")).toBeNull();
20
+ expect(createUrlIfValid("http://")).toBeNull();
21
+ });
22
+
23
+ export function isValidUrl(url: string) {
24
+ return !!createUrlIfValid(url);
25
+ }
26
+ import.meta.vitest?.test("isValidUrl", ({ expect }) => {
27
+ // Test with valid URLs
28
+ expect(isValidUrl("https://example.com")).toBe(true);
29
+ expect(isValidUrl("http://localhost:3000")).toBe(true);
30
+ expect(isValidUrl("ftp://example.com")).toBe(true);
31
+
32
+ // Test with invalid URLs
33
+ expect(isValidUrl("")).toBe(false);
34
+ expect(isValidUrl("not a url")).toBe(false);
35
+ expect(isValidUrl("http://")).toBe(false);
36
+ });
37
+
38
+ export function isValidHostname(hostname: string) {
39
+ // Basic validation
40
+ if (!hostname || hostname.startsWith('.') || hostname.endsWith('.') || hostname.includes('..')) {
41
+ return false;
42
+ }
43
+
44
+ const url = createUrlIfValid(`https://${hostname}`);
45
+ if (!url) return false;
46
+ return url.hostname === hostname;
47
+ }
48
+ import.meta.vitest?.test("isValidHostname", ({ expect }) => {
49
+ // Test with valid hostnames
50
+ expect(isValidHostname("example.com")).toBe(true);
51
+ expect(isValidHostname("localhost")).toBe(true);
52
+ expect(isValidHostname("sub.domain.example.com")).toBe(true);
53
+ expect(isValidHostname("127.0.0.1")).toBe(true);
54
+
55
+ // Test with invalid hostnames
56
+ expect(isValidHostname("")).toBe(false);
57
+ expect(isValidHostname("example.com/path")).toBe(false);
58
+ expect(isValidHostname("https://example.com")).toBe(false);
59
+ expect(isValidHostname("example com")).toBe(false);
60
+ });
61
+
62
+ export function isValidHostnameWithWildcards(hostname: string) {
63
+ // Empty hostnames are invalid
64
+ if (!hostname) return false;
65
+
66
+ // Check if it contains wildcards
67
+ const hasWildcard = hostname.includes('*');
68
+
69
+ if (!hasWildcard) {
70
+ // If no wildcards, validate as a normal hostname
71
+ return isValidHostname(hostname);
72
+ }
73
+
74
+ // Basic validation checks that apply even with wildcards
75
+ // - Hostname cannot start or end with a dot
76
+ if (hostname.startsWith('.') || hostname.endsWith('.')) {
77
+ return false;
78
+ }
79
+
80
+ // - No consecutive dots
81
+ if (hostname.includes('..')) {
82
+ return false;
83
+ }
84
+
85
+ // For wildcard validation, check that non-wildcard parts contain valid characters
86
+ // Replace wildcards with a valid placeholder to check the rest
87
+ const testHostname = hostname.replace(/\*+/g, 'wildcard');
88
+
89
+ // Check if the resulting string would be a valid hostname
90
+ if (!/^[a-zA-Z0-9.-]+$/.test(testHostname)) {
91
+ return false;
92
+ }
93
+
94
+ // Additional check: ensure the pattern makes sense
95
+ // Check each segment between wildcards
96
+ const segments = hostname.split(/\*+/);
97
+ for (let i = 0; i < segments.length; i++) {
98
+ const segment = segments[i];
99
+ if (segment === '') continue; // Empty segments are OK (consecutive wildcards)
100
+
101
+ // First segment can't start with dot
102
+ if (i === 0 && segment.startsWith('.')) {
103
+ return false;
104
+ }
105
+
106
+ // Last segment can't end with dot
107
+ if (i === segments.length - 1 && segment.endsWith('.')) {
108
+ return false;
109
+ }
110
+
111
+ // No segment should have consecutive dots
112
+ if (segment.includes('..')) {
113
+ return false;
114
+ }
115
+ }
116
+
117
+ return true;
118
+ }
119
+ import.meta.vitest?.test("isValidHostnameWithWildcards", ({ expect }) => {
120
+ // Test with valid regular hostnames
121
+ expect(isValidHostnameWithWildcards("example.com")).toBe(true);
122
+ expect(isValidHostnameWithWildcards("localhost")).toBe(true);
123
+ expect(isValidHostnameWithWildcards("sub.domain.example.com")).toBe(true);
124
+
125
+ // Test with valid wildcard hostnames
126
+ expect(isValidHostnameWithWildcards("*.example.com")).toBe(true);
127
+ expect(isValidHostnameWithWildcards("a-*.example.com")).toBe(true);
128
+ expect(isValidHostnameWithWildcards("*.*.org")).toBe(true);
129
+ expect(isValidHostnameWithWildcards("**.example.com")).toBe(true);
130
+ expect(isValidHostnameWithWildcards("sub.**.com")).toBe(true);
131
+ expect(isValidHostnameWithWildcards("*-api.*.com")).toBe(true);
132
+
133
+ // Test with invalid hostnames
134
+ expect(isValidHostnameWithWildcards("")).toBe(false);
135
+ expect(isValidHostnameWithWildcards("example.com/path")).toBe(false);
136
+ expect(isValidHostnameWithWildcards("https://example.com")).toBe(false);
137
+ expect(isValidHostnameWithWildcards("example com")).toBe(false);
138
+ expect(isValidHostnameWithWildcards(".example.com")).toBe(false);
139
+ expect(isValidHostnameWithWildcards("example.com.")).toBe(false);
140
+ expect(isValidHostnameWithWildcards("example..com")).toBe(false);
141
+ expect(isValidHostnameWithWildcards("*.example..com")).toBe(false);
142
+ });
143
+
144
+ export function matchHostnamePattern(pattern: string, hostname: string): boolean {
145
+ // If no wildcards, it's an exact match
146
+ if (!pattern.includes('*')) {
147
+ return pattern === hostname;
148
+ }
149
+
150
+ // Convert the pattern to a regex
151
+ // First, escape all regex special characters except *
152
+ let regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
153
+
154
+ // Use a placeholder for ** to handle it separately from single *
155
+ const doubleWildcardPlaceholder = '\x00DOUBLE_WILDCARD\x00';
156
+ regexPattern = regexPattern.replace(/\*\*/g, doubleWildcardPlaceholder);
157
+
158
+ // Replace single * with a pattern that matches anything except dots
159
+ regexPattern = regexPattern.replace(/\*/g, '[^.]*');
160
+
161
+ // Replace the double wildcard placeholder with a pattern that matches anything including dots
162
+ regexPattern = regexPattern.replace(new RegExp(doubleWildcardPlaceholder, 'g'), '.*');
163
+
164
+ // Anchor the pattern to match the entire hostname
165
+ regexPattern = '^' + regexPattern + '$';
166
+
167
+ try {
168
+ const regex = new RegExp(regexPattern);
169
+ return regex.test(hostname);
170
+ } catch {
171
+ return false;
172
+ }
173
+ }
174
+ import.meta.vitest?.test("matchHostnamePattern", ({ expect }) => {
175
+ // Test exact matches
176
+ expect(matchHostnamePattern("example.com", "example.com")).toBe(true);
177
+ expect(matchHostnamePattern("example.com", "other.com")).toBe(false);
178
+
179
+ // Test single wildcard matches
180
+ expect(matchHostnamePattern("*.example.com", "api.example.com")).toBe(true);
181
+ expect(matchHostnamePattern("*.example.com", "www.example.com")).toBe(true);
182
+ expect(matchHostnamePattern("*.example.com", "example.com")).toBe(false);
183
+ expect(matchHostnamePattern("*.example.com", "api.v2.example.com")).toBe(false);
184
+
185
+ // Test double wildcard matches
186
+ expect(matchHostnamePattern("**.example.com", "api.example.com")).toBe(true);
187
+ expect(matchHostnamePattern("**.example.com", "api.v2.example.com")).toBe(true);
188
+ expect(matchHostnamePattern("**.example.com", "a.b.c.example.com")).toBe(true);
189
+ expect(matchHostnamePattern("**.example.com", "example.com")).toBe(false);
190
+
191
+ // Test complex patterns
192
+ expect(matchHostnamePattern("api-*.example.com", "api-v1.example.com")).toBe(true);
193
+ expect(matchHostnamePattern("api-*.example.com", "api-v2.example.com")).toBe(true);
194
+ expect(matchHostnamePattern("api-*.example.com", "api.example.com")).toBe(false);
195
+ expect(matchHostnamePattern("*.*.org", "mail.example.org")).toBe(true);
196
+ expect(matchHostnamePattern("*.*.org", "example.org")).toBe(false);
197
+ });
198
+
199
+ export function getHardcodedFallbackUrls(primaryBaseUrl: string): string[] {
200
+ // `defaultBaseUrl` in `@stackframe/*` SDK builds is `https://api.stack-auth.com`
201
+ // (see `packages/template/src/lib/hexclave-app/apps/implementations/common.ts`).
202
+ // The `@hexclave/*` mirror packages will get this rewritten to the hexclave
203
+ // hostname by `scripts/rewrite-packages-to-hexclave.ts` in a follow-up PR; both
204
+ // branches below stay listed so either build resolves its own fallback set.
205
+ if (primaryBaseUrl === "https://api.stack-auth.com") {
206
+ return ["https://api1.stack-auth.com", "https://api2.stack-auth.com"];
207
+ }
208
+ if (primaryBaseUrl === "https://api.dev.stack-auth.com") {
209
+ return ["https://api1.dev.stack-auth.com", "https://api2.dev.stack-auth.com"];
210
+ }
211
+ if (primaryBaseUrl === "https://api.hexclave.com") {
212
+ return ["https://api1.hexclave.com", "https://api2.hexclave.com"];
213
+ }
214
+ if (primaryBaseUrl === "https://api.dev.hexclave.com") {
215
+ return ["https://api1.dev.hexclave.com", "https://api2.dev.hexclave.com"];
216
+ }
217
+ const localhostMatch = primaryBaseUrl.match(/^http:\/\/localhost:(\d+)02$/);
218
+ if (localhostMatch) {
219
+ return [`http://localhost:${localhostMatch[1]}10`];
220
+ }
221
+ return [];
222
+ }
223
+
224
+ export function getDefaultApiUrls(primaryBaseUrl: string): string[] {
225
+ return [primaryBaseUrl, ...getHardcodedFallbackUrls(primaryBaseUrl)];
226
+ }
227
+
228
+ export function isLocalhost(urlOrString: string | URL) {
229
+ const url = createUrlIfValid(urlOrString);
230
+ if (!url) return false;
231
+ if (url.hostname === "localhost" || url.hostname.endsWith(".localhost")) return true;
232
+ if (url.hostname.match(/^127\.\d+\.\d+\.\d+$/)) return true;
233
+ if (url.hostname === "[::1]" || url.hostname === "::1") return true;
234
+ return false;
235
+ }
236
+ import.meta.vitest?.test("isLocalhost", ({ expect }) => {
237
+ // Test with localhost URLs
238
+ expect(isLocalhost("http://localhost")).toBe(true);
239
+ expect(isLocalhost("https://localhost:8080")).toBe(true);
240
+ expect(isLocalhost("http://sub.localhost")).toBe(true);
241
+ expect(isLocalhost("http://127.0.0.1")).toBe(true);
242
+ expect(isLocalhost("http://127.1.2.3")).toBe(true);
243
+ expect(isLocalhost("http://[::1]")).toBe(true);
244
+
245
+ // Test with non-localhost URLs
246
+ expect(isLocalhost("https://example.com")).toBe(false);
247
+ expect(isLocalhost("http://192.168.1.1")).toBe(false);
248
+ expect(isLocalhost("http://10.0.0.1")).toBe(false);
249
+
250
+ // Test with URL objects
251
+ expect(isLocalhost(new URL("http://localhost"))).toBe(true);
252
+ expect(isLocalhost(new URL("https://example.com"))).toBe(false);
253
+
254
+ // Test with invalid URLs
255
+ expect(isLocalhost("not a url")).toBe(false);
256
+ expect(isLocalhost("")).toBe(false);
257
+ });
258
+
259
+ export function isRelative(url: string) {
260
+ const randomDomain = `${generateSecureRandomString()}.stack-auth.example.com`;
261
+ const u = createUrlIfValid(url, `https://${randomDomain}`);
262
+ if (!u) return false;
263
+ if (u.host !== randomDomain) return false;
264
+ if (u.protocol !== "https:") return false;
265
+ return true;
266
+ }
267
+ import.meta.vitest?.test("isRelative", ({ expect }) => {
268
+ // We can't easily mock generateSecureRandomString in this context
269
+ // but we can still test the function's behavior
270
+
271
+ // Test with relative URLs
272
+ expect(isRelative("/")).toBe(true);
273
+ expect(isRelative("/path")).toBe(true);
274
+ expect(isRelative("/path?query=value#hash")).toBe(true);
275
+
276
+ // Test with absolute URLs
277
+ expect(isRelative("https://example.com")).toBe(false);
278
+ expect(isRelative("http://example.com")).toBe(false);
279
+ expect(isRelative("//example.com")).toBe(false);
280
+
281
+ // Note: The implementation treats empty strings and invalid URLs as relative
282
+ // This is because they can be resolved against a base URL
283
+ expect(isRelative("")).toBe(true);
284
+ expect(isRelative("not a url")).toBe(true);
285
+ });
286
+
287
+ export function getRelativePart(url: URL) {
288
+ return url.pathname + url.search + url.hash;
289
+ }
290
+ import.meta.vitest?.test("getRelativePart", ({ expect }) => {
291
+ // Test with various URLs
292
+ expect(getRelativePart(new URL("https://example.com"))).toBe("/");
293
+ expect(getRelativePart(new URL("https://example.com/path"))).toBe("/path");
294
+ expect(getRelativePart(new URL("https://example.com/path?query=value"))).toBe("/path?query=value");
295
+ expect(getRelativePart(new URL("https://example.com/path#hash"))).toBe("/path#hash");
296
+ expect(getRelativePart(new URL("https://example.com/path?query=value#hash"))).toBe("/path?query=value#hash");
297
+
298
+ // Test with different domains but same paths
299
+ const url1 = new URL("https://example.com/path?query=value#hash");
300
+ const url2 = new URL("https://different.com/path?query=value#hash");
301
+ expect(getRelativePart(url1)).toBe(getRelativePart(url2));
302
+ });
303
+
304
+ /**
305
+ * A template literal tag that returns a URL.
306
+ *
307
+ * Any values passed are encoded.
308
+ */
309
+ export function url(strings: TemplateStringsArray | readonly string[], ...values: (string | number | boolean)[]): URL {
310
+ return new URL(urlString(strings, ...values));
311
+ }
312
+ import.meta.vitest?.test("url", ({ expect }) => {
313
+ // Test with no interpolation
314
+ expect(url`https://example.com`).toBeInstanceOf(URL);
315
+ expect(url`https://example.com`.href).toBe("https://example.com/");
316
+
317
+ // Test with string interpolation
318
+ expect(url`https://example.com/${"path"}`).toBeInstanceOf(URL);
319
+ expect(url`https://example.com/${"path"}`.pathname).toBe("/path");
320
+
321
+ // Test with number interpolation
322
+ expect(url`https://example.com/${42}`).toBeInstanceOf(URL);
323
+ expect(url`https://example.com/${42}`.pathname).toBe("/42");
324
+
325
+ // Test with boolean interpolation
326
+ expect(url`https://example.com/${true}`).toBeInstanceOf(URL);
327
+ expect(url`https://example.com/${true}`.pathname).toBe("/true");
328
+
329
+ // Test with special characters in interpolation
330
+ expect(url`https://example.com/${"path with spaces"}`).toBeInstanceOf(URL);
331
+ expect(url`https://example.com/${"path with spaces"}`.pathname).toBe("/path%20with%20spaces");
332
+
333
+ // Test with multiple interpolations
334
+ expect(url`https://example.com/${"path"}?query=${"value"}`).toBeInstanceOf(URL);
335
+ expect(url`https://example.com/${"path"}?query=${"value"}`.pathname).toBe("/path");
336
+ expect(url`https://example.com/${"path"}?query=${"value"}`.search).toBe("?query=value");
337
+ });
338
+
339
+
340
+ /**
341
+ * A template literal tag that returns a URL string.
342
+ *
343
+ * Any values passed are encoded.
344
+ */
345
+ export function urlString(strings: TemplateStringsArray | readonly string[], ...values: (string | number | boolean)[]): string {
346
+ return templateIdentity(strings, ...values.map(encodeURIComponent));
347
+ }
348
+ import.meta.vitest?.test("urlString", ({ expect }) => {
349
+ // Test with no interpolation
350
+ expect(urlString`https://example.com`).toBe("https://example.com");
351
+
352
+ // Test with string interpolation
353
+ expect(urlString`https://example.com/${"path"}`).toBe("https://example.com/path");
354
+
355
+ // Test with number interpolation
356
+ expect(urlString`https://example.com/${42}`).toBe("https://example.com/42");
357
+
358
+ // Test with boolean interpolation
359
+ expect(urlString`https://example.com/${true}`).toBe("https://example.com/true");
360
+
361
+ // Test with special characters in interpolation
362
+ expect(urlString`https://example.com/${"path with spaces"}`).toBe("https://example.com/path%20with%20spaces");
363
+ expect(urlString`https://example.com/${"?&="}`).toBe("https://example.com/%3F%26%3D");
364
+
365
+ // Test with multiple interpolations
366
+ expect(urlString`https://example.com/${"path"}?query=${"value"}`).toBe("https://example.com/path?query=value");
367
+ expect(urlString`https://example.com/${"path"}?query=${"value with spaces"}`).toBe("https://example.com/path?query=value%20with%20spaces");
368
+ });
369
+
370
+ export function isChildUrl(parentUrl: URL, maybeChildUrl: URL) {
371
+ return parentUrl.origin === maybeChildUrl.origin
372
+ && isChildPath(parentUrl.pathname, maybeChildUrl.pathname)
373
+ && [...parentUrl.searchParams].every(([key, value]) => maybeChildUrl.searchParams.get(key) === value)
374
+ && (!parentUrl.hash || parentUrl.hash === maybeChildUrl.hash);
375
+ }
376
+ import.meta.vitest?.test("isChildUrl", ({ expect }) => {
377
+ // true
378
+ expect(isChildUrl(new URL("https://abc.com/"), new URL("https://abc.com/"))).toBe(true);
379
+ expect(isChildUrl(new URL("https://abc.com/"), new URL("https://abc.com/path"))).toBe(true);
380
+ expect(isChildUrl(new URL("https://abc.com/"), new URL("https://abc.com/path?query=value"))).toBe(true);
381
+ expect(isChildUrl(new URL("https://abc.com/"), new URL("https://abc.com/path?query=value#hash"))).toBe(true);
382
+
383
+ // false
384
+ expect(isChildUrl(new URL("https://abc.com"), new URL("https://example.com"))).toBe(false);
385
+ expect(isChildUrl(new URL("https://abc.com/"), new URL("https://example.com/path"))).toBe(false);
386
+ expect(isChildUrl(new URL("https://abc.com/"), new URL("https://example.com/path?query=value"))).toBe(false);
387
+ expect(isChildUrl(new URL("https://abc.com/"), new URL("https://example.com/path?query=value#hash"))).toBe(false);
388
+ expect(isChildUrl(new URL("https://example.com"), new URL("https://abc.com/path?query=value#hash"))).toBe(false);
389
+ expect(isChildUrl(new URL("https://example.com?query=value123"), new URL("https://example.com/path?query=value#hash"))).toBe(false);
390
+ });
391
+
392
+ export function isChildPath(parentPath: string, maybeChildPath: string) {
393
+ parentPath = parentPath.endsWith("/") ? parentPath : parentPath + "/";
394
+ maybeChildPath = maybeChildPath.endsWith("/") ? maybeChildPath : maybeChildPath + "/";
395
+ return maybeChildPath.startsWith(parentPath);
396
+ }
397
+ import.meta.vitest?.test("isSubPath", ({ expect }) => {
398
+ expect(isChildPath("/", "/")).toBe(true);
399
+ expect(isChildPath("/", "/path")).toBe(true);
400
+ expect(isChildPath("/path", "/")).toBe(false);
401
+ expect(isChildPath("/path", "/path")).toBe(true);
402
+ expect(isChildPath("/path/", "/path")).toBe(true);
403
+ expect(isChildPath("/path", "/path/")).toBe(true);
404
+ expect(isChildPath("/path/", "/path/")).toBe(true);
405
+ expect(isChildPath("/path", "/path/abc")).toBe(true);
406
+ expect(isChildPath("/path/", "/path/abc")).toBe(true);
407
+ expect(isChildPath("/path", "/path-abc")).toBe(false);
408
+ expect(isChildPath("/path", "/path-abc/")).toBe(false);
409
+ expect(isChildPath("/path/", "/path-abc")).toBe(false);
410
+ expect(isChildPath("/path/", "/path-abc/")).toBe(false);
411
+ });