rune-lab 0.4.0 → 0.4.1

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 (433) hide show
  1. package/.gitignore +12 -0
  2. package/README.md +3 -3
  3. package/deno.json +50 -0
  4. package/justfile +5 -0
  5. package/package.json +2 -79
  6. package/scripts/build_pkg.ts +51 -0
  7. package/scripts/ci.just +26 -0
  8. package/scripts/deploy.just +21 -0
  9. package/scripts/dev.just +4 -0
  10. package/scripts/inject.just +72 -0
  11. package/src/RuneProvider.svelte +105 -0
  12. package/src/i18n/message-resolver.test.ts +103 -0
  13. package/src/i18n/message-resolver.ts +89 -0
  14. package/src/i18n/project.inlang/settings.json +26 -0
  15. package/src/i18n/translations/ar.json +89 -0
  16. package/src/i18n/translations/de.json +89 -0
  17. package/src/i18n/translations/en.json +89 -0
  18. package/src/i18n/translations/es.json +89 -0
  19. package/src/i18n/translations/fr.json +89 -0
  20. package/src/i18n/translations/hi.json +89 -0
  21. package/src/i18n/translations/it.json +89 -0
  22. package/src/i18n/translations/ja.json +89 -0
  23. package/src/i18n/translations/ko.json +89 -0
  24. package/src/i18n/translations/pt.json +89 -0
  25. package/src/i18n/translations/ru.json +89 -0
  26. package/src/i18n/translations/vi.json +89 -0
  27. package/src/i18n/translations/zh.json +89 -0
  28. package/src/kernel/deno.json +5 -0
  29. package/src/kernel/src/actions/portal.ts +41 -0
  30. package/src/kernel/src/actions/shortcut-listener.ts +50 -0
  31. package/src/kernel/src/context/app.svelte.ts +98 -0
  32. package/src/kernel/src/context/context.ts +31 -0
  33. package/src/kernel/src/context/stores.svelte.ts +96 -0
  34. package/src/kernel/src/context/types.ts +110 -0
  35. package/src/kernel/src/context/useRuneLab.ts +38 -0
  36. package/src/kernel/src/mod.ts +13 -0
  37. package/src/kernel/src/persistence/createConfigStore.svelte.ts +120 -0
  38. package/src/kernel/src/persistence/drivers.test.ts +95 -0
  39. package/src/kernel/src/persistence/drivers.ts +77 -0
  40. package/src/kernel/src/persistence/provider.test.ts +58 -0
  41. package/{dist/state/persistence/provider.js → src/kernel/src/persistence/provider.ts} +29 -14
  42. package/src/kernel/src/persistence/types.ts +19 -0
  43. package/src/kernel/src/persistence/usePersistence.ts +9 -0
  44. package/src/kernel/src/registry/mod.ts +221 -0
  45. package/src/kernel/src/registry/registry.test.ts +162 -0
  46. package/src/kernel/src/tokens/props.ts +94 -0
  47. package/src/mod.ts +10 -0
  48. package/src/runes/layout/deno.json +5 -0
  49. package/src/runes/layout/src/APP_CONFIGURATIONS.ts +56 -0
  50. package/{dist/ui/features/config → src/runes/layout/src}/AppSettingSelector.svelte +1 -1
  51. package/{dist/ui/layout → src/runes/layout/src}/ConnectedNavigationPanel.svelte +1 -1
  52. package/{dist/ui/layout → src/runes/layout/src}/ConnectedWorkspaceStrip.svelte +1 -1
  53. package/src/runes/layout/src/Icon.svelte +7 -0
  54. package/{dist/ui/features/config → src/runes/layout/src}/LanguageSelector.svelte +7 -5
  55. package/{dist/ui/layout → src/runes/layout/src}/NavigationPanel.svelte +1 -4
  56. package/{dist/ui/features/config → src/runes/layout/src}/ResourceSelector.svelte +3 -5
  57. package/{dist/ui/features/config → src/runes/layout/src}/ThemeSelector.svelte +2 -2
  58. package/{dist/ui/layout → src/runes/layout/src}/WorkspaceLayout.svelte +6 -4
  59. package/{dist/ui/layout → src/runes/layout/src}/WorkspaceStrip.svelte +1 -1
  60. package/src/runes/layout/src/connection-factory.ts +95 -0
  61. package/src/runes/layout/src/language.svelte.ts +44 -0
  62. package/src/runes/layout/src/mod.ts +71 -0
  63. package/src/runes/layout/src/store.svelte.ts +142 -0
  64. package/src/runes/layout/src/theme.svelte.ts +73 -0
  65. package/src/runes/layout/src/types.ts +11 -0
  66. package/src/runes/palettes/deno.json +8 -0
  67. package/{dist/ui/features/command-palette → src/runes/palettes/src/commands}/CommandPalette.svelte +4 -4
  68. package/src/runes/palettes/src/commands/mod.ts +2 -0
  69. package/src/runes/palettes/src/commands/store.svelte.ts +80 -0
  70. package/src/runes/palettes/src/mod.ts +51 -0
  71. package/{dist/ui/components → src/runes/palettes/src/notifications}/Toaster.svelte +1 -2
  72. package/src/runes/palettes/src/notifications/bridge.ts +56 -0
  73. package/src/runes/palettes/src/notifications/mod.ts +4 -0
  74. package/src/runes/palettes/src/notifications/store.svelte.ts +58 -0
  75. package/{dist/ui/features → src/runes/palettes/src}/shortcuts/ShortcutPalette.svelte +4 -4
  76. package/src/runes/palettes/src/shortcuts/mod.ts +4 -0
  77. package/src/runes/palettes/src/shortcuts/store.svelte.ts +85 -0
  78. package/src/runes/palettes/src/shortcuts/types.ts +4 -0
  79. package/src/runes/palettes/src/shortcuts/useShortcuts.ts +85 -0
  80. package/src/runes/plugins/money/deno.json +8 -0
  81. package/{dist/ui/features/config → src/runes/plugins/money/src}/CurrencySelector.svelte +2 -2
  82. package/{dist/ui/components/money → src/runes/plugins/money/src}/MoneyDisplay.svelte +2 -2
  83. package/{dist/ui/components/money → src/runes/plugins/money/src}/MoneyInput.svelte +4 -8
  84. package/src/runes/plugins/money/src/currency.svelte.ts +95 -0
  85. package/src/runes/plugins/money/src/currency.test.ts +40 -0
  86. package/src/runes/plugins/money/src/exchange-rate.svelte.ts +176 -0
  87. package/src/runes/plugins/money/src/exchange-rate.test.ts +95 -0
  88. package/src/runes/plugins/money/src/mod.ts +79 -0
  89. package/src/runes/plugins/money/src/money-primitive.test.ts +217 -0
  90. package/src/runes/plugins/money/src/money-primitive.ts +220 -0
  91. package/src/runes/plugins/money/src/money.test.ts +183 -0
  92. package/src/runes/plugins/money/src/money.ts +382 -0
  93. package/src/runes/plugins/money/src/strategies.test.ts +143 -0
  94. package/{dist/core/exchange-rate/strategies.js → src/runes/plugins/money/src/strategies.ts} +45 -22
  95. package/src/runes/plugins/money/src/types.ts +27 -0
  96. package/src/runes/plugins/money/src/useMoney.ts +261 -0
  97. package/src/runes/plugins/money/src/useMoneyFilter.ts +132 -0
  98. package/tsconfig.json +41 -0
  99. package/vite.config.ts +44 -0
  100. package/dist/core/design-tokens/props.d.ts +0 -52
  101. package/dist/core/design-tokens/props.d.ts.map +0 -1
  102. package/dist/core/design-tokens/props.js +0 -34
  103. package/dist/core/exchange-rate/strategies.d.ts +0 -52
  104. package/dist/core/exchange-rate/strategies.d.ts.map +0 -1
  105. package/dist/core/index.d.ts +0 -9
  106. package/dist/core/index.d.ts.map +0 -1
  107. package/dist/core/index.js +0 -8
  108. package/dist/core/internal/message-resolver.d.ts +0 -42
  109. package/dist/core/internal/message-resolver.d.ts.map +0 -1
  110. package/dist/core/internal/message-resolver.js +0 -47
  111. package/dist/core/layout/types.d.ts +0 -60
  112. package/dist/core/layout/types.d.ts.map +0 -1
  113. package/dist/core/layout/types.js +0 -4
  114. package/dist/core/money/index.d.ts +0 -2
  115. package/dist/core/money/index.d.ts.map +0 -1
  116. package/dist/core/money/index.js +0 -2
  117. package/dist/core/money/money-primitive.d.ts +0 -101
  118. package/dist/core/money/money-primitive.d.ts.map +0 -1
  119. package/dist/core/money/money-primitive.js +0 -161
  120. package/dist/core/money/money.d.ts +0 -129
  121. package/dist/core/money/money.d.ts.map +0 -1
  122. package/dist/core/money/money.js +0 -250
  123. package/dist/core/persistence/types.d.ts +0 -18
  124. package/dist/core/persistence/types.d.ts.map +0 -1
  125. package/dist/core/persistence/types.js +0 -2
  126. package/dist/core/shortcuts/types.d.ts +0 -60
  127. package/dist/core/shortcuts/types.d.ts.map +0 -1
  128. package/dist/core/shortcuts/types.js +0 -4
  129. package/dist/index.d.ts +0 -6
  130. package/dist/index.js +0 -7
  131. package/dist/state/api.svelte.d.ts +0 -26
  132. package/dist/state/api.svelte.js +0 -65
  133. package/dist/state/app.svelte.d.ts +0 -48
  134. package/dist/state/app.svelte.js +0 -82
  135. package/dist/state/auth/index.d.ts +0 -2
  136. package/dist/state/auth/index.js +0 -2
  137. package/dist/state/auth/session.svelte.d.ts +0 -40
  138. package/dist/state/auth/session.svelte.js +0 -59
  139. package/dist/state/auth/types.d.ts +0 -30
  140. package/dist/state/auth/types.js +0 -3
  141. package/dist/state/cart.svelte.d.ts +0 -64
  142. package/dist/state/cart.svelte.js +0 -122
  143. package/dist/state/commands.svelte.d.ts +0 -46
  144. package/dist/state/commands.svelte.js +0 -62
  145. package/dist/state/composables/useMoney.d.ts +0 -31
  146. package/dist/state/composables/useMoney.js +0 -126
  147. package/dist/state/composables/useMoneyFilter.d.ts +0 -20
  148. package/dist/state/composables/useMoneyFilter.js +0 -81
  149. package/dist/state/composables/usePersistence.d.ts +0 -2
  150. package/dist/state/composables/usePersistence.js +0 -5
  151. package/dist/state/composables/useRuneLab.d.ts +0 -19
  152. package/dist/state/composables/useRuneLab.js +0 -30
  153. package/dist/state/composables/useShortcuts.d.ts +0 -33
  154. package/dist/state/composables/useShortcuts.js +0 -75
  155. package/dist/state/context.d.ts +0 -15
  156. package/dist/state/context.js +0 -16
  157. package/dist/state/createConfigStore.svelte.d.ts +0 -28
  158. package/dist/state/createConfigStore.svelte.js +0 -72
  159. package/dist/state/currency.svelte.d.ts +0 -32
  160. package/dist/state/currency.svelte.js +0 -96
  161. package/dist/state/currency.test.d.ts +0 -1
  162. package/dist/state/currency.test.js +0 -35
  163. package/dist/state/exchange-rate.svelte.d.ts +0 -43
  164. package/dist/state/exchange-rate.svelte.js +0 -145
  165. package/dist/state/exchange-rate.test.d.ts +0 -1
  166. package/dist/state/exchange-rate.test.js +0 -75
  167. package/dist/state/index.d.ts +0 -26
  168. package/dist/state/index.js +0 -32
  169. package/dist/state/language.svelte.d.ts +0 -57
  170. package/dist/state/language.svelte.js +0 -43
  171. package/dist/state/layout.svelte.d.ts +0 -49
  172. package/dist/state/layout.svelte.js +0 -108
  173. package/dist/state/persistence/drivers.d.ts +0 -12
  174. package/dist/state/persistence/drivers.js +0 -75
  175. package/dist/state/persistence/drivers.test.d.ts +0 -1
  176. package/dist/state/persistence/drivers.test.js +0 -79
  177. package/dist/state/persistence/provider.d.ts +0 -23
  178. package/dist/state/persistence/provider.test.d.ts +0 -1
  179. package/dist/state/persistence/provider.test.js +0 -51
  180. package/dist/state/registry/index.d.ts +0 -44
  181. package/dist/state/registry/index.js +0 -58
  182. package/dist/state/registry/registry.test.d.ts +0 -1
  183. package/dist/state/registry/registry.test.js +0 -112
  184. package/dist/state/registry/types.d.ts +0 -20
  185. package/dist/state/registry/types.js +0 -3
  186. package/dist/state/shortcuts.svelte.d.ts +0 -121
  187. package/dist/state/shortcuts.svelte.js +0 -222
  188. package/dist/state/theme.svelte.d.ts +0 -15
  189. package/dist/state/theme.svelte.js +0 -92
  190. package/dist/state/toast-bridge.d.ts +0 -29
  191. package/dist/state/toast-bridge.js +0 -43
  192. package/dist/state/toast.svelte.d.ts +0 -27
  193. package/dist/state/toast.svelte.js +0 -43
  194. package/dist/ui/actions/portal.d.ts +0 -7
  195. package/dist/ui/actions/portal.js +0 -32
  196. package/dist/ui/components/ApiMonitor.svelte +0 -162
  197. package/dist/ui/components/ApiMonitor.svelte.d.ts +0 -3
  198. package/dist/ui/components/Icon.svelte +0 -108
  199. package/dist/ui/components/Icon.svelte.d.ts +0 -14
  200. package/dist/ui/components/Kyntharil.svelte +0 -48
  201. package/dist/ui/components/Kyntharil.svelte.d.ts +0 -6
  202. package/dist/ui/components/RuneProvider.svelte +0 -218
  203. package/dist/ui/components/RuneProvider.svelte.d.ts +0 -49
  204. package/dist/ui/components/Toaster.svelte.d.ts +0 -18
  205. package/dist/ui/components/money/MoneyDisplay.svelte.d.ts +0 -33
  206. package/dist/ui/components/money/MoneyDisplay.svelte.test.d.ts +0 -1
  207. package/dist/ui/components/money/MoneyDisplay.svelte.test.js +0 -117
  208. package/dist/ui/components/money/MoneyInput.svelte.d.ts +0 -34
  209. package/dist/ui/components/money/index.d.ts +0 -2
  210. package/dist/ui/components/money/index.js +0 -3
  211. package/dist/ui/components/user/UserAvatar.svelte +0 -66
  212. package/dist/ui/components/user/UserAvatar.svelte.d.ts +0 -13
  213. package/dist/ui/components/user/UserProfile.svelte +0 -79
  214. package/dist/ui/components/user/UserProfile.svelte.d.ts +0 -17
  215. package/dist/ui/components/user/index.d.ts +0 -2
  216. package/dist/ui/components/user/index.js +0 -3
  217. package/dist/ui/features/command-palette/CommandPalette.svelte.d.ts +0 -6
  218. package/dist/ui/features/config/APP_CONFIGURATIONS.d.ts +0 -29
  219. package/dist/ui/features/config/APP_CONFIGURATIONS.js +0 -38
  220. package/dist/ui/features/config/AppSettingSelector.svelte.d.ts +0 -13
  221. package/dist/ui/features/config/CurrencySelector.svelte.d.ts +0 -8
  222. package/dist/ui/features/config/LanguageSelector.svelte.d.ts +0 -8
  223. package/dist/ui/features/config/ResourceSelector.svelte.d.ts +0 -25
  224. package/dist/ui/features/config/ThemeSelector.svelte.d.ts +0 -8
  225. package/dist/ui/features/notifications/NotificationBell.svelte.d.ts +0 -11
  226. package/dist/ui/features/notifications/index.d.ts +0 -1
  227. package/dist/ui/features/notifications/index.js +0 -2
  228. package/dist/ui/features/shortcuts/ShortcutBinder.svelte +0 -17
  229. package/dist/ui/features/shortcuts/ShortcutBinder.svelte.d.ts +0 -7
  230. package/dist/ui/features/shortcuts/ShortcutPalette.svelte.d.ts +0 -6
  231. package/dist/ui/index.d.ts +0 -30
  232. package/dist/ui/index.js +0 -46
  233. package/dist/ui/layout/ConnectedNavigationPanel.svelte.d.ts +0 -10
  234. package/dist/ui/layout/ConnectedWorkspaceStrip.svelte.d.ts +0 -9
  235. package/dist/ui/layout/ContentArea.svelte.d.ts +0 -8
  236. package/dist/ui/layout/DetailPanel.svelte.d.ts +0 -7
  237. package/dist/ui/layout/NavigationPanel.svelte.d.ts +0 -14
  238. package/dist/ui/layout/WorkspaceLayout.svelte.d.ts +0 -32
  239. package/dist/ui/layout/WorkspaceStrip.svelte.d.ts +0 -11
  240. package/dist/ui/layout/connection-factory.d.ts +0 -50
  241. package/dist/ui/layout/connection-factory.js +0 -58
  242. package/dist/ui/layout/index.d.ts +0 -7
  243. package/dist/ui/layout/index.js +0 -7
  244. package/dist/ui/paraglide/README.md +0 -147
  245. package/dist/ui/paraglide/messages/_index.d.ts +0 -87
  246. package/dist/ui/paraglide/messages/_index.js +0 -88
  247. package/dist/ui/paraglide/messages/abyss.d.ts +0 -16
  248. package/dist/ui/paraglide/messages/abyss.js +0 -84
  249. package/dist/ui/paraglide/messages/acid.d.ts +0 -16
  250. package/dist/ui/paraglide/messages/acid.js +0 -84
  251. package/dist/ui/paraglide/messages/active_toasts.d.ts +0 -16
  252. package/dist/ui/paraglide/messages/active_toasts.js +0 -84
  253. package/dist/ui/paraglide/messages/aed3.d.ts +0 -17
  254. package/dist/ui/paraglide/messages/aed3.js +0 -85
  255. package/dist/ui/paraglide/messages/all_currencies.d.ts +0 -16
  256. package/dist/ui/paraglide/messages/all_currencies.js +0 -84
  257. package/dist/ui/paraglide/messages/all_languages.d.ts +0 -16
  258. package/dist/ui/paraglide/messages/all_languages.js +0 -84
  259. package/dist/ui/paraglide/messages/all_themes.d.ts +0 -16
  260. package/dist/ui/paraglide/messages/all_themes.js +0 -84
  261. package/dist/ui/paraglide/messages/api_status.d.ts +0 -16
  262. package/dist/ui/paraglide/messages/api_status.js +0 -84
  263. package/dist/ui/paraglide/messages/app_info.d.ts +0 -16
  264. package/dist/ui/paraglide/messages/app_info.js +0 -84
  265. package/dist/ui/paraglide/messages/appearance.d.ts +0 -16
  266. package/dist/ui/paraglide/messages/appearance.js +0 -84
  267. package/dist/ui/paraglide/messages/aqua.d.ts +0 -16
  268. package/dist/ui/paraglide/messages/aqua.js +0 -84
  269. package/dist/ui/paraglide/messages/ar.d.ts +0 -16
  270. package/dist/ui/paraglide/messages/ar.js +0 -84
  271. package/dist/ui/paraglide/messages/autumn.d.ts +0 -16
  272. package/dist/ui/paraglide/messages/autumn.js +0 -84
  273. package/dist/ui/paraglide/messages/black.d.ts +0 -16
  274. package/dist/ui/paraglide/messages/black.js +0 -84
  275. package/dist/ui/paraglide/messages/brl3.d.ts +0 -17
  276. package/dist/ui/paraglide/messages/brl3.js +0 -85
  277. package/dist/ui/paraglide/messages/bumblebee.d.ts +0 -16
  278. package/dist/ui/paraglide/messages/bumblebee.js +0 -84
  279. package/dist/ui/paraglide/messages/business.d.ts +0 -16
  280. package/dist/ui/paraglide/messages/business.js +0 -84
  281. package/dist/ui/paraglide/messages/cad3.d.ts +0 -17
  282. package/dist/ui/paraglide/messages/cad3.js +0 -85
  283. package/dist/ui/paraglide/messages/caramellatte.d.ts +0 -16
  284. package/dist/ui/paraglide/messages/caramellatte.js +0 -84
  285. package/dist/ui/paraglide/messages/cmyk.d.ts +0 -16
  286. package/dist/ui/paraglide/messages/cmyk.js +0 -84
  287. package/dist/ui/paraglide/messages/cny3.d.ts +0 -17
  288. package/dist/ui/paraglide/messages/cny3.js +0 -85
  289. package/dist/ui/paraglide/messages/coffee.d.ts +0 -16
  290. package/dist/ui/paraglide/messages/coffee.js +0 -84
  291. package/dist/ui/paraglide/messages/commands_label.d.ts +0 -16
  292. package/dist/ui/paraglide/messages/commands_label.js +0 -84
  293. package/dist/ui/paraglide/messages/corporate.d.ts +0 -16
  294. package/dist/ui/paraglide/messages/corporate.js +0 -84
  295. package/dist/ui/paraglide/messages/cupcake.d.ts +0 -16
  296. package/dist/ui/paraglide/messages/cupcake.js +0 -84
  297. package/dist/ui/paraglide/messages/currency.d.ts +0 -16
  298. package/dist/ui/paraglide/messages/currency.js +0 -84
  299. package/dist/ui/paraglide/messages/current_currency.d.ts +0 -16
  300. package/dist/ui/paraglide/messages/current_currency.js +0 -84
  301. package/dist/ui/paraglide/messages/current_language.d.ts +0 -16
  302. package/dist/ui/paraglide/messages/current_language.js +0 -84
  303. package/dist/ui/paraglide/messages/current_theme.d.ts +0 -16
  304. package/dist/ui/paraglide/messages/current_theme.js +0 -84
  305. package/dist/ui/paraglide/messages/currently_in_queue.d.ts +0 -16
  306. package/dist/ui/paraglide/messages/currently_in_queue.js +0 -84
  307. package/dist/ui/paraglide/messages/cyberpunk.d.ts +0 -16
  308. package/dist/ui/paraglide/messages/cyberpunk.js +0 -84
  309. package/dist/ui/paraglide/messages/dark.d.ts +0 -16
  310. package/dist/ui/paraglide/messages/dark.js +0 -84
  311. package/dist/ui/paraglide/messages/de.d.ts +0 -16
  312. package/dist/ui/paraglide/messages/de.js +0 -84
  313. package/dist/ui/paraglide/messages/dim.d.ts +0 -16
  314. package/dist/ui/paraglide/messages/dim.js +0 -84
  315. package/dist/ui/paraglide/messages/dracula.d.ts +0 -16
  316. package/dist/ui/paraglide/messages/dracula.js +0 -84
  317. package/dist/ui/paraglide/messages/emerald.d.ts +0 -16
  318. package/dist/ui/paraglide/messages/emerald.js +0 -84
  319. package/dist/ui/paraglide/messages/en.d.ts +0 -16
  320. package/dist/ui/paraglide/messages/en.js +0 -84
  321. package/dist/ui/paraglide/messages/es.d.ts +0 -16
  322. package/dist/ui/paraglide/messages/es.js +0 -84
  323. package/dist/ui/paraglide/messages/eur3.d.ts +0 -17
  324. package/dist/ui/paraglide/messages/eur3.js +0 -85
  325. package/dist/ui/paraglide/messages/extended_controls.d.ts +0 -16
  326. package/dist/ui/paraglide/messages/extended_controls.js +0 -84
  327. package/dist/ui/paraglide/messages/fantasy.d.ts +0 -16
  328. package/dist/ui/paraglide/messages/fantasy.js +0 -84
  329. package/dist/ui/paraglide/messages/forest.d.ts +0 -16
  330. package/dist/ui/paraglide/messages/forest.js +0 -84
  331. package/dist/ui/paraglide/messages/fr.d.ts +0 -16
  332. package/dist/ui/paraglide/messages/fr.js +0 -84
  333. package/dist/ui/paraglide/messages/garden.d.ts +0 -16
  334. package/dist/ui/paraglide/messages/garden.js +0 -84
  335. package/dist/ui/paraglide/messages/gbp3.d.ts +0 -17
  336. package/dist/ui/paraglide/messages/gbp3.js +0 -85
  337. package/dist/ui/paraglide/messages/halloween.d.ts +0 -16
  338. package/dist/ui/paraglide/messages/halloween.js +0 -84
  339. package/dist/ui/paraglide/messages/hello_world.d.ts +0 -18
  340. package/dist/ui/paraglide/messages/hello_world.js +0 -84
  341. package/dist/ui/paraglide/messages/hi.d.ts +0 -16
  342. package/dist/ui/paraglide/messages/hi.js +0 -84
  343. package/dist/ui/paraglide/messages/inr3.d.ts +0 -17
  344. package/dist/ui/paraglide/messages/inr3.js +0 -85
  345. package/dist/ui/paraglide/messages/it.d.ts +0 -16
  346. package/dist/ui/paraglide/messages/it.js +0 -84
  347. package/dist/ui/paraglide/messages/ja.d.ts +0 -16
  348. package/dist/ui/paraglide/messages/ja.js +0 -84
  349. package/dist/ui/paraglide/messages/jpy3.d.ts +0 -17
  350. package/dist/ui/paraglide/messages/jpy3.js +0 -85
  351. package/dist/ui/paraglide/messages/ko.d.ts +0 -16
  352. package/dist/ui/paraglide/messages/ko.js +0 -84
  353. package/dist/ui/paraglide/messages/krw3.d.ts +0 -17
  354. package/dist/ui/paraglide/messages/krw3.js +0 -85
  355. package/dist/ui/paraglide/messages/languages.d.ts +0 -16
  356. package/dist/ui/paraglide/messages/languages.js +0 -84
  357. package/dist/ui/paraglide/messages/lemonade.d.ts +0 -16
  358. package/dist/ui/paraglide/messages/lemonade.js +0 -84
  359. package/dist/ui/paraglide/messages/light.d.ts +0 -16
  360. package/dist/ui/paraglide/messages/light.js +0 -84
  361. package/dist/ui/paraglide/messages/live_store_dashboard.d.ts +0 -16
  362. package/dist/ui/paraglide/messages/live_store_dashboard.js +0 -84
  363. package/dist/ui/paraglide/messages/localization.d.ts +0 -16
  364. package/dist/ui/paraglide/messages/localization.js +0 -84
  365. package/dist/ui/paraglide/messages/lofi.d.ts +0 -16
  366. package/dist/ui/paraglide/messages/lofi.js +0 -84
  367. package/dist/ui/paraglide/messages/luxury.d.ts +0 -16
  368. package/dist/ui/paraglide/messages/luxury.js +0 -84
  369. package/dist/ui/paraglide/messages/mxn3.d.ts +0 -17
  370. package/dist/ui/paraglide/messages/mxn3.js +0 -85
  371. package/dist/ui/paraglide/messages/name_label.d.ts +0 -16
  372. package/dist/ui/paraglide/messages/name_label.js +0 -84
  373. package/dist/ui/paraglide/messages/night.d.ts +0 -16
  374. package/dist/ui/paraglide/messages/night.js +0 -84
  375. package/dist/ui/paraglide/messages/nord.d.ts +0 -16
  376. package/dist/ui/paraglide/messages/nord.js +0 -84
  377. package/dist/ui/paraglide/messages/pastel.d.ts +0 -16
  378. package/dist/ui/paraglide/messages/pastel.js +0 -84
  379. package/dist/ui/paraglide/messages/pt.d.ts +0 -16
  380. package/dist/ui/paraglide/messages/pt.js +0 -84
  381. package/dist/ui/paraglide/messages/real_time_monitor_desc.d.ts +0 -16
  382. package/dist/ui/paraglide/messages/real_time_monitor_desc.js +0 -84
  383. package/dist/ui/paraglide/messages/registered_in_registry.d.ts +0 -16
  384. package/dist/ui/paraglide/messages/registered_in_registry.js +0 -84
  385. package/dist/ui/paraglide/messages/retro.d.ts +0 -16
  386. package/dist/ui/paraglide/messages/retro.js +0 -84
  387. package/dist/ui/paraglide/messages/ru.d.ts +0 -16
  388. package/dist/ui/paraglide/messages/ru.js +0 -84
  389. package/dist/ui/paraglide/messages/silk.d.ts +0 -16
  390. package/dist/ui/paraglide/messages/silk.js +0 -84
  391. package/dist/ui/paraglide/messages/state_label.d.ts +0 -16
  392. package/dist/ui/paraglide/messages/state_label.js +0 -84
  393. package/dist/ui/paraglide/messages/sunset.d.ts +0 -16
  394. package/dist/ui/paraglide/messages/sunset.js +0 -84
  395. package/dist/ui/paraglide/messages/synthwave.d.ts +0 -16
  396. package/dist/ui/paraglide/messages/synthwave.js +0 -84
  397. package/dist/ui/paraglide/messages/system.d.ts +0 -16
  398. package/dist/ui/paraglide/messages/system.js +0 -84
  399. package/dist/ui/paraglide/messages/theme.d.ts +0 -16
  400. package/dist/ui/paraglide/messages/theme.js +0 -84
  401. package/dist/ui/paraglide/messages/themes.d.ts +0 -16
  402. package/dist/ui/paraglide/messages/themes.js +0 -84
  403. package/dist/ui/paraglide/messages/url_label.d.ts +0 -16
  404. package/dist/ui/paraglide/messages/url_label.js +0 -84
  405. package/dist/ui/paraglide/messages/usd3.d.ts +0 -17
  406. package/dist/ui/paraglide/messages/usd3.js +0 -85
  407. package/dist/ui/paraglide/messages/valentine.d.ts +0 -16
  408. package/dist/ui/paraglide/messages/valentine.js +0 -84
  409. package/dist/ui/paraglide/messages/version_label.d.ts +0 -16
  410. package/dist/ui/paraglide/messages/version_label.js +0 -84
  411. package/dist/ui/paraglide/messages/vi.d.ts +0 -16
  412. package/dist/ui/paraglide/messages/vi.js +0 -84
  413. package/dist/ui/paraglide/messages/winter.d.ts +0 -16
  414. package/dist/ui/paraglide/messages/winter.js +0 -84
  415. package/dist/ui/paraglide/messages/wireframe.d.ts +0 -16
  416. package/dist/ui/paraglide/messages/wireframe.js +0 -84
  417. package/dist/ui/paraglide/messages/zh.d.ts +0 -16
  418. package/dist/ui/paraglide/messages/zh.js +0 -84
  419. package/dist/ui/paraglide/messages.d.ts +0 -2
  420. package/dist/ui/paraglide/messages.js +0 -4
  421. package/dist/ui/paraglide/registry.d.ts +0 -21
  422. package/dist/ui/paraglide/registry.js +0 -31
  423. package/dist/ui/paraglide/runtime.d.ts +0 -730
  424. package/dist/ui/paraglide/runtime.js +0 -1812
  425. package/dist/ui/paraglide/server.d.ts +0 -92
  426. package/dist/ui/paraglide/server.js +0 -239
  427. package/dist/ui/primitives/DatePicker.svelte +0 -257
  428. package/dist/ui/primitives/DatePicker.svelte.d.ts +0 -17
  429. package/dist/ui/primitives/index.d.ts +0 -1
  430. package/dist/ui/primitives/index.js +0 -3
  431. /package/{dist/ui/layout → src/runes/layout/src}/ContentArea.svelte +0 -0
  432. /package/{dist/ui/layout → src/runes/layout/src}/DetailPanel.svelte +0 -0
  433. /package/{dist/ui/features → src/runes/palettes/src}/notifications/NotificationBell.svelte +0 -0
