@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,212 @@
1
+ import { throwErr } from "./errors";
2
+ import { nicify } from "./strings";
3
+
4
+ /**
5
+ * Creates a proxy that throws an error when any property is accessed, function is called,
6
+ * or any other operation is performed on it.
7
+ *
8
+ * Useful for placeholder values that should never actually be used at runtime.
9
+ */
10
+ export function throwingProxy<T = any>(error: string | Error): T {
11
+ const doThrow = (): never => {
12
+ if (typeof error === "string") {
13
+ throwErr(error);
14
+ } else {
15
+ throwErr(error);
16
+ }
17
+ };
18
+
19
+ return new Proxy(() => {}, {
20
+ get: doThrow,
21
+ set: doThrow,
22
+ has: doThrow,
23
+ deleteProperty: doThrow,
24
+ ownKeys: doThrow,
25
+ getOwnPropertyDescriptor: doThrow,
26
+ defineProperty: doThrow,
27
+ getPrototypeOf: doThrow,
28
+ setPrototypeOf: doThrow,
29
+ isExtensible: doThrow,
30
+ preventExtensions: doThrow,
31
+ apply: doThrow,
32
+ construct: doThrow,
33
+ }) as T;
34
+ }
35
+
36
+ export function logged<T extends object>(name: string, toLog: T, options: {} = {}): T {
37
+ const proxy = new Proxy(toLog, {
38
+ get(target, prop, receiver) {
39
+ const orig = Reflect.get(target, prop, receiver);
40
+ if (typeof orig === "function") {
41
+ return function (this: any, ...args: any[]) {
42
+ const success = (v: any, isPromise: boolean) => console.debug(`logged(...): Called ${name}.${String(prop)}(${args.map(a => nicify(a)).join(", ")}) => ${isPromise ? "Promise<" : ""}${nicify(result)}${isPromise ? ">" : ""}`, { this: this, args, promise: isPromise ? result : false, result: v, trace: new Error() });
43
+ const error = (e: any, isPromise: boolean) => console.debug(`logged(...): Error in ${name}.${String(prop)}(${args.map(a => nicify(a)).join(", ")})`, { this: this, args, promise: isPromise ? result : false, error: e, trace: new Error() });
44
+
45
+ let result: unknown;
46
+ try {
47
+ result = orig.apply(this, args);
48
+ } catch (e) {
49
+ error(e, false);
50
+ throw e;
51
+ }
52
+ if (result instanceof Promise) {
53
+ result.then((v) => success(v, true)).catch((e) => error(e, true));
54
+ } else {
55
+ success(result, false);
56
+ }
57
+ return result;
58
+ };
59
+ }
60
+ return orig;
61
+ },
62
+ set(target, prop, value) {
63
+ console.log(`Setting ${name}.${String(prop)} to ${value}`);
64
+ return Reflect.set(target, prop, value);
65
+ },
66
+ apply(target, thisArg, args) {
67
+ console.log(`Calling ${name}(${JSON.stringify(args).slice(1, -1)})`);
68
+ return Reflect.apply(target as any, thisArg, args);
69
+ },
70
+ construct(target, args, newTarget) {
71
+ console.log(`Constructing ${name}(${JSON.stringify(args).slice(1, -1)})`);
72
+ return Reflect.construct(target as any, args, newTarget);
73
+ },
74
+ defineProperty(target, prop, descriptor) {
75
+ console.log(`Defining ${name}.${String(prop)} as ${JSON.stringify(descriptor)}`);
76
+ return Reflect.defineProperty(target, prop, descriptor);
77
+ },
78
+ deleteProperty(target, prop) {
79
+ console.log(`Deleting ${name}.${String(prop)}`);
80
+ return Reflect.deleteProperty(target, prop);
81
+ },
82
+ setPrototypeOf(target, prototype) {
83
+ console.log(`Setting prototype of ${name} to ${prototype}`);
84
+ return Reflect.setPrototypeOf(target, prototype);
85
+ },
86
+ preventExtensions(target) {
87
+ console.log(`Preventing extensions of ${name}`);
88
+ return Reflect.preventExtensions(target);
89
+ },
90
+ });
91
+ return proxy;
92
+ }
93
+
94
+ export function createLazyProxy<FactoryResult>(factory: () => FactoryResult): FactoryResult {
95
+ let cache: FactoryResult | undefined = undefined;
96
+ let initialized: boolean = false;
97
+
98
+ function initializeIfNeeded() {
99
+ if (!initialized) {
100
+ cache = factory();
101
+ initialized = true;
102
+ }
103
+ return cache!;
104
+ }
105
+
106
+ return new Proxy({}, {
107
+ get(target, prop, receiver) {
108
+ const instance = initializeIfNeeded();
109
+ return Reflect.get(instance, prop, receiver);
110
+ },
111
+ set(target, prop, value, receiver) {
112
+ const instance = initializeIfNeeded();
113
+ return Reflect.set(instance, prop, value, receiver);
114
+ },
115
+ has(target, prop) {
116
+ const instance = initializeIfNeeded();
117
+ return Reflect.has(instance, prop);
118
+ },
119
+ deleteProperty(target, prop) {
120
+ const instance = initializeIfNeeded();
121
+ return Reflect.deleteProperty(instance, prop);
122
+ },
123
+ ownKeys(target) {
124
+ const instance = initializeIfNeeded();
125
+ return Reflect.ownKeys(instance);
126
+ },
127
+ getOwnPropertyDescriptor(target, prop) {
128
+ const instance = initializeIfNeeded();
129
+ return Reflect.getOwnPropertyDescriptor(instance, prop);
130
+ },
131
+ defineProperty(target, prop, descriptor) {
132
+ const instance = initializeIfNeeded();
133
+ return Reflect.defineProperty(instance, prop, descriptor);
134
+ },
135
+ getPrototypeOf(target) {
136
+ const instance = initializeIfNeeded();
137
+ return Reflect.getPrototypeOf(instance);
138
+ },
139
+ setPrototypeOf(target, proto) {
140
+ const instance = initializeIfNeeded();
141
+ return Reflect.setPrototypeOf(instance, proto);
142
+ },
143
+ isExtensible(target) {
144
+ const instance = initializeIfNeeded();
145
+ return Reflect.isExtensible(instance);
146
+ },
147
+ preventExtensions(target) {
148
+ const instance = initializeIfNeeded();
149
+ return Reflect.preventExtensions(instance);
150
+ },
151
+ apply(target, thisArg, argumentsList) {
152
+ const instance = initializeIfNeeded();
153
+ return Reflect.apply(instance as any, thisArg, argumentsList);
154
+ },
155
+ construct(target, argumentsList, newTarget) {
156
+ const instance = initializeIfNeeded();
157
+ return Reflect.construct(instance as any, argumentsList, newTarget);
158
+ }
159
+ }) as FactoryResult;
160
+ }
161
+ import.meta.vitest?.test("createLazyProxy", ({ expect }) => {
162
+ // Test with a simple object factory
163
+ let factoryCallCount = 0;
164
+ const createObject = () => {
165
+ factoryCallCount++;
166
+ return { value: 42, method: () => "hello" };
167
+ };
168
+
169
+ const proxy = createLazyProxy(createObject);
170
+
171
+ // Factory should not be called until property is accessed
172
+ expect(factoryCallCount).toBe(0);
173
+
174
+ // Accessing a property should initialize the object
175
+ expect(proxy.value).toBe(42);
176
+ expect(factoryCallCount).toBe(1);
177
+
178
+ // Accessing another property should not call factory again
179
+ expect(proxy.method()).toBe("hello");
180
+ expect(factoryCallCount).toBe(1);
181
+
182
+ // Test with property setting
183
+ proxy.value = 100;
184
+ expect(proxy.value).toBe(100);
185
+ expect(factoryCallCount).toBe(1);
186
+
187
+ // Test with a class factory
188
+ let classFactoryCallCount = 0;
189
+ class TestClass {
190
+ constructor() {
191
+ classFactoryCallCount++;
192
+ }
193
+
194
+ getValue() {
195
+ return "class value";
196
+ }
197
+ }
198
+
199
+ const classFactory = () => new TestClass();
200
+ const classProxy = createLazyProxy(classFactory);
201
+
202
+ // Factory should not be called until method is accessed
203
+ expect(classFactoryCallCount).toBe(0);
204
+
205
+ // Accessing a method should initialize the object
206
+ expect(classProxy.getValue()).toBe("class value");
207
+ expect(classFactoryCallCount).toBe(1);
208
+
209
+ // Accessing the method again should not call factory again
210
+ expect(classProxy.getValue()).toBe("class value");
211
+ expect(classFactoryCallCount).toBe(1);
212
+ });
@@ -0,0 +1,308 @@
1
+ import React, { SetStateAction } from "react";
2
+ import { isBrowserLike } from "./env";
3
+ import { neverResolve, runAsynchronously } from "./promises";
4
+ import { AsyncResult } from "./results";
5
+ import { ensureMonkeyPatch, NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL } from "./monkey-patch";
6
+ import { deindent } from "./strings";
7
+
8
+ export function componentWrapper<
9
+ C extends React.ComponentType<any> | keyof React.JSX.IntrinsicElements,
10
+ ExtraProps extends {} = {}
11
+ >(displayName: string, render: React.ForwardRefRenderFunction<RefFromComponent<C>, React.ComponentPropsWithRef<C> & ExtraProps>) {
12
+ const Component = forwardRefIfNeeded(render);
13
+ Component.displayName = displayName;
14
+ return Component;
15
+ }
16
+ type RefFromComponent<C extends React.ComponentType<any> | keyof React.JSX.IntrinsicElements> = NonNullable<RefFromComponentDistCond<React.ComponentPropsWithRef<C>["ref"]>>;
17
+ type RefFromComponentDistCond<A> = A extends React.RefObject<infer T> ? T : never; // distributive conditional type; see https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types
18
+
19
+ const react18PromiseCache = new WeakMap<Promise<unknown>, AsyncResult<unknown, unknown>>();
20
+ export function use<T>(promise: Promise<T>): T {
21
+ if ("use" in React) {
22
+ return React.use(promise);
23
+ } else {
24
+ if (react18PromiseCache.has(promise)) {
25
+ const result = react18PromiseCache.get(promise)!;
26
+ if (result.status === "pending") {
27
+ throw promise;
28
+ } else if (result.status === "ok") {
29
+ return result.data as T;
30
+ } else {
31
+ throw result.error;
32
+ }
33
+ } else {
34
+ react18PromiseCache.set(promise, { "status": "pending", progress: undefined });
35
+ runAsynchronously(async () => {
36
+ try {
37
+ const res = await promise;
38
+ react18PromiseCache.set(promise, { "status": "ok", data: res });
39
+ } catch (e) {
40
+ react18PromiseCache.set(promise, { "status": "error", error: e });
41
+ }
42
+ });
43
+ throw promise;
44
+ }
45
+ }
46
+ }
47
+
48
+ export function forwardRefIfNeeded<T, P = {}>(render: React.ForwardRefRenderFunction<T, P>): React.FC<P & { ref?: React.Ref<T> }> {
49
+ // TODO: when we drop support for react 18, remove this
50
+
51
+ const version = React.version;
52
+ const major = parseInt(version.split(".")[0]);
53
+ if (major < 19) {
54
+ return React.forwardRef<T, P>(render as any) as any;
55
+ } else {
56
+ return ((props: P) => render(props, (props as any).ref)) as any;
57
+ }
58
+ }
59
+
60
+ export function getNodeText(node: React.ReactNode): string {
61
+ if (["number", "string"].includes(typeof node)) {
62
+ return `${node}`;
63
+ }
64
+ if (!node) {
65
+ return "";
66
+ }
67
+ if (Array.isArray(node)) {
68
+ return node.map(getNodeText).join("");
69
+ }
70
+ if (React.isValidElement<{ children?: React.ReactNode }>(node)) {
71
+ return getNodeText(node.props.children);
72
+ }
73
+ throw new Error(`Unknown node type: ${typeof node}`);
74
+ }
75
+ import.meta.vitest?.test("getNodeText", ({ expect }) => {
76
+ // Test with string
77
+ expect(getNodeText("hello")).toBe("hello");
78
+
79
+ // Test with number
80
+ expect(getNodeText(42)).toBe("42");
81
+
82
+ // Test with null/undefined
83
+ expect(getNodeText(null)).toBe("");
84
+ expect(getNodeText(undefined)).toBe("");
85
+
86
+ // Test with array
87
+ expect(getNodeText(["hello", " ", "world"])).toBe("hello world");
88
+ expect(getNodeText([1, 2, 3])).toBe("123");
89
+
90
+ // Test with mixed array
91
+ expect(getNodeText(["hello", 42, null])).toBe("hello42");
92
+
93
+ // Test with React element
94
+ const mockElement = React.createElement("span", null, "child text");
95
+ expect(getNodeText(mockElement)).toBe("child text");
96
+
97
+ // Test with nested React elements
98
+ const nestedElement = React.createElement("div", null, React.createElement("span", null, "nested text"));
99
+ expect(getNodeText(nestedElement)).toBe("nested text");
100
+
101
+ // Test with array of React elements
102
+ const arrayOfElements = [
103
+ React.createElement("span", null, "first"),
104
+ React.createElement("span", null, "second"),
105
+ ];
106
+ expect(getNodeText(arrayOfElements)).toBe("firstsecond");
107
+ });
108
+
109
+ /**
110
+ * Suspends the currently rendered component indefinitely. Will not unsuspend unless the component rerenders.
111
+ *
112
+ * You can use this to translate older query- or AsyncResult-based code to new the Suspense system, for example: `if (query.isLoading) suspend();`
113
+ */
114
+ export function suspend(): never {
115
+ use(neverResolve());
116
+ throw new Error("Somehow a Promise that never resolves was resolved?");
117
+ }
118
+
119
+ export function mapRef<T, R>(ref: ReadonlyRef<T>, mapper: (value: T) => R): ReadonlyRef<R> {
120
+ let last: [T, R] | null = null;
121
+ return {
122
+ get current() {
123
+ const input = ref.current;
124
+ if (last === null || input !== last[0]) {
125
+ last = [input, mapper(input)];
126
+ }
127
+ return last[1];
128
+ },
129
+ };
130
+ }
131
+
132
+ export type ReadonlyRef<T> = {
133
+ readonly current: T,
134
+ };
135
+
136
+ export type RefState<T> = ReadonlyRef<T> & {
137
+ set: (updater: SetStateAction<T>) => void,
138
+ };
139
+
140
+ /**
141
+ * Like useState, but its value is immediately available on refState.current after being set.
142
+ *
143
+ * Like useRef, but setting the value will cause a rerender.
144
+ *
145
+ * Note that useRefState returns a new object every time a rerender happens due to a value change, which is intentional
146
+ * as it allows you to specify it in a dependency array like this:
147
+ *
148
+ * ```tsx
149
+ * useEffect(() => {
150
+ * // do something with refState.current
151
+ * }, [refState]); // instead of refState.current
152
+ * ```
153
+ *
154
+ * If you don't want this, you can wrap the result in a useMemo call.
155
+ */
156
+ export function useRefState<T>(initialValue: T | (() => T)): RefState<T> {
157
+ // Support lazy initialization like React.useState does: if initialValue is a function,
158
+ // call it once to get the actual initial value (React.useRef does NOT do this automatically).
159
+ const lazyInitRef = React.useRef<{ v: T } | null>(null);
160
+ if (lazyInitRef.current === null) {
161
+ lazyInitRef.current = {
162
+ v: typeof initialValue === "function" ? (initialValue as () => T)() : initialValue,
163
+ };
164
+ }
165
+ const resolvedInitialValue = lazyInitRef.current.v;
166
+ const [, setState] = React.useState<T>(() => resolvedInitialValue);
167
+ const ref = React.useRef(resolvedInitialValue);
168
+ const setValue = React.useCallback((updater: SetStateAction<T>) => {
169
+ const value: T = typeof updater === "function" ? (updater as any)(ref.current) : updater;
170
+ ref.current = value;
171
+ setState(value);
172
+ }, []);
173
+ const res = React.useMemo(() => ({
174
+ get current() {
175
+ return ref.current;
176
+ },
177
+ set: setValue,
178
+ }), [setValue]);
179
+ return res;
180
+ }
181
+
182
+ export function mapRefState<T, R>(refState: RefState<T>, mapper: (value: T) => R, reverseMapper: (oldT: T, newR: R) => T): RefState<R> {
183
+ let last: [T, R] | null = null;
184
+ return {
185
+ get current() {
186
+ const input = refState.current;
187
+ if (last === null || input !== last[0]) {
188
+ last = [input, mapper(input)];
189
+ }
190
+ return last[1];
191
+ },
192
+ set(updater: SetStateAction<R>) {
193
+ const value: R = typeof updater === "function" ? (updater as any)(this.current) : updater;
194
+ refState.set(reverseMapper(refState.current, value));
195
+ },
196
+ };
197
+ }
198
+
199
+ export function useQueryState(key: string, defaultValue?: string) {
200
+ const getValue = () => new URLSearchParams(window.location.search).get(key) ?? defaultValue ?? null;
201
+
202
+ const [value, setValue] = React.useState(getValue);
203
+
204
+ React.useEffect(() => {
205
+ const onPopState = () => setValue(getValue());
206
+ window.addEventListener("popstate", onPopState);
207
+ return () => window.removeEventListener("popstate", onPopState);
208
+ }, []);
209
+
210
+ const update = (next: string | null) => {
211
+ const params = new URLSearchParams(window.location.search);
212
+ if (next !== null) {
213
+ params.set(key, next);
214
+ } else {
215
+ params.delete(key);
216
+ }
217
+ const newUrl = params.toString()
218
+ ? `${window.location.pathname}?${params.toString()}`
219
+ : window.location.pathname;
220
+ window.history.pushState(null, "", newUrl);
221
+ setValue(next);
222
+ };
223
+
224
+ return [value, update] as const;
225
+ }
226
+
227
+ export function shouldRethrowRenderingError(error: unknown): boolean {
228
+ return !!error && typeof error === "object" && "digest" in error && error.digest === "BAILOUT_TO_CLIENT_SIDE_RENDERING";
229
+ }
230
+
231
+ export class NoSuspenseBoundaryError extends Error {
232
+ digest: string;
233
+ reason: string;
234
+ __noSuspenseBoundarySentinel = NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL;
235
+
236
+ constructor(options: { caller?: string }) {
237
+ ensureMonkeyPatch();
238
+
239
+ super(deindent`
240
+ Suspense boundary not found! Read the error message below carefully (or paste it into your AI agent).
241
+
242
+ ${options.caller ?? "This code path"} attempted to display a loading indicator, but didn't find a Suspense boundary above it. Please read the error message below carefully.
243
+
244
+ There are several potential causes:
245
+
246
+ 1. [Next.js] You are missing a loading.tsx file in your app directory. Fix it by adding a loading.tsx file in your app directory.
247
+
248
+ 2. [React] You are missing a <Suspense> boundary in your component. Fix it by wrapping your component (or the entire app) in a <Suspense> component.
249
+
250
+ 3. [Next.js] The component is rendered in the root (outermost) layout.tsx or template.tsx file. Next.js does not wrap those files in a Suspense boundary, even if there is a loading.tsx file in the same folder. To fix it, wrap your layout inside a route group like this:
251
+
252
+ - app
253
+ - - layout.tsx // contains <html> and <body>, alongside providers and other components that don't need ${options.caller ?? "this code path"}
254
+ - - loading.tsx // required for suspense
255
+ - - (main)
256
+ - - - layout.tsx // contains the main layout of your app, like a sidebar or a header, and can use ${options.caller ?? "this code path"}
257
+ - - - route.tsx // your actual main page
258
+ - - - the rest of your app
259
+
260
+ For more information on this approach, see Next's documentation on route groups: https://nextjs.org/docs/app/building-your-application/routing/route-groups
261
+
262
+ 4. You caught this error with try-catch or a custom error boundary. Fix this by rethrowing the error or not catching it in the first place.
263
+
264
+ 5. Your version of Hexclave is too old. Upgrade to the latest version to see if that fixes the issue.
265
+
266
+ See: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout
267
+
268
+ More information on SSR and Suspense boundaries: https://react.dev/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content
269
+ `);
270
+
271
+ this.name = "NoSuspenseBoundaryError";
272
+ this.reason = options.caller ?? "suspendIfSsr()";
273
+
274
+ // set the digest so nextjs doesn't log the error
275
+ // https://github.com/vercel/next.js/blob/d01d6d9c35a8c2725b3d74c1402ab76d4779a6cf/packages/next/src/shared/lib/lazy-dynamic/bailout-to-csr.ts#L14
276
+ this.digest = "BAILOUT_TO_CLIENT_SIDE_RENDERING";
277
+ }
278
+ }
279
+ import.meta.vitest?.test("NoSuspenseBoundaryError", ({ expect }) => {
280
+ // Test with default options
281
+ const defaultError = new NoSuspenseBoundaryError({});
282
+ expect(defaultError.name).toBe("NoSuspenseBoundaryError");
283
+ expect(defaultError.reason).toBe("suspendIfSsr()");
284
+ expect(defaultError.digest).toBe("BAILOUT_TO_CLIENT_SIDE_RENDERING");
285
+ expect(defaultError.message).toContain("This code path attempted to display a loading indicator");
286
+
287
+ // Test with custom caller
288
+ const customError = new NoSuspenseBoundaryError({ caller: "CustomComponent" });
289
+ expect(customError.name).toBe("NoSuspenseBoundaryError");
290
+ expect(customError.reason).toBe("CustomComponent");
291
+ expect(customError.digest).toBe("BAILOUT_TO_CLIENT_SIDE_RENDERING");
292
+ expect(customError.message).toContain("CustomComponent attempted to display a loading indicator");
293
+
294
+ // Verify error message contains all the necessary information
295
+ expect(customError.message).toContain("loading.tsx");
296
+ expect(customError.message).toContain("route groups");
297
+ expect(customError.message).toContain("https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout");
298
+ });
299
+
300
+
301
+ /**
302
+ * Use this in a component or a hook to disable SSR. Should be wrapped in a Suspense boundary, or it will throw an error.
303
+ */
304
+ export function suspendIfSsr(caller?: string) {
305
+ if (!isBrowserLike()) {
306
+ throw new NoSuspenseBoundaryError({ caller });
307
+ }
308
+ }