@hexclave/shared 1.0.2 → 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 (353) 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 +217 -30
  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 +63 -63
  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 +216 -31
  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 +63 -63
  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 +8 -8
  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 +32 -32
  69. package/dist/esm/interface/crud/current-user.d.ts +9 -9
  70. package/dist/esm/interface/crud/email-outbox.d.ts +200 -200
  71. package/dist/esm/interface/crud/products.d.ts +13 -13
  72. package/dist/esm/interface/crud/products.d.ts.map +1 -1
  73. package/dist/esm/interface/crud/project-api-keys.d.ts +4 -4
  74. package/dist/esm/interface/crud/project-permissions.js +5 -5
  75. package/dist/esm/interface/crud/project-permissions.js.map +1 -1
  76. package/dist/esm/interface/crud/projects.d.ts +36 -36
  77. package/dist/esm/interface/crud/team-member-profiles.d.ts +18 -18
  78. package/dist/esm/interface/crud/team-permissions.js +5 -5
  79. package/dist/esm/interface/crud/team-permissions.js.map +1 -1
  80. package/dist/esm/interface/crud/transactions.d.ts +6 -6
  81. package/dist/esm/interface/crud/transactions.d.ts.map +1 -1
  82. package/dist/esm/interface/crud/users.d.ts +12 -12
  83. package/dist/esm/interface/page-component-versions.js +107 -107
  84. package/dist/esm/interface/page-component-versions.js.map +1 -1
  85. package/dist/esm/interface/webhooks.d.ts +2 -2
  86. package/dist/esm/known-errors.d.ts +2 -2
  87. package/dist/esm/known-errors.js +4 -4
  88. package/dist/esm/known-errors.js.map +1 -1
  89. package/dist/esm/schema-fields.d.ts +17 -7
  90. package/dist/esm/schema-fields.d.ts.map +1 -1
  91. package/dist/esm/schema-fields.js +26 -16
  92. package/dist/esm/schema-fields.js.map +1 -1
  93. package/dist/esm/sessions.d.ts +7 -7
  94. package/dist/esm/utils/crypto.d.ts.map +1 -1
  95. package/dist/esm/utils/crypto.js.map +1 -1
  96. package/dist/esm/utils/currency-constants.d.ts +6 -0
  97. package/dist/esm/utils/currency-constants.d.ts.map +1 -1
  98. package/dist/esm/utils/currency-constants.js.map +1 -1
  99. package/dist/esm/utils/dev-env-state-path.d.ts +3 -3
  100. package/dist/esm/utils/dev-env-state-path.d.ts.map +1 -1
  101. package/dist/esm/utils/dev-env-state-path.js +4 -4
  102. package/dist/esm/utils/dev-env-state-path.js.map +1 -1
  103. package/dist/esm/utils/errors.js +2 -2
  104. package/dist/esm/utils/errors.js.map +1 -1
  105. package/dist/esm/utils/esbuild.d.ts.map +1 -1
  106. package/dist/esm/utils/esbuild.js +0 -1
  107. package/dist/esm/utils/esbuild.js.map +1 -1
  108. package/dist/esm/utils/featurebase.d.ts +3 -3
  109. package/dist/esm/utils/featurebase.d.ts.map +1 -1
  110. package/dist/esm/utils/featurebase.js +15 -15
  111. package/dist/esm/utils/featurebase.js.map +1 -1
  112. package/dist/esm/utils/fs.js +6 -6
  113. package/dist/esm/utils/fs.js.map +1 -1
  114. package/dist/esm/utils/globals.d.ts.map +1 -1
  115. package/dist/esm/utils/globals.js +10 -10
  116. package/dist/esm/utils/globals.js.map +1 -1
  117. package/dist/esm/utils/jwt.d.ts.map +1 -1
  118. package/dist/esm/utils/jwt.js +3 -3
  119. package/dist/esm/utils/jwt.js.map +1 -1
  120. package/dist/esm/utils/redirect-urls.d.ts +3 -3
  121. package/dist/esm/utils/redirect-urls.d.ts.map +1 -1
  122. package/dist/esm/utils/redirect-urls.js +5 -5
  123. package/dist/esm/utils/redirect-urls.js.map +1 -1
  124. package/dist/esm/utils/urls.js.map +1 -1
  125. package/dist/helpers/vault/client-side.d.ts.map +1 -1
  126. package/dist/helpers/vault/client-side.js.map +1 -1
  127. package/dist/helpers/vault/server-side.d.ts.map +1 -1
  128. package/dist/helpers/vault/server-side.js.map +1 -1
  129. package/dist/hexclave-config-file.d.ts +15 -0
  130. package/dist/hexclave-config-file.d.ts.map +1 -0
  131. package/dist/{stack-config-file.js → hexclave-config-file.js} +8 -8
  132. package/dist/hexclave-config-file.js.map +1 -0
  133. package/dist/hooks/use-async-callback.js +1 -1
  134. package/dist/hooks/use-async-external-store.js +1 -1
  135. package/dist/hooks/use-strict-memo.js +1 -1
  136. package/dist/interface/admin-metrics.d.ts +8 -8
  137. package/dist/interface/admin-metrics.js.map +1 -1
  138. package/dist/interface/client-interface.js +2 -2
  139. package/dist/interface/client-interface.js.map +1 -1
  140. package/dist/interface/conversations.d.ts +32 -32
  141. package/dist/interface/crud/current-user.d.ts +9 -9
  142. package/dist/interface/crud/email-outbox.d.ts +200 -200
  143. package/dist/interface/crud/products.d.ts +13 -13
  144. package/dist/interface/crud/products.d.ts.map +1 -1
  145. package/dist/interface/crud/project-api-keys.d.ts +4 -4
  146. package/dist/interface/crud/project-permissions.js +5 -5
  147. package/dist/interface/crud/project-permissions.js.map +1 -1
  148. package/dist/interface/crud/projects.d.ts +36 -36
  149. package/dist/interface/crud/team-member-profiles.d.ts +18 -18
  150. package/dist/interface/crud/team-permissions.js +5 -5
  151. package/dist/interface/crud/team-permissions.js.map +1 -1
  152. package/dist/interface/crud/transactions.d.ts +6 -6
  153. package/dist/interface/crud/transactions.d.ts.map +1 -1
  154. package/dist/interface/crud/users.d.ts +12 -12
  155. package/dist/interface/page-component-versions.js +107 -107
  156. package/dist/interface/page-component-versions.js.map +1 -1
  157. package/dist/interface/webhooks.d.ts +2 -2
  158. package/dist/known-errors.d.ts +2 -2
  159. package/dist/known-errors.js +4 -4
  160. package/dist/known-errors.js.map +1 -1
  161. package/dist/schema-fields.d.ts +17 -7
  162. package/dist/schema-fields.d.ts.map +1 -1
  163. package/dist/schema-fields.js +26 -16
  164. package/dist/schema-fields.js.map +1 -1
  165. package/dist/sessions.d.ts +7 -7
  166. package/dist/utils/crypto.d.ts.map +1 -1
  167. package/dist/utils/crypto.js.map +1 -1
  168. package/dist/utils/currency-constants.d.ts +6 -0
  169. package/dist/utils/currency-constants.d.ts.map +1 -1
  170. package/dist/utils/currency-constants.js.map +1 -1
  171. package/dist/utils/dev-env-state-path.d.ts +3 -3
  172. package/dist/utils/dev-env-state-path.d.ts.map +1 -1
  173. package/dist/utils/dev-env-state-path.js +5 -5
  174. package/dist/utils/dev-env-state-path.js.map +1 -1
  175. package/dist/utils/errors.js +2 -2
  176. package/dist/utils/errors.js.map +1 -1
  177. package/dist/utils/esbuild.d.ts.map +1 -1
  178. package/dist/utils/esbuild.js +0 -1
  179. package/dist/utils/esbuild.js.map +1 -1
  180. package/dist/utils/featurebase.d.ts +3 -3
  181. package/dist/utils/featurebase.d.ts.map +1 -1
  182. package/dist/utils/featurebase.js +15 -15
  183. package/dist/utils/featurebase.js.map +1 -1
  184. package/dist/utils/fs.js.map +1 -1
  185. package/dist/utils/globals.d.ts.map +1 -1
  186. package/dist/utils/globals.js +10 -10
  187. package/dist/utils/globals.js.map +1 -1
  188. package/dist/utils/jwt.d.ts.map +1 -1
  189. package/dist/utils/jwt.js +3 -3
  190. package/dist/utils/jwt.js.map +1 -1
  191. package/dist/utils/redirect-urls.d.ts +3 -3
  192. package/dist/utils/redirect-urls.d.ts.map +1 -1
  193. package/dist/utils/redirect-urls.js +5 -5
  194. package/dist/utils/redirect-urls.js.map +1 -1
  195. package/dist/utils/urls.js.map +1 -1
  196. package/package.json +2 -1
  197. package/src/ai/image-limits.ts +62 -0
  198. package/src/ai/llms/llms.ts +36 -0
  199. package/src/ai/unified-prompts/reminders.ts +27 -0
  200. package/src/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.ts +1047 -0
  201. package/src/ai/unified-prompts/skill-site-prompt-parts/brain.ts +5 -0
  202. package/src/ai/unified-prompts/skill-site-prompt-parts/cli-help.ts +5 -0
  203. package/src/ai/unified-prompts/skill-site-prompt-parts/config-docs.ts +5 -0
  204. package/src/ai/unified-prompts/skill-site-prompt-parts/custom-components-instructions.ts +5 -0
  205. package/src/ai/unified-prompts/skill-site-prompt-parts/dashboard-instructions.ts +5 -0
  206. package/src/ai/unified-prompts/skill-site-prompt-parts/docs-index.ts +80 -0
  207. package/src/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.ts +322 -0
  208. package/src/ai/unified-prompts/skill-site-prompt-parts/openapi-specs.ts +5 -0
  209. package/src/ai/unified-prompts/skill-site-prompt-parts/sdk-interface-source.ts +5 -0
  210. package/src/ai/unified-prompts/skill-site-prompt.ts +72 -0
  211. package/src/apps/apps-config.ts +177 -0
  212. package/src/apps/apps-ui.tsx +112 -0
  213. package/src/config/README.md +154 -0
  214. package/src/config/db-sync-mappings.ts +2293 -0
  215. package/src/config/format.ts +388 -0
  216. package/src/config/migrate-catalogs-to-product-lines.ts +475 -0
  217. package/src/config/schema-fuzzer.test.ts +377 -0
  218. package/src/config/schema.ts +1507 -0
  219. package/src/config-authoring.test.ts +52 -0
  220. package/src/config-authoring.ts +37 -0
  221. package/src/config-rendering.ts +142 -0
  222. package/src/crud.tsx +275 -0
  223. package/src/global.d.ts +1 -0
  224. package/src/helpers/emails.ts +250 -0
  225. package/src/helpers/init-prompt.ts +39 -0
  226. package/src/helpers/password.ts +16 -0
  227. package/src/helpers/production-mode.ts +58 -0
  228. package/src/helpers/support-sla.test.ts +124 -0
  229. package/src/helpers/support-sla.ts +93 -0
  230. package/src/helpers/vault/client-side.ts +92 -0
  231. package/src/helpers/vault/server-side.ts +153 -0
  232. package/src/hexclave-config-file.ts +120 -0
  233. package/src/hooks/use-async-callback.tsx +43 -0
  234. package/src/hooks/use-async-external-store.tsx +24 -0
  235. package/src/hooks/use-hash.tsx +14 -0
  236. package/src/hooks/use-hover.tsx +88 -0
  237. package/src/hooks/use-strict-memo.tsx +157 -0
  238. package/src/index.ts +17 -0
  239. package/src/interface/admin-interface.ts +1043 -0
  240. package/src/interface/admin-metrics.ts +214 -0
  241. package/src/interface/client-interface.test.ts +765 -0
  242. package/src/interface/client-interface.ts +2586 -0
  243. package/src/interface/conversations.ts +109 -0
  244. package/src/interface/crud/analytics.ts +11 -0
  245. package/src/interface/crud/connected-accounts.ts +52 -0
  246. package/src/interface/crud/contact-channels.ts +74 -0
  247. package/src/interface/crud/current-user.ts +72 -0
  248. package/src/interface/crud/email-outbox.ts +263 -0
  249. package/src/interface/crud/email-templates.ts +50 -0
  250. package/src/interface/crud/emails.ts +22 -0
  251. package/src/interface/crud/internal-api-keys.ts +69 -0
  252. package/src/interface/crud/invoices.ts +36 -0
  253. package/src/interface/crud/items.ts +24 -0
  254. package/src/interface/crud/notification-preferences.ts +33 -0
  255. package/src/interface/crud/oauth-providers.ts +91 -0
  256. package/src/interface/crud/products.ts +42 -0
  257. package/src/interface/crud/project-api-keys.ts +100 -0
  258. package/src/interface/crud/project-permissions.ts +118 -0
  259. package/src/interface/crud/projects.ts +229 -0
  260. package/src/interface/crud/session-replays.ts +85 -0
  261. package/src/interface/crud/sessions.ts +66 -0
  262. package/src/interface/crud/sign-up-rules.ts +12 -0
  263. package/src/interface/crud/svix-token.ts +20 -0
  264. package/src/interface/crud/team-invitation-details.ts +22 -0
  265. package/src/interface/crud/team-invitation.ts +35 -0
  266. package/src/interface/crud/team-member-profiles.ts +61 -0
  267. package/src/interface/crud/team-memberships.ts +56 -0
  268. package/src/interface/crud/team-permissions.ts +119 -0
  269. package/src/interface/crud/teams.ts +142 -0
  270. package/src/interface/crud/transactions.ts +131 -0
  271. package/src/interface/crud/users.ts +185 -0
  272. package/src/interface/handler-urls.ts +74 -0
  273. package/src/interface/page-component-versions.ts +1702 -0
  274. package/src/interface/server-interface.ts +1146 -0
  275. package/src/interface/webhooks.ts +28 -0
  276. package/src/known-errors.tsx +2031 -0
  277. package/src/local-emulator.ts +5 -0
  278. package/src/payments/payment-methods.ts +167 -0
  279. package/src/payments/stripe-limits.ts +18 -0
  280. package/src/plans.ts +107 -0
  281. package/src/schema-fields.ts +944 -0
  282. package/src/sessions.ts +310 -0
  283. package/src/utils/api-keys.tsx +122 -0
  284. package/src/utils/arrays.tsx +248 -0
  285. package/src/utils/auth-methods.ts +8 -0
  286. package/src/utils/base64.tsx +24 -0
  287. package/src/utils/booleans.tsx +34 -0
  288. package/src/utils/browser-compat.tsx +17 -0
  289. package/src/utils/bytes.tsx +280 -0
  290. package/src/utils/caches.tsx +280 -0
  291. package/src/utils/cel-fields.ts +92 -0
  292. package/src/utils/cloud-hosts.tsx +92 -0
  293. package/src/utils/compile-time.tsx +11 -0
  294. package/src/utils/country-codes.ts +39 -0
  295. package/src/utils/crypto.tsx +174 -0
  296. package/src/utils/currencies.tsx +53 -0
  297. package/src/utils/currency-constants.tsx +51 -0
  298. package/src/utils/dates.tsx +246 -0
  299. package/src/utils/dev-env-state-path.ts +14 -0
  300. package/src/utils/dom.tsx +7 -0
  301. package/src/utils/env.tsx +117 -0
  302. package/src/utils/errors.tsx +284 -0
  303. package/src/utils/esbuild.tsx +235 -0
  304. package/src/utils/featurebase.tsx +247 -0
  305. package/src/utils/fs.tsx +33 -0
  306. package/src/utils/functions.tsx +22 -0
  307. package/src/utils/geo.tsx +16 -0
  308. package/src/utils/globals.tsx +48 -0
  309. package/src/utils/hashes.tsx +52 -0
  310. package/src/utils/html.tsx +60 -0
  311. package/src/utils/http.tsx +91 -0
  312. package/src/utils/ips.tsx +47 -0
  313. package/src/utils/json.tsx +200 -0
  314. package/src/utils/jsx-editable-transpiler.test.ts +156 -0
  315. package/src/utils/jsx-editable-transpiler.tsx +401 -0
  316. package/src/utils/jwt.tsx +170 -0
  317. package/src/utils/locks.tsx +71 -0
  318. package/src/utils/maps.tsx +407 -0
  319. package/src/utils/math.tsx +18 -0
  320. package/src/utils/monkey-patch.tsx +29 -0
  321. package/src/utils/node-http.tsx +47 -0
  322. package/src/utils/numbers.tsx +82 -0
  323. package/src/utils/oauth.tsx +9 -0
  324. package/src/utils/objects.tsx +581 -0
  325. package/src/utils/paginated-lists.test.ts +1039 -0
  326. package/src/utils/paginated-lists.tsx +479 -0
  327. package/src/utils/passkey.tsx +1 -0
  328. package/src/utils/promises.tsx +540 -0
  329. package/src/utils/proxies.tsx +212 -0
  330. package/src/utils/react.tsx +308 -0
  331. package/src/utils/redirect-urls.tsx +299 -0
  332. package/src/utils/regex.tsx +10 -0
  333. package/src/utils/results.tsx +411 -0
  334. package/src/utils/sentry.tsx +20 -0
  335. package/src/utils/stores.tsx +238 -0
  336. package/src/utils/strings.nicify.test.ts +253 -0
  337. package/src/utils/strings.tsx +723 -0
  338. package/src/utils/telemetry.tsx +37 -0
  339. package/src/utils/turnstile-browser.ts +106 -0
  340. package/src/utils/turnstile-flow.test.ts +101 -0
  341. package/src/utils/turnstile-flow.ts +266 -0
  342. package/src/utils/turnstile.ts +34 -0
  343. package/src/utils/typed-arrays.tsx +17 -0
  344. package/src/utils/types.tsx +208 -0
  345. package/src/utils/unicode.tsx +24 -0
  346. package/src/utils/urls.tsx +411 -0
  347. package/src/utils/uuids.tsx +47 -0
  348. package/dist/esm/stack-config-file.d.ts +0 -15
  349. package/dist/esm/stack-config-file.d.ts.map +0 -1
  350. package/dist/esm/stack-config-file.js.map +0 -1
  351. package/dist/stack-config-file.d.ts +0 -15
  352. package/dist/stack-config-file.d.ts.map +0 -1
  353. package/dist/stack-config-file.js.map +0 -1
