@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,52 @@
1
+ import { expect, it } from "vitest";
2
+ import { typeAssertExtends } from "./utils/types";
3
+ import { defineStackConfig, type StackConfig } from "./config-authoring";
4
+
5
+ const validConfig = defineStackConfig({
6
+ payments: {
7
+ items: {
8
+ todos: {
9
+ displayName: "Todo Slots",
10
+ customerType: "user",
11
+ },
12
+ },
13
+ },
14
+ });
15
+ const showOnboardingConfig = defineStackConfig("show-onboarding");
16
+
17
+ typeAssertExtends<typeof validConfig, StackConfig>()();
18
+ typeAssertExtends<typeof showOnboardingConfig, StackConfig>()();
19
+
20
+ it("returns its input unchanged", () => {
21
+ expect(defineStackConfig(validConfig)).toBe(validConfig);
22
+ expect(defineStackConfig(showOnboardingConfig)).toBe(showOnboardingConfig);
23
+ });
24
+
25
+ defineStackConfig({
26
+ // @ts-expect-error Top-level dot notation should not be accepted in typed config files.
27
+ "payments.items": {
28
+ todos: {
29
+ displayName: "Todo Slots",
30
+ customerType: "user",
31
+ },
32
+ },
33
+ });
34
+
35
+ defineStackConfig({
36
+ payments: {
37
+ // @ts-expect-error Unknown keys should not be accepted in typed config files.
38
+ missingField: true,
39
+ },
40
+ });
41
+
42
+ defineStackConfig({
43
+ payments: {
44
+ items: {
45
+ todos: {
46
+ displayName: "Todo Slots",
47
+ // @ts-expect-error Invalid enum values should fail type-checking.
48
+ customerType: "workspace",
49
+ },
50
+ },
51
+ },
52
+ });
@@ -0,0 +1,37 @@
1
+ import type { BranchConfigNormalizedOverride } from "./config/schema";
2
+
3
+ type StackConfigObject = BranchConfigNormalizedOverride;
4
+ export const showOnboardingHexclaveConfigValue = "show-onboarding";
5
+ /** @deprecated Use `HexclaveConfig` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */
6
+ export type StackConfig = StackConfigObject | typeof showOnboardingHexclaveConfigValue;
7
+
8
+ // Hexclave alias — same shape, declared separately so it doesn't inherit the deprecation tag.
9
+ export type HexclaveConfig = StackConfigObject | typeof showOnboardingHexclaveConfigValue;
10
+
11
+ type StrictConfigShape<Actual, Expected> =
12
+ Expected extends readonly unknown[]
13
+ ? Actual extends readonly unknown[]
14
+ ? { [K in keyof Actual]: K extends keyof Expected ? StrictConfigShape<Actual[K], Expected[K]> : never }
15
+ : Actual
16
+ : Expected extends object
17
+ ? Actual extends object
18
+ ? Exclude<keyof Actual, keyof Expected> extends never
19
+ ? { [K in keyof Actual]: K extends keyof Expected ? StrictConfigShape<Actual[K], Expected[K]> : never }
20
+ : never
21
+ : Actual
22
+ : Actual;
23
+
24
+ type StrictStackConfig<T extends StackConfig> =
25
+ T extends StackConfigObject
26
+ ? T & StrictConfigShape<T, StackConfigObject>
27
+ : T;
28
+
29
+ /** @deprecated Use `defineHexclaveConfig` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */
30
+ export function defineStackConfig<const T extends StackConfig>(config: StrictStackConfig<T>): T {
31
+ return config;
32
+ }
33
+
34
+ // Hexclave alias — separate function so it does not inherit the deprecation tag.
35
+ export function defineHexclaveConfig<const T extends HexclaveConfig>(config: StrictStackConfig<T>): T {
36
+ return config;
37
+ }
@@ -0,0 +1,142 @@
1
+ import { existsSync, readFileSync } from "fs";
2
+ import path from "path";
3
+ import { parseHexclaveConfigFileContent, renderConfigFileContent } from "./hexclave-config-file";
4
+ export { parseHexclaveConfigFileContent, renderConfigFileContent };
5
+
6
+ /**
7
+ * Packages that export the `HexclaveConfig` type, in priority order.
8
+ * The first match found in a project's dependencies wins. Hexclave-branded
9
+ * packages come first (canonical); the legacy `@stackframe/*` names remain
10
+ * so projects pinned to the last legacy release still render a config file
11
+ * that compiles against their installed SDK.
12
+ */
13
+ const CONFIG_IMPORT_PACKAGES = [
14
+ "@hexclave/next",
15
+ "@hexclave/react",
16
+ "@hexclave/js",
17
+ "@hexclave/template",
18
+ "@stackframe/stack",
19
+ "@stackframe/react",
20
+ "@stackframe/js",
21
+ "@stackframe/template",
22
+ ] as const;
23
+
24
+ /**
25
+ * Given a list of dependency names (from package.json), returns the SDK
26
+ * package that should be used for the `HexclaveConfig` import, or `undefined`
27
+ * if none of the known packages are installed.
28
+ */
29
+ export function detectConfigImportPackage(dependencies: string[]): string | undefined {
30
+ for (const pkg of CONFIG_IMPORT_PACKAGES) {
31
+ if (dependencies.includes(pkg)) {
32
+ return pkg;
33
+ }
34
+ }
35
+ return undefined;
36
+ }
37
+
38
+ /**
39
+ * Walks up from `dir` to find the nearest `package.json` and returns the
40
+ * best SDK package to use for the `HexclaveConfig` type import.
41
+ */
42
+ export function detectImportPackageFromDir(dir: string): string | undefined {
43
+ let current = dir;
44
+ while (true) {
45
+ const pkgPath = path.join(current, "package.json");
46
+ if (existsSync(pkgPath)) {
47
+ try {
48
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
49
+ const deps = [
50
+ ...Object.keys(pkg.dependencies ?? {}),
51
+ ...Object.keys(pkg.devDependencies ?? {}),
52
+ ];
53
+ return detectConfigImportPackage(deps);
54
+ } catch {
55
+ return undefined;
56
+ }
57
+ }
58
+ const parent = path.dirname(current);
59
+ if (parent === current) break;
60
+ current = parent;
61
+ }
62
+ return undefined;
63
+ }
64
+
65
+ import.meta.vitest?.test("renderConfigFileContent normalizes config exports", ({ expect }) => {
66
+ expect(renderConfigFileContent({
67
+ "payments.items.todos.displayName": "Todo Slots",
68
+ "payments.items.todos.customerType": "user",
69
+ })).toContain(`export const config: HexclaveConfig = {
70
+ "payments": {
71
+ "items": {
72
+ "todos": {
73
+ "displayName": "Todo Slots",
74
+ "customerType": "user"
75
+ }
76
+ }
77
+ }
78
+ };`);
79
+ });
80
+
81
+ import.meta.vitest?.test("parseHexclaveConfigFileContent parses static config exports", ({ expect }) => {
82
+ expect(parseHexclaveConfigFileContent(`
83
+ import type { StackConfig } from "@hexclave/js";
84
+ export const config: StackConfig = {
85
+ auth: { allowSignUp: true },
86
+ payments: { testMode: false },
87
+ };
88
+ `, "stack.config.ts")).toMatchInlineSnapshot(`
89
+ {
90
+ "auth": {
91
+ "allowSignUp": true,
92
+ },
93
+ "payments": {
94
+ "testMode": false,
95
+ },
96
+ }
97
+ `);
98
+ });
99
+
100
+ import.meta.vitest?.test("parseHexclaveConfigFileContent parses show-onboarding", ({ expect }) => {
101
+ expect(parseHexclaveConfigFileContent('export const config = "show-onboarding";', "stack.config.ts")).toBe("show-onboarding");
102
+ });
103
+
104
+ import.meta.vitest?.test("parseHexclaveConfigFileContent rejects dynamic config exports", ({ expect }) => {
105
+ expect(() => parseHexclaveConfigFileContent("export const config = makeConfig();", "stack.config.ts")).toThrow(/Unsupported config expression/);
106
+ });
107
+
108
+ import.meta.vitest?.test("renderConfigFileContent rejects conflicting dotted keys", ({ expect }) => {
109
+ expect(() => renderConfigFileContent({
110
+ "a.b": 1,
111
+ "a.b.c": 2,
112
+ })).toThrowError(/conflicting keys.*"a\.b\.c"/);
113
+ });
114
+
115
+ import.meta.vitest?.test("renderConfigFileContent rejects invalid config exports", ({ expect }) => {
116
+ expect(() => renderConfigFileContent(null)).toThrowErrorMatchingInlineSnapshot(
117
+ `[Error: Invalid config: expected a plain object.]`,
118
+ );
119
+ });
120
+
121
+ import.meta.vitest?.test("renderConfigFileContent uses custom import package", ({ expect }) => {
122
+ const content = renderConfigFileContent({}, "@hexclave/next");
123
+ expect(content).toContain('import type { HexclaveConfig } from "@hexclave/next";');
124
+ });
125
+
126
+ import.meta.vitest?.test("renderConfigFileContent defaults to @hexclave/js", ({ expect }) => {
127
+ const content = renderConfigFileContent({});
128
+ expect(content).toContain('import type { HexclaveConfig } from "@hexclave/js";');
129
+ });
130
+
131
+ import.meta.vitest?.test("detectConfigImportPackage picks first matching package by priority", ({ expect }) => {
132
+ expect(detectConfigImportPackage(["@hexclave/next", "@hexclave/js"])).toBe("@hexclave/next");
133
+ expect(detectConfigImportPackage(["@hexclave/react", "@hexclave/js"])).toBe("@hexclave/react");
134
+ expect(detectConfigImportPackage(["@hexclave/js"])).toBe("@hexclave/js");
135
+ // Hexclave names take priority over legacy stackframe names when both appear.
136
+ expect(detectConfigImportPackage(["@stackframe/stack", "@hexclave/next"])).toBe("@hexclave/next");
137
+ // Legacy fallback still works for projects pinned to the last @stackframe/* release.
138
+ expect(detectConfigImportPackage(["@stackframe/stack"])).toBe("@stackframe/stack");
139
+ expect(detectConfigImportPackage(["@stackframe/template"])).toBe("@stackframe/template");
140
+ expect(detectConfigImportPackage(["lodash", "express"])).toBeUndefined();
141
+ expect(detectConfigImportPackage([])).toBeUndefined();
142
+ });
package/src/crud.tsx ADDED
@@ -0,0 +1,275 @@
1
+ import * as yup from 'yup';
2
+ import { yupObject, yupString } from './schema-fields';
3
+ import { filterUndefined } from './utils/objects';
4
+ import { NullishCoalesce } from './utils/types';
5
+
6
+ export type AccessType = "client" | "server" | "admin";
7
+ export type CrudOperation = "create" | "read" | "update" | "delete";
8
+ export type CrudlOperation = "create" | "read" | "update" | "delete" | "list";
9
+ export type AccessTypeXCrudOperation = `${AccessType}${Capitalize<CrudOperation>}`;
10
+ export type AccessTypeXCrudlOperation = `${AccessType}${Capitalize<CrudlOperation>}`;
11
+
12
+ declare module 'yup' {
13
+ // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
14
+ export interface CustomSchemaMetadata {
15
+ openapiField?: {
16
+ description?: string,
17
+ exampleValue?: any,
18
+ hidden?: boolean,
19
+ onlyShowInOperations?: Capitalize<CrudlOperation>[],
20
+ },
21
+ }
22
+ }
23
+
24
+ type ShownEndpointDocumentation = {
25
+ summary: string,
26
+ description: string,
27
+ tags?: string[],
28
+ crudOperation?: Capitalize<CrudlOperation>,
29
+ };
30
+ export type EndpointDocumentation =
31
+ | ({ hidden: true } & Partial<ShownEndpointDocumentation>)
32
+ | ({ hidden?: boolean } & ShownEndpointDocumentation);
33
+
34
+
35
+ type InnerCrudSchema<
36
+ CreateSchema extends yup.AnySchema | undefined = yup.AnySchema | undefined,
37
+ ReadSchema extends yup.AnySchema | undefined = yup.AnySchema | undefined,
38
+ UpdateSchema extends yup.AnySchema | undefined = yup.AnySchema | undefined,
39
+ DeleteSchema extends yup.AnySchema | undefined = yup.AnySchema | undefined,
40
+ > = {
41
+ createSchema: CreateSchema,
42
+ createDocs: EndpointDocumentation | undefined,
43
+
44
+ readSchema: ReadSchema,
45
+ readDocs: EndpointDocumentation | undefined,
46
+ listDocs: EndpointDocumentation | undefined,
47
+
48
+ updateSchema: UpdateSchema,
49
+ updateDocs: EndpointDocumentation | undefined,
50
+
51
+ deleteSchema: DeleteSchema,
52
+ deleteDocs: EndpointDocumentation | undefined,
53
+ };
54
+
55
+ export type CrudSchema<
56
+ ClientSchema extends InnerCrudSchema = InnerCrudSchema,
57
+ ServerSchema extends InnerCrudSchema = InnerCrudSchema,
58
+ AdminSchema extends InnerCrudSchema = InnerCrudSchema,
59
+ > = {
60
+ client: ClientSchema,
61
+ server: ServerSchema,
62
+ admin: AdminSchema,
63
+
64
+ hasCreate: boolean,
65
+ hasRead: boolean,
66
+ hasUpdate: boolean,
67
+ hasDelete: boolean,
68
+ };
69
+
70
+ export type CrudSchemaCreationOptions = {
71
+ [K in AccessTypeXCrudOperation as `${K}Schema`]?: yup.AnySchema
72
+ };
73
+
74
+ type FillInOptionalsPrepareStep<O extends CrudSchemaCreationOptions> =
75
+ & { [K in keyof Required<CrudSchemaCreationOptions>]: K extends keyof O ? O[K] : undefined };
76
+
77
+ type FillInOptionalsStep<O extends FillInOptionalsPrepareStep<CrudSchemaCreationOptions>> = {
78
+ clientCreateSchema: NullishCoalesce<O['clientCreateSchema'], undefined>,
79
+ clientReadSchema: NullishCoalesce<O['clientReadSchema'], undefined>,
80
+ clientUpdateSchema: NullishCoalesce<O['clientUpdateSchema'], undefined>,
81
+ clientDeleteSchema: NullishCoalesce<O['clientDeleteSchema'], undefined>,
82
+
83
+ serverCreateSchema: NullishCoalesce<O['serverCreateSchema'], O['clientCreateSchema']>,
84
+ serverReadSchema: NullishCoalesce<O['serverReadSchema'], O['clientReadSchema']>,
85
+ serverUpdateSchema: NullishCoalesce<O['serverUpdateSchema'], O['clientUpdateSchema']>,
86
+ serverDeleteSchema: NullishCoalesce<O['serverDeleteSchema'], O['clientDeleteSchema']>,
87
+
88
+ adminCreateSchema: NullishCoalesce<O['adminCreateSchema'], O['serverCreateSchema']>,
89
+ adminReadSchema: NullishCoalesce<O['adminReadSchema'], O['serverReadSchema']>,
90
+ adminUpdateSchema: NullishCoalesce<O['adminUpdateSchema'], O['serverUpdateSchema']>,
91
+ adminDeleteSchema: NullishCoalesce<O['adminDeleteSchema'], O['serverDeleteSchema']>,
92
+ };
93
+
94
+ type FillInOptionals<O extends CrudSchemaCreationOptions> = FillInOptionalsStep<FillInOptionalsStep<FillInOptionalsStep<FillInOptionalsPrepareStep<O>>>>;
95
+
96
+ type CrudSchemaFromOptionsInner<O extends FillInOptionals<any>> = CrudSchema<
97
+ InnerCrudSchema<O['clientCreateSchema'], O['clientReadSchema'], O['clientUpdateSchema'], O['clientDeleteSchema']>,
98
+ InnerCrudSchema<O['serverCreateSchema'], O['serverReadSchema'], O['serverUpdateSchema'], O['serverDeleteSchema']>,
99
+ InnerCrudSchema<O['adminCreateSchema'], O['adminReadSchema'], O['adminUpdateSchema'], O['adminDeleteSchema']>
100
+ >;
101
+
102
+ export type CrudSchemaFromOptions<O extends CrudSchemaCreationOptions> = CrudSchemaFromOptionsInner<FillInOptionals<O>>;
103
+
104
+ type InnerCrudTypeOf<S extends InnerCrudSchema> =
105
+ & (S['createSchema'] extends {} ? { Create: yup.InferType<S['createSchema']> } : {})
106
+ & (S['readSchema'] extends {} ? { Read: yup.InferType<S['readSchema']> } : {})
107
+ & (S['updateSchema'] extends {} ? { Update: yup.InferType<S['updateSchema']> } : {})
108
+ & (S['deleteSchema'] extends {} ? { Delete: yup.InferType<S['deleteSchema']> } : {})
109
+ & (S['readSchema'] extends {} ? { List: {
110
+ items: yup.InferType<S['readSchema']>[],
111
+ is_paginated: boolean,
112
+ pagination?: {
113
+ next_cursor: string | null,
114
+ },
115
+ }, } : {});
116
+
117
+ export type CrudTypeOf<S extends CrudSchema> = {
118
+ Client: InnerCrudTypeOf<S['client']>,
119
+ Server: InnerCrudTypeOf<S['server']>,
120
+ Admin: InnerCrudTypeOf<S['admin']>,
121
+ }
122
+
123
+ type CrudDocsCreationOptions<SO extends CrudSchemaCreationOptions> = {
124
+ [X in AccessTypeXCrudlOperation]?: EndpointDocumentation
125
+ };
126
+
127
+ export function createCrud<SO extends CrudSchemaCreationOptions>(options: SO & { docs?: CrudDocsCreationOptions<SO> }): CrudSchemaFromOptions<SO> {
128
+ const docs = options.docs ?? {};
129
+ const client = {
130
+ createSchema: options.clientCreateSchema,
131
+ createDocs: docs.clientCreate,
132
+
133
+ readSchema: options.clientReadSchema,
134
+ readDocs: docs.clientRead,
135
+ listDocs: docs.clientList,
136
+
137
+ updateSchema: options.clientUpdateSchema,
138
+ updateDocs: docs.clientUpdate,
139
+
140
+ deleteSchema: options.clientDeleteSchema,
141
+ deleteDocs: docs.clientDelete,
142
+ };
143
+
144
+ const serverOverrides = filterUndefined({
145
+ createSchema: options.serverCreateSchema,
146
+ createDocs: docs.serverCreate,
147
+
148
+ readSchema: options.serverReadSchema,
149
+ readDocs: docs.serverRead,
150
+ listDocs: docs.serverList,
151
+
152
+ updateSchema: options.serverUpdateSchema,
153
+ updateDocs: docs.serverUpdate,
154
+
155
+ deleteSchema: options.serverDeleteSchema,
156
+ deleteDocs: docs.serverDelete,
157
+ });
158
+ const server = {
159
+ ...client,
160
+ ...serverOverrides,
161
+ };
162
+
163
+ const adminOverrides = filterUndefined({
164
+ createSchema: options.adminCreateSchema,
165
+ createDocs: docs.adminCreate,
166
+
167
+ readSchema: options.adminReadSchema,
168
+ readDocs: docs.adminRead,
169
+ listDocs: docs.adminList,
170
+
171
+ updateSchema: options.adminUpdateSchema,
172
+ updateDocs: docs.adminUpdate,
173
+
174
+ deleteSchema: options.adminDeleteSchema,
175
+ deleteDocs: docs.adminDelete,
176
+ });
177
+ const admin = {
178
+ ...server,
179
+ ...adminOverrides,
180
+ };
181
+
182
+ return {
183
+ client: client as any,
184
+ server: server as any,
185
+ admin: admin as any,
186
+
187
+ hasCreate: !!admin.createSchema,
188
+ hasRead: !!admin.readSchema,
189
+ hasUpdate: !!admin.updateSchema,
190
+ hasDelete: !!admin.deleteSchema,
191
+ };
192
+ }
193
+
194
+ import.meta.vitest?.test("createCrud", ({ expect }) => {
195
+ // Test with empty options
196
+ const emptyCrud = createCrud({});
197
+ expect(emptyCrud.hasCreate).toBe(false);
198
+ expect(emptyCrud.hasRead).toBe(false);
199
+ expect(emptyCrud.hasUpdate).toBe(false);
200
+ expect(emptyCrud.hasDelete).toBe(false);
201
+ expect(emptyCrud.client.createSchema).toBeUndefined();
202
+ expect(emptyCrud.server.createSchema).toBeUndefined();
203
+ expect(emptyCrud.admin.createSchema).toBeUndefined();
204
+
205
+ // Test with client schemas only
206
+ const mockSchema = yupObject().shape({
207
+ name: yupString().defined(),
208
+ });
209
+
210
+ const clientOnlyCrud = createCrud({
211
+ clientCreateSchema: mockSchema,
212
+ clientReadSchema: mockSchema,
213
+ });
214
+ expect(clientOnlyCrud.hasCreate).toBe(true);
215
+ expect(clientOnlyCrud.hasRead).toBe(true);
216
+ expect(clientOnlyCrud.hasUpdate).toBe(false);
217
+ expect(clientOnlyCrud.hasDelete).toBe(false);
218
+ expect(clientOnlyCrud.client.createSchema).toBe(mockSchema);
219
+ expect(clientOnlyCrud.server.createSchema).toBe(mockSchema);
220
+ expect(clientOnlyCrud.admin.createSchema).toBe(mockSchema);
221
+
222
+ // Test with server overrides
223
+ const serverSchema = yupObject().shape({
224
+ name: yupString().defined(),
225
+ internalField: yupString().defined(),
226
+ });
227
+
228
+ const serverOverrideCrud = createCrud({
229
+ clientCreateSchema: mockSchema,
230
+ serverCreateSchema: serverSchema,
231
+ });
232
+ expect(serverOverrideCrud.hasCreate).toBe(true);
233
+ expect(serverOverrideCrud.client.createSchema).toBe(mockSchema);
234
+ expect(serverOverrideCrud.server.createSchema).toBe(serverSchema);
235
+ expect(serverOverrideCrud.admin.createSchema).toBe(serverSchema);
236
+
237
+ // Test with admin overrides
238
+ const adminSchema = yupObject().shape({
239
+ name: yupString().defined(),
240
+ internalField: yupString().defined(),
241
+ adminField: yupString().defined(),
242
+ });
243
+
244
+ const fullOverrideCrud = createCrud({
245
+ clientCreateSchema: mockSchema,
246
+ serverCreateSchema: serverSchema,
247
+ adminCreateSchema: adminSchema,
248
+ });
249
+ expect(fullOverrideCrud.hasCreate).toBe(true);
250
+ expect(fullOverrideCrud.client.createSchema).toBe(mockSchema);
251
+ expect(fullOverrideCrud.server.createSchema).toBe(serverSchema);
252
+ expect(fullOverrideCrud.admin.createSchema).toBe(adminSchema);
253
+
254
+ // Test with documentation
255
+ const crudWithDocs = createCrud({
256
+ clientCreateSchema: mockSchema,
257
+ docs: {
258
+ clientCreate: {
259
+ summary: "Create a resource",
260
+ description: "Creates a new resource",
261
+ tags: ["resources"],
262
+ },
263
+ },
264
+ });
265
+ expect(crudWithDocs.client.createDocs).toEqual({
266
+ summary: "Create a resource",
267
+ description: "Creates a new resource",
268
+ tags: ["resources"],
269
+ });
270
+ expect(crudWithDocs.server.createDocs).toEqual({
271
+ summary: "Create a resource",
272
+ description: "Creates a new resource",
273
+ tags: ["resources"],
274
+ });
275
+ });
@@ -0,0 +1 @@
1
+ import type { } from "react/canary";