@@ -0,0 +1,176 @@
1
+ // sdk/state/src/exchange-rate.svelte.ts
2
+
3
+ import { getContext } from "svelte";
4
+ import {
5
+ convertMoney,
6
+ createMoney,
7
+ CURRENCY_MAP,
8
+ directConversion,
9
+ inverseConversion,
10
+ type RateMap,
11
+ resolveRate,
12
+ type ScaledRate,
13
+ scaledRate,
14
+ toMoneySnapshot,
15
+ triangularConversion,
16
+ } from "@rune-lab/money";
17
+ import { RUNE_LAB_CONTEXT } from "@rune-lab/kernel";
18
+
19
+ export class ExchangeRateStore {
20
+ #rates = $state<RateMap>({});
21
+ #baseCurrency = $state<string>("USD");
22
+
23
+ /**
24
+ * The current exchange rates relative to the base currency.
25
+ */
26
+ get rates(): RateMap {
27
+ return this.#rates;
28
+ }
29
+
30
+ /**
31
+ * The base currency all rates are relative to.
32
+ */
33
+ get baseCurrency(): string {
34
+ return this.#baseCurrency;
35
+ }
36
+
37
+ /**
38
+ * Returns true if exchange rates have been loaded.
39
+ */
40
+ get hasRates(): boolean {
41
+ return Object.keys(this.#rates).length > 0;
42
+ }
43
+
44
+ /**
45
+ * Set exchange rates from a human-readable float map.
46
+ * @param base - The base currency code (e.g., "USD")
47
+ * @param rawRates - A map of currency codes to float rates (e.g., { MXN: 17.23 })
48
+ */
49
+ setRates(base: string, rawRates: Record<string, number>) {
50
+ const nextRates: RateMap = {};
51
+ for (const [code, rate] of Object.entries(rawRates)) {
52
+ nextRates[code] = scaledRate(rate, 4);
53
+ }
54
+ this.#baseCurrency = base;
55
+ this.#rates = nextRates;
56
+ }
57
+
58
+ /**
59
+ * Directly set pre-scaled rates.
60
+ */
61
+ setScaledRates(base: string, rates: RateMap) {
62
+ this.#baseCurrency = base;
63
+ this.#rates = rates;
64
+ }
65
+
66
+ /**
67
+ * Resolves the exchange rate between two currencies.
68
+ * Returns a ScaledRate representing how many units of toCode are in 1 unit of fromCode.
69
+ */
70
+ getRate(fromCode: string, toCode: string): ScaledRate | number | undefined {
71
+ if (fromCode === toCode) return 1;
72
+
73
+ const fromCurrency = CURRENCY_MAP[fromCode];
74
+ const toCurrency = CURRENCY_MAP[toCode];
75
+ if (!fromCurrency || !toCurrency) return undefined;
76
+
77
+ if (!this.hasRates) return undefined;
78
+
79
+ try {
80
+ // 1 unit of fromCode (major unit)
81
+ const oneUnit = Math.pow(
82
+ Array.isArray(fromCurrency.base)
83
+ ? fromCurrency.base[0]
84
+ : fromCurrency.base,
85
+ fromCurrency.exponent,
86
+ );
87
+
88
+ const currentRates = this.#rates;
89
+ const currentBase = this.#baseCurrency;
90
+
91
+ const sourceMoney = createMoney(oneUnit, fromCode);
92
+
93
+ let targetMoney;
94
+ if (fromCode === currentBase) {
95
+ // Direct: base → target
96
+ targetMoney = convertMoney(sourceMoney, toCode, currentRates);
97
+ } else if (toCode === currentBase) {
98
+ // Inverse: target → base
99
+ const rateToBase = currentRates[fromCode];
100
+ if (!rateToBase) return undefined;
101
+ const r = resolveRate(rateToBase);
102
+ targetMoney = convertMoney(sourceMoney, toCode, {
103
+ [toCode]: scaledRate(1 / r, 6),
104
+ });
105
+ } else {
106
+ // Triangular: source → base → target
107
+ const rateToBase = currentRates[fromCode];
108
+ const rateToTarget = currentRates[toCode];
109
+ if (!rateToBase || !rateToTarget) return undefined;
110
+
111
+ const r1 = resolveRate(rateToBase);
112
+ const r2 = resolveRate(rateToTarget);
113
+
114
+ targetMoney = convertMoney(sourceMoney, toCode, {
115
+ [toCode]: scaledRate(r2 / r1, 6),
116
+ });
117
+ }
118
+
119
+ const snapshot = toMoneySnapshot(targetMoney);
120
+ return {
121
+ amount: snapshot.amount,
122
+ scale: snapshot.scale,
123
+ };
124
+ } catch (_err) {
125
+ return undefined;
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Internal conversion logic that handles triangulation through base currency.
131
+ * Delegates all math to ConversionStrategies (directConversion, inverseConversion, triangularConversion).
132
+ */
133
+ convertAmount(amount: number, fromCode: string, toCode: string): number {
134
+ if (fromCode === toCode) return amount;
135
+ if (!this.hasRates) return amount;
136
+
137
+ // Direct: Base → Target
138
+ if (fromCode === this.#baseCurrency) {
139
+ const rateToTarget = this.#rates[toCode];
140
+ if (!rateToTarget) return amount;
141
+ return directConversion(amount, resolveRate(rateToTarget));
142
+ }
143
+
144
+ // Inverse: Target → Base
145
+ if (toCode === this.#baseCurrency) {
146
+ const rateFromSource = this.#rates[fromCode];
147
+ if (!rateFromSource) return amount;
148
+ return inverseConversion(amount, resolveRate(rateFromSource));
149
+ }
150
+
151
+ // Triangulation: From → Base → To
152
+ const rateFromSource = this.#rates[fromCode];
153
+ const rateToTarget = this.#rates[toCode];
154
+ if (!rateFromSource || !rateToTarget) return amount;
155
+
156
+ return triangularConversion(
157
+ amount,
158
+ resolveRate(rateFromSource),
159
+ resolveRate(rateToTarget),
160
+ );
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Factory to create the ExchangeRateStore.
166
+ */
167
+ export function createExchangeRateStore(): ExchangeRateStore {
168
+ return new ExchangeRateStore();
169
+ }
170
+
171
+ /**
172
+ * Consumer to retrieve the ExchangeRateStore from context.
173
+ */
174
+ export function getExchangeRateStore(): ExchangeRateStore {
175
+ return getContext<ExchangeRateStore>(RUNE_LAB_CONTEXT.exchangeRate);
176
+ }
@@ -0,0 +1,95 @@
1
+ import { describe, expect, it, vi } from "vite-plus/test";
2
+ import { ExchangeRateStore } from "./exchange-rate.svelte.ts";
3
+ import { currencyStore, setExchangeRateStore } from "./currency.svelte.ts";
4
+ import { inMemoryDriver, RUNE_LAB_CONTEXT } from "@rune-lab/kernel";
5
+ import { languageStore } from "../../../layout/src/language.svelte.ts";
6
+
7
+ // Mock useMoney since it uses getContext and other stores
8
+ vi.mock("./useMoney.ts", () => ({
9
+ useMoney: () => ({
10
+ format: (amount: number, code?: string, _unit?: unknown) =>
11
+ `${amount} ${code ?? "USD"}`,
12
+ }),
13
+ }));
14
+
15
+ describe("ExchangeRateStore", () => {
16
+ it("should initialize with default base", () => {
17
+ const store = new ExchangeRateStore();
18
+ expect(store.baseCurrency).toBe("USD");
19
+ expect(store.hasRates).toBe(false);
20
+ });
21
+
22
+ it("should set and retrieve rates", () => {
23
+ const store = new ExchangeRateStore();
24
+ store.setRates("USD", { MXN: 17.23, EUR: 0.91 });
25
+
26
+ expect(store.hasRates).toBe(true);
27
+ expect(store.baseCurrency).toBe("USD");
28
+
29
+ const rateToMxn = store.getRate("USD", "MXN");
30
+ expect(rateToMxn).toEqual({ amount: 17230000, scale: 6 });
31
+ });
32
+
33
+ it("should handle inverse rates", () => {
34
+ const store = new ExchangeRateStore();
35
+ store.setRates("USD", { MXN: 20 });
36
+
37
+ const rateToBase = store.getRate("MXN", "USD");
38
+ expect(rateToBase).toEqual({ amount: 5000000, scale: 8 });
39
+ });
40
+
41
+ it("should handle triangulation (cross-rates)", () => {
42
+ const store = new ExchangeRateStore();
43
+ store.setRates("USD", { MXN: 20, EUR: 0.5 });
44
+
45
+ const rateMxnToEur = store.getRate("MXN", "EUR");
46
+ expect(rateMxnToEur).toEqual({ amount: 2500000, scale: 8 });
47
+ });
48
+ });
49
+
50
+ describe("CurrencyStore Integration", () => {
51
+ it("should convert amount when ExchangeRateStore is wired", () => {
52
+ const exchangeRateStore = new ExchangeRateStore();
53
+ exchangeRateStore.setRates("USD", { MXN: 20 });
54
+
55
+ setExchangeRateStore(exchangeRateStore);
56
+
57
+ expect(currencyStore.canConvert).toBe(true);
58
+
59
+ const converted = currencyStore.convertAmount(10000, "USD", "MXN");
60
+ expect(converted).toBe(200000);
61
+ });
62
+
63
+ it("should return original amount if no rates available", () => {
64
+ const exchangeRateStore = new ExchangeRateStore();
65
+ setExchangeRateStore(exchangeRateStore);
66
+
67
+ expect(currencyStore.canConvert).toBe(false);
68
+
69
+ const converted = currencyStore.convertAmount(10000, "USD", "MXN");
70
+ expect(converted).toBe(10000);
71
+ });
72
+ });
73
+
74
+ describe("useMoneyFilter", () => {
75
+ // Simple test for logic, skipping full component mount for brevity
76
+ it("matches() should work with conversion", () => {
77
+ const exchangeRateStore = new ExchangeRateStore();
78
+ exchangeRateStore.setRates("USD", { MXN: 20 });
79
+
80
+ setExchangeRateStore(exchangeRateStore);
81
+
82
+ // Manual mock of context for the composable
83
+ // In a real test we'd use render() from @testing-library/svelte
84
+ const mockContext = new Map();
85
+ mockContext.set(RUNE_LAB_CONTEXT.currency, currencyStore);
86
+ mockContext.set(RUNE_LAB_CONTEXT.exchangeRate, exchangeRateStore);
87
+ mockContext.set(RUNE_LAB_CONTEXT.language, languageStore);
88
+
89
+ // We can't easily call useMoneyFilter outside component without more setup
90
+ // but we can verify the matches logic in CurrencyStore
91
+ const amountInMxn = 1000000; // 10,000 MXN
92
+ const amountInUsd = currencyStore.convertAmount(amountInMxn, "MXN", "USD");
93
+ expect(amountInUsd).toBe(50000); // 500 USD
94
+ });
95
+ });
@@ -0,0 +1,79 @@
1
+ import { defineRune, RUNE_LAB_CONTEXT } from "@rune-lab/kernel";
2
+ import type { PersistenceDriver, RunePlugin } from "@rune-lab/kernel";
3
+ import {
4
+ type Currency,
5
+ currencyStore,
6
+ setExchangeRateStore,
7
+ } from "./currency.svelte.ts";
8
+ import { createExchangeRateStore } from "./exchange-rate.svelte.ts";
9
+ import type { ExchangeRateStore } from "./exchange-rate.svelte.ts";
10
+
11
+ export * from "./currency.svelte.ts";
12
+ export * from "./exchange-rate.svelte.ts";
13
+ export * from "./types.ts";
14
+ export * from "./money.ts";
15
+ export * from "./strategies.ts";
16
+ export * from "./money-primitive.ts";
17
+ export * from "./useMoney.ts";
18
+ export * from "./useMoneyFilter.ts";
19
+
20
+ interface MoneyConfig {
21
+ exchangeRates?: {
22
+ base: string;
23
+ rates: Record<string, number>;
24
+ };
25
+ currencies?: Currency[];
26
+ defaultCurrency?: string;
27
+ }
28
+
29
+ /**
30
+ * Money Plugin — provides currency management and exchange rate conversion.
31
+ */
32
+ export const MoneyPlugin: RunePlugin = defineRune({
33
+ id: "rune-lab.money",
34
+ stores: [
35
+ {
36
+ id: "exchangeRate",
37
+ contextKey: RUNE_LAB_CONTEXT.exchangeRate,
38
+ factory: (config: unknown) => {
39
+ const store = createExchangeRateStore();
40
+ const c = config as MoneyConfig;
41
+ if (c?.exchangeRates) {
42
+ store.setRates(c.exchangeRates.base, c.exchangeRates.rates);
43
+ }
44
+ return store;
45
+ },
46
+ noPersistence: true,
47
+ },
48
+ {
49
+ id: "currency",
50
+ contextKey: RUNE_LAB_CONTEXT.currency,
51
+ factory: (
52
+ config: unknown,
53
+ _driver: PersistenceDriver,
54
+ stores: Map<string, unknown>,
55
+ ) => {
56
+ const c = config as MoneyConfig;
57
+ setExchangeRateStore(stores.get("exchangeRate") as ExchangeRateStore);
58
+
59
+ if (c?.currencies) {
60
+ for (const cur of c.currencies) {
61
+ currencyStore.addCurrency(cur);
62
+ }
63
+ }
64
+
65
+ if (
66
+ c?.defaultCurrency &&
67
+ !(currencyStore as unknown as { current: unknown }).current
68
+ ) {
69
+ if (currencyStore.get(c.defaultCurrency as never)) {
70
+ currencyStore.set(c.defaultCurrency as never);
71
+ }
72
+ }
73
+
74
+ return currencyStore;
75
+ },
76
+ dependsOn: ["exchangeRate"],
77
+ },
78
+ ],
79
+ });
@@ -0,0 +1,217 @@
1
+ import { describe, expect, it } from "vite-plus/test";
2
+ import { MoneyPrimitive } from "./money-primitive.ts";
3
+ import { registerCurrency } from "./money.ts";
4
+
5
+ describe("MoneyPrimitive", () => {
6
+ describe("Construction", () => {
7
+ it("should create from minor units", () => {
8
+ const money = MoneyPrimitive.fromMinor(1299, "USD");
9
+ expect(money.amount).toBe(1299);
10
+ expect(money.currencyCode).toBe("USD");
11
+ expect(money.scale).toBe(2);
12
+ });
13
+
14
+ it("should create from major units", () => {
15
+ const money = MoneyPrimitive.fromMajor(12.99, "USD");
16
+ expect(money.amount).toBe(1299);
17
+ expect(money.currencyCode).toBe("USD");
18
+ expect(money.scale).toBe(2);
19
+ });
20
+
21
+ it("should round fractional minor units", () => {
22
+ const money = MoneyPrimitive.fromMajor(12.999, "USD");
23
+ expect(money.amount).toBe(1300); // rounded
24
+ });
25
+
26
+ it("should throw for unknown currency", () => {
27
+ expect(() => MoneyPrimitive.fromMinor(100, "UNKNOWN")).toThrow(
28
+ "Unknown currency code",
29
+ );
30
+ });
31
+ });
32
+
33
+ describe("JPY (exponent 0)", () => {
34
+ it("should handle zero-exponent currencies", () => {
35
+ const yen = MoneyPrimitive.fromMinor(5000, "JPY");
36
+ expect(yen.minor).toBe(5000);
37
+ expect(yen.major).toBe(5000); // no decimal shift for JPY
38
+ expect(yen.scale).toBe(0);
39
+ });
40
+
41
+ it("should create from major with no decimal shift", () => {
42
+ const yen = MoneyPrimitive.fromMajor(5000, "JPY");
43
+ expect(yen.amount).toBe(5000);
44
+ });
45
+ });
46
+
47
+ describe("BHD (exponent 3)", () => {
48
+ it("should handle 3-exponent currencies", () => {
49
+ const BHD = { code: "BHD", base: 10, exponent: 3 };
50
+ registerCurrency("BHD", BHD);
51
+
52
+ const dinar = MoneyPrimitive.fromMajor(1.234, "BHD");
53
+ expect(dinar.amount).toBe(1234);
54
+ expect(dinar.major).toBe(1.234);
55
+ expect(dinar.scale).toBe(3);
56
+ });
57
+ });
58
+
59
+ describe("Getters", () => {
60
+ it(".minor returns the raw amount", () => {
61
+ const money = MoneyPrimitive.fromMinor(1500, "USD");
62
+ expect(money.minor).toBe(1500);
63
+ });
64
+
65
+ it(".major returns the human-readable float", () => {
66
+ const money = MoneyPrimitive.fromMinor(1500, "USD");
67
+ expect(money.major).toBe(15.0);
68
+ });
69
+
70
+ it(".major handles MXN (exponent 2)", () => {
71
+ const money = MoneyPrimitive.fromMinor(25050, "MXN");
72
+ expect(money.major).toBe(250.5);
73
+ });
74
+ });
75
+
76
+ describe("Formatting", () => {
77
+ it("should format USD in en-US", () => {
78
+ const money = MoneyPrimitive.fromMinor(1299, "USD");
79
+ expect(money.format("en-US")).toMatch(/\$12\.99/);
80
+ });
81
+
82
+ it("should format MXN in es-MX", () => {
83
+ const money = MoneyPrimitive.fromMinor(25050, "MXN");
84
+ expect(money.format("es-MX")).toMatch(/\$250\.50/);
85
+ });
86
+
87
+ it("should format JPY with no decimals", () => {
88
+ const money = MoneyPrimitive.fromMinor(5000, "JPY");
89
+ const formatted = money.format("ja-JP");
90
+ expect(formatted).toMatch(/5,000/);
91
+ });
92
+ });
93
+
94
+ describe("Arithmetic (immutability)", () => {
95
+ it("add returns a new instance", () => {
96
+ const a = MoneyPrimitive.fromMinor(1000, "USD");
97
+ const b = MoneyPrimitive.fromMinor(500, "USD");
98
+ const result = a.add(b);
99
+
100
+ expect(result.amount).toBe(1500);
101
+ expect(result).not.toBe(a); // new instance
102
+ expect(a.amount).toBe(1000); // original unchanged
103
+ });
104
+
105
+ it("subtract returns a new instance", () => {
106
+ const a = MoneyPrimitive.fromMinor(1000, "USD");
107
+ const b = MoneyPrimitive.fromMinor(300, "USD");
108
+ const result = a.subtract(b);
109
+
110
+ expect(result.amount).toBe(700);
111
+ expect(a.amount).toBe(1000);
112
+ });
113
+
114
+ it("multiply returns a new instance", () => {
115
+ const money = MoneyPrimitive.fromMinor(1000, "USD");
116
+ const result = money.multiply(2.5);
117
+
118
+ expect(result.amount).toBe(2500);
119
+ expect(money.amount).toBe(1000);
120
+ });
121
+
122
+ it("multiply rounds correctly", () => {
123
+ const money = MoneyPrimitive.fromMinor(100, "USD");
124
+ const result = money.multiply(0.33);
125
+
126
+ expect(result.amount).toBe(33);
127
+ });
128
+
129
+ it("throws on currency mismatch for add", () => {
130
+ const usd = MoneyPrimitive.fromMinor(100, "USD");
131
+ const eur = MoneyPrimitive.fromMinor(100, "EUR");
132
+
133
+ expect(() => usd.add(eur)).toThrow("Currency mismatch");
134
+ });
135
+
136
+ it("throws on currency mismatch for subtract", () => {
137
+ const usd = MoneyPrimitive.fromMinor(100, "USD");
138
+ const eur = MoneyPrimitive.fromMinor(100, "EUR");
139
+
140
+ expect(() => usd.subtract(eur)).toThrow("Currency mismatch");
141
+ });
142
+ });
143
+
144
+ describe("Comparison", () => {
145
+ it("equals returns true for identical values", () => {
146
+ const a = MoneyPrimitive.fromMinor(1000, "USD");
147
+ const b = MoneyPrimitive.fromMinor(1000, "USD");
148
+ expect(a.equals(b)).toBe(true);
149
+ });
150
+
151
+ it("equals returns false for different amounts", () => {
152
+ const a = MoneyPrimitive.fromMinor(1000, "USD");
153
+ const b = MoneyPrimitive.fromMinor(2000, "USD");
154
+ expect(a.equals(b)).toBe(false);
155
+ });
156
+
157
+ it("equals returns false for different currencies", () => {
158
+ const a = MoneyPrimitive.fromMinor(1000, "USD");
159
+ const b = MoneyPrimitive.fromMinor(1000, "EUR");
160
+ expect(a.equals(b)).toBe(false);
161
+ });
162
+
163
+ it("isZero returns true for zero amount", () => {
164
+ expect(MoneyPrimitive.fromMinor(0, "USD").isZero()).toBe(true);
165
+ });
166
+
167
+ it("isZero returns false for non-zero", () => {
168
+ expect(MoneyPrimitive.fromMinor(1, "USD").isZero()).toBe(false);
169
+ });
170
+
171
+ it("isNegative works correctly", () => {
172
+ expect(MoneyPrimitive.fromMinor(-100, "USD").isNegative()).toBe(true);
173
+ expect(MoneyPrimitive.fromMinor(0, "USD").isNegative()).toBe(false);
174
+ expect(MoneyPrimitive.fromMinor(100, "USD").isNegative()).toBe(false);
175
+ });
176
+ });
177
+
178
+ describe("Serialization (JSON round-trip)", () => {
179
+ it("should round-trip standard currency", () => {
180
+ const original = MoneyPrimitive.fromMinor(1299, "USD");
181
+ const json = original.toJSON();
182
+
183
+ expect(json).toEqual({
184
+ amount: 1299,
185
+ currencyCode: "USD",
186
+ scale: 2,
187
+ });
188
+
189
+ const restored = MoneyPrimitive.fromJSON(json);
190
+ expect(restored.equals(original)).toBe(true);
191
+ });
192
+
193
+ it("should round-trip JPY", () => {
194
+ const original = MoneyPrimitive.fromMinor(5000, "JPY");
195
+ const json = original.toJSON();
196
+ const restored = MoneyPrimitive.fromJSON(json);
197
+
198
+ expect(restored.amount).toBe(5000);
199
+ expect(restored.scale).toBe(0);
200
+ expect(restored.equals(original)).toBe(true);
201
+ });
202
+
203
+ it("should round-trip through JSON.stringify/parse", () => {
204
+ const original = MoneyPrimitive.fromMajor(42.5, "EUR");
205
+ const serialized = JSON.stringify(original);
206
+ const parsed = JSON.parse(serialized);
207
+ const restored = MoneyPrimitive.fromJSON(parsed);
208
+
209
+ expect(restored.equals(original)).toBe(true);
210
+ });
211
+
212
+ it("toString returns a debug-friendly string", () => {
213
+ const money = MoneyPrimitive.fromMinor(1299, "USD");
214
+ expect(money.toString()).toBe("MoneyPrimitive(1299 USD scale=2)");
215
+ });
216
+ });
217
+ });