@@ -0,0 +1,475 @@
1
+ import { HexclaveAssertionError } from "../utils/errors";
2
+ import { isObjectLike, set, typedEntries } from "../utils/objects";
3
+
4
+ /**
5
+ * Renames properties in an object based on a path condition.
6
+ * This is a local copy to avoid circular dependencies with schema.ts.
7
+ */
8
+ function renameProperty(obj: Record<string, any>, oldPath: string | ((path: string[]) => boolean), newName: string | ((path: string[]) => string)): any {
9
+ const pathCond = typeof oldPath === "function" ? oldPath : (p: string[]) => p.join(".") === oldPath;
10
+ const pathMapper = typeof newName === "function" ? newName : (p: string[]) => (newName as string);
11
+
12
+ const res: Record<string, any> = Array.isArray(obj) ? [] : {};
13
+ for (const [key, originalValue] of typedEntries(obj)) {
14
+ const path = key.split(".");
15
+
16
+ for (let i = 0; i < path.length; i++) {
17
+ const pathPrefix = path.slice(0, i + 1);
18
+ if (pathCond(pathPrefix)) {
19
+ const name = pathMapper(pathPrefix);
20
+ if (name.includes(".")) throw new HexclaveAssertionError(`newName must not contain a dot. Provided: ${name}`);
21
+ path[i] = name;
22
+ }
23
+ }
24
+
25
+ const value = isObjectLike(originalValue) ? renameProperty(originalValue, p => pathCond([...path, ...p]), p => pathMapper([...path, ...p])) : originalValue;
26
+ set(res, path.join("."), value);
27
+ }
28
+
29
+ return res;
30
+ }
31
+
32
+ /**
33
+ * Migrates the old "catalogs" format to "productLines", including:
34
+ * 1. Inferring customerType for each catalog from its products (since old catalogs didn't have customerType)
35
+ * 2. Removing empty catalogs (those with no products) since they can't have customerType inferred
36
+ * 3. Renaming payments.catalogs -> payments.productLines
37
+ * 4. Renaming payments.products.*.catalogId -> payments.products.*.productLineId
38
+ *
39
+ * This handles all config formats (nested objects, flat dot-notation, or mixed).
40
+ */
41
+ export function migrateCatalogsToProductLines(obj: Record<string, any>): Record<string, any> {
42
+ // Step 1: Collect catalogId -> customerType mappings from products
43
+ const catalogCustomerTypes = new Map<string, string>();
44
+ collectCatalogCustomerTypes(obj, [], catalogCustomerTypes);
45
+
46
+ // Step 2: Find all catalog IDs that exist
47
+ const allCatalogIds = new Set<string>();
48
+ findAllCatalogIds(obj, [], allCatalogIds);
49
+
50
+ // Step 3: Add customerType keys for catalogs that need them
51
+ let res = { ...obj };
52
+ for (const catalogId of allCatalogIds) {
53
+ const customerType = catalogCustomerTypes.get(catalogId);
54
+ if (customerType) {
55
+ // Find the format used for this catalog and add customerType in the same format
56
+ res = addCustomerTypeToCatalog(res, catalogId, customerType);
57
+ } else {
58
+ // Empty catalog (no products) - remove it since we can't infer customerType
59
+ res = removeCatalog(res, catalogId);
60
+ }
61
+ }
62
+
63
+ // Step 4: Rename catalogs -> productLines
64
+ res = renameProperty(res, "payments.catalogs", "productLines");
65
+
66
+ // Step 5: Rename catalogId -> productLineId in products
67
+ res = renameProperty(res, (p) => p.length === 4 && p[0] === "payments" && p[1] === "products" && p[3] === "catalogId", () => "productLineId");
68
+
69
+ return res;
70
+ }
71
+
72
+ /**
73
+ * Recursively collects catalogId -> customerType mappings from products.
74
+ * Handles both nested and flat config formats.
75
+ */
76
+ function collectCatalogCustomerTypes(
77
+ obj: Record<string, any>,
78
+ basePath: string[],
79
+ result: Map<string, string>
80
+ ): void {
81
+ for (const [key, value] of typedEntries(obj)) {
82
+ const keyParts = key.split(".");
83
+ const fullPath = [...basePath, ...keyParts];
84
+
85
+ // Check for flat format at ROOT level only: payments.products.<productId>.catalogId
86
+ // We check basePath.length === 0 to ensure we're at root and key contains the full path
87
+ if (basePath.length === 0 && fullPath.length === 4 && fullPath[0] === "payments" && fullPath[1] === "products" && fullPath[3] === "catalogId") {
88
+ if (typeof value === "string") {
89
+ const productId = fullPath[2];
90
+ // Look for customerType in the same flat format
91
+ const customerTypeKey = `payments.products.${productId}.customerType`;
92
+ if (customerTypeKey in obj && typeof obj[customerTypeKey] === "string") {
93
+ result.set(value, obj[customerTypeKey]);
94
+ }
95
+ }
96
+ }
97
+
98
+ // Check for nested format: payments.products.<productId> with object value
99
+ if (fullPath.length === 3 && fullPath[0] === "payments" && fullPath[1] === "products" && isObjectLike(value)) {
100
+ const catalogId = findPropertyValue(value, "catalogId") ?? findPropertyValue(value, "productLineId");
101
+ const customerType = findPropertyValue(value, "customerType");
102
+ if (catalogId && typeof catalogId === "string" && customerType && typeof customerType === "string") {
103
+ result.set(catalogId, customerType);
104
+ }
105
+ }
106
+
107
+ if (isObjectLike(value)) {
108
+ collectCatalogCustomerTypes(value, fullPath, result);
109
+ }
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Finds a property value in an object, handling both direct properties and dot-notation keys.
115
+ */
116
+ function findPropertyValue(obj: Record<string, any>, propertyName: string): any {
117
+ // Direct property
118
+ if (propertyName in obj) {
119
+ return obj[propertyName];
120
+ }
121
+ // Check for dot-notation keys that end with the property name
122
+ for (const key of Object.keys(obj)) {
123
+ const parts = key.split(".");
124
+ if (parts[parts.length - 1] === propertyName) {
125
+ return obj[key];
126
+ }
127
+ }
128
+ return undefined;
129
+ }
130
+
131
+ /**
132
+ * Finds all catalog IDs that exist in the config.
133
+ */
134
+ function findAllCatalogIds(
135
+ obj: Record<string, any>,
136
+ basePath: string[],
137
+ result: Set<string>
138
+ ): void {
139
+ for (const [key, value] of typedEntries(obj)) {
140
+ const keyParts = key.split(".");
141
+ const fullPath = [...basePath, ...keyParts];
142
+
143
+ // Check for catalog entry at payments.catalogs.<catalogId>
144
+ if (fullPath.length >= 3 && fullPath[0] === "payments" && fullPath[1] === "catalogs") {
145
+ const catalogId = fullPath[2];
146
+ result.add(catalogId);
147
+ }
148
+
149
+ if (isObjectLike(value)) {
150
+ findAllCatalogIds(value, fullPath, result);
151
+ }
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Removes a catalog from the config in any format (nested or flat).
157
+ */
158
+ function removeCatalog(obj: Record<string, any>, catalogId: string): Record<string, any> {
159
+ // Don't process arrays - they should be copied as-is
160
+ if (Array.isArray(obj)) {
161
+ return obj;
162
+ }
163
+
164
+ const res: Record<string, any> = {};
165
+
166
+ for (const [key, value] of typedEntries(obj)) {
167
+ const keyParts = key.split(".");
168
+
169
+ // Check if this key is for the catalog we want to remove
170
+ // Format 4: "payments.catalogs.<catalogId>.displayName" or similar flat keys
171
+ if (keyParts.length >= 3 && keyParts[0] === "payments" && keyParts[1] === "catalogs" && keyParts[2] === catalogId) {
172
+ // Skip this key - don't copy it
173
+ continue;
174
+ }
175
+
176
+ // Format 3: "payments.catalogs.<catalogId>" as a single key
177
+ if (key === `payments.catalogs.${catalogId}`) {
178
+ continue;
179
+ }
180
+
181
+ if (Array.isArray(value)) {
182
+ res[key] = value;
183
+ } else if (isObjectLike(value)) {
184
+ const processed = removeCatalog(value, catalogId);
185
+ // Check if we need to remove the catalog from nested structures
186
+ if (key === "payments" && isObjectLike(processed) && "catalogs" in processed) {
187
+ const catalogs = processed.catalogs;
188
+ if (isObjectLike(catalogs) && catalogId in catalogs) {
189
+ const { [catalogId]: _, ...rest } = catalogs as Record<string, any>;
190
+ res[key] = { ...processed, catalogs: rest };
191
+ continue;
192
+ }
193
+ }
194
+ // Format 2: "payments.catalogs" as a key with nested catalog
195
+ if (key === "payments.catalogs" && isObjectLike(processed) && catalogId in processed) {
196
+ const { [catalogId]: _, ...rest } = processed as Record<string, any>;
197
+ res[key] = rest;
198
+ continue;
199
+ }
200
+ res[key] = processed;
201
+ } else {
202
+ res[key] = value;
203
+ }
204
+ }
205
+
206
+ return res;
207
+ }
208
+
209
+ /**
210
+ * Adds customerType to a catalog in the appropriate format.
211
+ */
212
+ function addCustomerTypeToCatalog(obj: Record<string, any>, catalogId: string, customerType: string): Record<string, any> {
213
+ // Don't process arrays - they should be copied as-is
214
+ if (Array.isArray(obj)) {
215
+ return obj;
216
+ }
217
+
218
+ const res: Record<string, any> = {};
219
+
220
+ // First, copy all existing properties
221
+ for (const [key, value] of typedEntries(obj)) {
222
+ if (Array.isArray(value)) {
223
+ // Copy arrays directly without processing
224
+ res[key] = value;
225
+ } else if (isObjectLike(value)) {
226
+ res[key] = addCustomerTypeToCatalog(value, catalogId, customerType);
227
+ } else {
228
+ res[key] = value;
229
+ }
230
+ }
231
+
232
+ // Check what format the catalog uses
233
+ // Format 1: { payments: { catalogs: { [catalogId]: { ... } } } }
234
+ const payments = res.payments;
235
+ if (payments && isObjectLike(payments) && !Array.isArray(payments) && typeof payments !== "function") {
236
+ const paymentsObj = payments as Record<string, any>;
237
+ const catalogs = paymentsObj.catalogs;
238
+ if (catalogs && isObjectLike(catalogs) && !Array.isArray(catalogs) && typeof catalogs !== "function") {
239
+ const catalogsObj = catalogs as Record<string, any>;
240
+ const catalog = catalogsObj[catalogId];
241
+ if (catalog && isObjectLike(catalog)) {
242
+ if (!("customerType" in catalog)) {
243
+ catalogsObj[catalogId] = { ...catalog, customerType };
244
+ }
245
+ return res;
246
+ }
247
+ }
248
+ }
249
+
250
+ // Format 2: { "payments.catalogs": { [catalogId]: { ... } } }
251
+ const paymentsCatalogs = res["payments.catalogs"];
252
+ if (paymentsCatalogs && isObjectLike(paymentsCatalogs) && !Array.isArray(paymentsCatalogs) && typeof paymentsCatalogs !== "function") {
253
+ const catalogs = paymentsCatalogs as Record<string, any>;
254
+ if (catalogId in catalogs && isObjectLike(catalogs[catalogId])) {
255
+ if (!("customerType" in catalogs[catalogId])) {
256
+ catalogs[catalogId] = { ...catalogs[catalogId], customerType };
257
+ }
258
+ return res;
259
+ }
260
+ }
261
+
262
+ // Format 3: { "payments.catalogs.[catalogId]": { ... } }
263
+ const catalogKey = `payments.catalogs.${catalogId}`;
264
+ if (catalogKey in res && isObjectLike(res[catalogKey])) {
265
+ if (!("customerType" in res[catalogKey])) {
266
+ res[catalogKey] = { ...res[catalogKey], customerType };
267
+ }
268
+ return res;
269
+ }
270
+
271
+ // Format 4: { "payments.catalogs.[catalogId].displayName": "..." }
272
+ // Need to add a new key for customerType
273
+ const customerTypeKey = `payments.catalogs.${catalogId}.customerType`;
274
+ for (const key of Object.keys(res)) {
275
+ if (key.startsWith(`payments.catalogs.${catalogId}.`) && key !== customerTypeKey) {
276
+ // Found a flat key for this catalog, add customerType in same format
277
+ res[customerTypeKey] = customerType;
278
+ return res;
279
+ }
280
+ }
281
+
282
+ return res;
283
+ }
284
+
285
+ // Tests
286
+ import.meta.vitest?.test("migrateCatalogsToProductLines - basic migrations", ({ expect }) => {
287
+ // Basic nested format
288
+ expect(migrateCatalogsToProductLines({
289
+ payments: {
290
+ catalogs: {
291
+ myCatalog: { displayName: "My Catalog" }
292
+ },
293
+ products: {
294
+ myProduct: { catalogId: "myCatalog", customerType: "user", prices: {} }
295
+ }
296
+ }
297
+ })).toEqual({
298
+ payments: {
299
+ productLines: {
300
+ myCatalog: { displayName: "My Catalog", customerType: "user" }
301
+ },
302
+ products: {
303
+ myProduct: { productLineId: "myCatalog", customerType: "user", prices: {} }
304
+ }
305
+ }
306
+ });
307
+
308
+ // Flat format
309
+ expect(migrateCatalogsToProductLines({
310
+ "payments.catalogs.myCatalog": { displayName: "My Catalog" },
311
+ "payments.products.myProduct.catalogId": "myCatalog",
312
+ "payments.products.myProduct.customerType": "user",
313
+ })).toEqual({
314
+ "payments.productLines.myCatalog": { displayName: "My Catalog", customerType: "user" },
315
+ "payments.products.myProduct.productLineId": "myCatalog",
316
+ "payments.products.myProduct.customerType": "user",
317
+ });
318
+
319
+ // Mixed format
320
+ expect(migrateCatalogsToProductLines({
321
+ payments: {
322
+ catalogs: { myCatalog: { displayName: "My Catalog" } }
323
+ },
324
+ "payments.products.myProduct": { catalogId: "myCatalog", customerType: "user" }
325
+ })).toEqual({
326
+ payments: {
327
+ productLines: { myCatalog: { displayName: "My Catalog", customerType: "user" } }
328
+ },
329
+ "payments.products.myProduct": { productLineId: "myCatalog", customerType: "user" }
330
+ });
331
+ });
332
+
333
+ import.meta.vitest?.test("migrateCatalogsToProductLines - does not overwrite existing customerType", ({ expect }) => {
334
+ // If catalog already has customerType, don't overwrite it
335
+ expect(migrateCatalogsToProductLines({
336
+ payments: {
337
+ catalogs: {
338
+ myCatalog: { displayName: "My Catalog", customerType: "team" }
339
+ },
340
+ products: {
341
+ myProduct: { catalogId: "myCatalog", customerType: "user", prices: {} }
342
+ }
343
+ }
344
+ })).toEqual({
345
+ payments: {
346
+ productLines: {
347
+ myCatalog: { displayName: "My Catalog", customerType: "team" }
348
+ },
349
+ products: {
350
+ myProduct: { productLineId: "myCatalog", customerType: "user", prices: {} }
351
+ }
352
+ }
353
+ });
354
+ });
355
+
356
+ import.meta.vitest?.test("migrateCatalogsToProductLines - multiple catalogs and products", ({ expect }) => {
357
+ expect(migrateCatalogsToProductLines({
358
+ payments: {
359
+ catalogs: {
360
+ userPlans: { displayName: "User Plans" },
361
+ teamPlans: { displayName: "Team Plans" }
362
+ },
363
+ products: {
364
+ userBasic: { catalogId: "userPlans", customerType: "user", prices: {} },
365
+ userPro: { catalogId: "userPlans", customerType: "user", prices: {} },
366
+ teamBasic: { catalogId: "teamPlans", customerType: "team", prices: {} }
367
+ }
368
+ }
369
+ })).toEqual({
370
+ payments: {
371
+ productLines: {
372
+ userPlans: { displayName: "User Plans", customerType: "user" },
373
+ teamPlans: { displayName: "Team Plans", customerType: "team" }
374
+ },
375
+ products: {
376
+ userBasic: { productLineId: "userPlans", customerType: "user", prices: {} },
377
+ userPro: { productLineId: "userPlans", customerType: "user", prices: {} },
378
+ teamBasic: { productLineId: "teamPlans", customerType: "team", prices: {} }
379
+ }
380
+ }
381
+ });
382
+ });
383
+
384
+ import.meta.vitest?.test("migrateCatalogsToProductLines - catalog without products is removed", ({ expect }) => {
385
+ // Catalog without any products should be removed since we can't infer customerType
386
+ expect(migrateCatalogsToProductLines({
387
+ payments: {
388
+ catalogs: {
389
+ emptyCatalog: { displayName: "Empty" }
390
+ },
391
+ products: {}
392
+ }
393
+ })).toEqual({
394
+ payments: {
395
+ productLines: {},
396
+ products: {}
397
+ }
398
+ });
399
+ });
400
+
401
+ import.meta.vitest?.test("migrateCatalogsToProductLines - products without catalogId", ({ expect }) => {
402
+ // Catalogs without any products referencing them should be removed
403
+ expect(migrateCatalogsToProductLines({
404
+ payments: {
405
+ catalogs: {
406
+ myCatalog: { displayName: "My Catalog" }
407
+ },
408
+ products: {
409
+ standalone: { customerType: "user", prices: {} }
410
+ }
411
+ }
412
+ })).toEqual({
413
+ payments: {
414
+ productLines: {},
415
+ products: {
416
+ standalone: { customerType: "user", prices: {} }
417
+ }
418
+ }
419
+ });
420
+ });
421
+
422
+ import.meta.vitest?.test("migrateCatalogsToProductLines - deeply nested flat format", ({ expect }) => {
423
+ expect(migrateCatalogsToProductLines({
424
+ "payments.catalogs.myCatalog.displayName": "My Catalog",
425
+ "payments.products.myProduct.catalogId": "myCatalog",
426
+ "payments.products.myProduct.customerType": "user",
427
+ "payments.products.myProduct.prices": {},
428
+ })).toEqual({
429
+ "payments.productLines.myCatalog.displayName": "My Catalog",
430
+ "payments.productLines.myCatalog.customerType": "user",
431
+ "payments.products.myProduct.productLineId": "myCatalog",
432
+ "payments.products.myProduct.customerType": "user",
433
+ "payments.products.myProduct.prices": {},
434
+ });
435
+ });
436
+
437
+ import.meta.vitest?.test("migrateCatalogsToProductLines - no catalogs", ({ expect }) => {
438
+ // Config without catalogs should pass through unchanged (except productLineId rename)
439
+ expect(migrateCatalogsToProductLines({
440
+ payments: {
441
+ products: {
442
+ myProduct: { catalogId: "someCatalog", customerType: "user" }
443
+ }
444
+ }
445
+ })).toEqual({
446
+ payments: {
447
+ products: {
448
+ myProduct: { productLineId: "someCatalog", customerType: "user" }
449
+ }
450
+ }
451
+ });
452
+ });
453
+
454
+ import.meta.vitest?.test("migrateCatalogsToProductLines - already migrated (productLines)", ({ expect }) => {
455
+ // Already has productLines, should not change customerType
456
+ expect(migrateCatalogsToProductLines({
457
+ payments: {
458
+ productLines: {
459
+ myLine: { displayName: "My Line", customerType: "team" }
460
+ },
461
+ products: {
462
+ myProduct: { productLineId: "myLine", customerType: "team" }
463
+ }
464
+ }
465
+ })).toEqual({
466
+ payments: {
467
+ productLines: {
468
+ myLine: { displayName: "My Line", customerType: "team" }
469
+ },
470
+ products: {
471
+ myProduct: { productLineId: "myLine", customerType: "team" }
472
+ }
473
+ }
474
+ });
475
+ });