rune-lab 0.3.1 → 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.
- package/.gitignore +12 -0
- package/README.md +4 -4
- package/deno.json +50 -0
- package/justfile +5 -0
- package/package.json +3 -80
- package/scripts/build_pkg.ts +51 -0
- package/scripts/ci.just +26 -0
- package/scripts/deploy.just +21 -0
- package/scripts/dev.just +4 -0
- package/scripts/inject.just +72 -0
- package/src/RuneProvider.svelte +105 -0
- package/src/i18n/message-resolver.test.ts +103 -0
- package/src/i18n/message-resolver.ts +89 -0
- package/src/i18n/project.inlang/settings.json +26 -0
- package/src/i18n/translations/ar.json +89 -0
- package/src/i18n/translations/de.json +89 -0
- package/src/i18n/translations/en.json +89 -0
- package/src/i18n/translations/es.json +89 -0
- package/src/i18n/translations/fr.json +89 -0
- package/src/i18n/translations/hi.json +89 -0
- package/src/i18n/translations/it.json +89 -0
- package/src/i18n/translations/ja.json +89 -0
- package/src/i18n/translations/ko.json +89 -0
- package/src/i18n/translations/pt.json +89 -0
- package/src/i18n/translations/ru.json +89 -0
- package/src/i18n/translations/vi.json +89 -0
- package/src/i18n/translations/zh.json +89 -0
- package/src/kernel/deno.json +5 -0
- package/src/kernel/src/actions/portal.ts +41 -0
- package/src/kernel/src/actions/shortcut-listener.ts +50 -0
- package/src/kernel/src/context/app.svelte.ts +98 -0
- package/src/kernel/src/context/context.ts +31 -0
- package/src/kernel/src/context/stores.svelte.ts +96 -0
- package/src/kernel/src/context/types.ts +110 -0
- package/src/kernel/src/context/useRuneLab.ts +38 -0
- package/src/kernel/src/mod.ts +13 -0
- package/src/kernel/src/persistence/createConfigStore.svelte.ts +120 -0
- package/src/kernel/src/persistence/drivers.test.ts +95 -0
- package/src/kernel/src/persistence/drivers.ts +77 -0
- package/src/kernel/src/persistence/provider.test.ts +58 -0
- package/src/kernel/src/persistence/provider.ts +58 -0
- package/src/kernel/src/persistence/types.ts +19 -0
- package/src/kernel/src/persistence/usePersistence.ts +9 -0
- package/src/kernel/src/registry/mod.ts +221 -0
- package/src/kernel/src/registry/registry.test.ts +162 -0
- package/src/kernel/src/tokens/props.ts +94 -0
- package/src/mod.ts +10 -0
- package/src/runes/layout/deno.json +5 -0
- package/src/runes/layout/src/APP_CONFIGURATIONS.ts +56 -0
- package/{dist/ui/features/config → src/runes/layout/src}/AppSettingSelector.svelte +1 -1
- package/{dist/ui/layout → src/runes/layout/src}/ConnectedNavigationPanel.svelte +7 -8
- package/{dist/ui/layout → src/runes/layout/src}/ConnectedWorkspaceStrip.svelte +5 -3
- package/src/runes/layout/src/Icon.svelte +7 -0
- package/src/runes/layout/src/LanguageSelector.svelte +45 -0
- package/{dist/ui/layout → src/runes/layout/src}/NavigationPanel.svelte +1 -4
- package/src/runes/layout/src/ResourceSelector.svelte +90 -0
- package/src/runes/layout/src/ThemeSelector.svelte +50 -0
- package/{dist/ui/layout → src/runes/layout/src}/WorkspaceLayout.svelte +13 -3
- package/{dist/ui/layout → src/runes/layout/src}/WorkspaceStrip.svelte +1 -1
- package/src/runes/layout/src/connection-factory.ts +95 -0
- package/src/runes/layout/src/language.svelte.ts +44 -0
- package/src/runes/layout/src/mod.ts +71 -0
- package/src/runes/layout/src/store.svelte.ts +142 -0
- package/src/runes/layout/src/theme.svelte.ts +73 -0
- package/src/runes/layout/src/types.ts +11 -0
- package/src/runes/palettes/deno.json +8 -0
- package/{dist/ui/features/command-palette → src/runes/palettes/src/commands}/CommandPalette.svelte +4 -4
- package/src/runes/palettes/src/commands/mod.ts +2 -0
- package/src/runes/palettes/src/commands/store.svelte.ts +80 -0
- package/src/runes/palettes/src/mod.ts +51 -0
- package/{dist/ui/components → src/runes/palettes/src/notifications}/Toaster.svelte +1 -2
- package/src/runes/palettes/src/notifications/bridge.ts +56 -0
- package/src/runes/palettes/src/notifications/mod.ts +4 -0
- package/src/runes/palettes/src/notifications/store.svelte.ts +58 -0
- package/{dist/ui/features → src/runes/palettes/src}/shortcuts/ShortcutPalette.svelte +4 -4
- package/src/runes/palettes/src/shortcuts/mod.ts +4 -0
- package/src/runes/palettes/src/shortcuts/store.svelte.ts +85 -0
- package/src/runes/palettes/src/shortcuts/types.ts +4 -0
- package/src/runes/palettes/src/shortcuts/useShortcuts.ts +85 -0
- package/src/runes/plugins/money/deno.json +8 -0
- package/src/runes/plugins/money/src/CurrencySelector.svelte +41 -0
- package/src/runes/plugins/money/src/MoneyDisplay.svelte +142 -0
- package/src/runes/plugins/money/src/MoneyInput.svelte +208 -0
- package/src/runes/plugins/money/src/currency.svelte.ts +95 -0
- package/src/runes/plugins/money/src/currency.test.ts +40 -0
- package/src/runes/plugins/money/src/exchange-rate.svelte.ts +176 -0
- package/src/runes/plugins/money/src/exchange-rate.test.ts +95 -0
- package/src/runes/plugins/money/src/mod.ts +79 -0
- package/src/runes/plugins/money/src/money-primitive.test.ts +217 -0
- package/src/runes/plugins/money/src/money-primitive.ts +220 -0
- package/src/runes/plugins/money/src/money.test.ts +183 -0
- package/src/runes/plugins/money/src/money.ts +382 -0
- package/src/runes/plugins/money/src/strategies.test.ts +143 -0
- package/src/runes/plugins/money/src/strategies.ts +95 -0
- package/src/runes/plugins/money/src/types.ts +27 -0
- package/src/runes/plugins/money/src/useMoney.ts +261 -0
- package/src/runes/plugins/money/src/useMoneyFilter.ts +132 -0
- package/tsconfig.json +41 -0
- package/vite.config.ts +44 -0
- package/dist/core/index.d.ts +0 -4
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -3
- package/dist/core/internal/message-resolver.d.ts +0 -42
- package/dist/core/internal/message-resolver.d.ts.map +0 -1
- package/dist/core/internal/message-resolver.js +0 -47
- package/dist/core/money/index.d.ts +0 -2
- package/dist/core/money/index.d.ts.map +0 -1
- package/dist/core/money/index.js +0 -2
- package/dist/core/money/money.d.ts +0 -57
- package/dist/core/money/money.d.ts.map +0 -1
- package/dist/core/money/money.js +0 -132
- package/dist/core/persistence/types.d.ts +0 -18
- package/dist/core/persistence/types.d.ts.map +0 -1
- package/dist/core/persistence/types.js +0 -2
- package/dist/index.d.ts +0 -5
- package/dist/index.js +0 -5
- package/dist/state/api.svelte.d.ts +0 -26
- package/dist/state/api.svelte.js +0 -65
- package/dist/state/app.svelte.d.ts +0 -48
- package/dist/state/app.svelte.js +0 -82
- package/dist/state/auth/index.d.ts +0 -2
- package/dist/state/auth/index.js +0 -2
- package/dist/state/auth/session.svelte.d.ts +0 -40
- package/dist/state/auth/session.svelte.js +0 -57
- package/dist/state/auth/types.d.ts +0 -30
- package/dist/state/auth/types.js +0 -3
- package/dist/state/cart.svelte.d.ts +0 -64
- package/dist/state/cart.svelte.js +0 -122
- package/dist/state/commands.svelte.d.ts +0 -46
- package/dist/state/commands.svelte.js +0 -62
- package/dist/state/composables/useMoney.d.ts +0 -15
- package/dist/state/composables/useMoney.js +0 -62
- package/dist/state/composables/usePersistence.d.ts +0 -2
- package/dist/state/composables/usePersistence.js +0 -5
- package/dist/state/composables/useRuneLab.d.ts +0 -19
- package/dist/state/composables/useRuneLab.js +0 -30
- package/dist/state/context.d.ts +0 -14
- package/dist/state/context.js +0 -15
- package/dist/state/createConfigStore.svelte.d.ts +0 -55
- package/dist/state/createConfigStore.svelte.js +0 -61
- package/dist/state/currency.svelte.d.ts +0 -28
- package/dist/state/currency.svelte.js +0 -80
- package/dist/state/daisyui.d.ts +0 -4
- package/dist/state/index.d.ts +0 -19
- package/dist/state/index.js +0 -25
- package/dist/state/language.svelte.d.ts +0 -64
- package/dist/state/language.svelte.js +0 -44
- package/dist/state/layout.svelte.d.ts +0 -49
- package/dist/state/layout.svelte.js +0 -108
- package/dist/state/persistence/drivers.d.ts +0 -12
- package/dist/state/persistence/drivers.js +0 -73
- package/dist/state/shortcuts.svelte.d.ts +0 -121
- package/dist/state/shortcuts.svelte.js +0 -222
- package/dist/state/theme.svelte.d.ts +0 -22
- package/dist/state/theme.svelte.js +0 -92
- package/dist/state/toast-bridge.d.ts +0 -29
- package/dist/state/toast-bridge.js +0 -43
- package/dist/state/toast.svelte.d.ts +0 -27
- package/dist/state/toast.svelte.js +0 -43
- package/dist/ui/actions/portal.d.ts +0 -7
- package/dist/ui/actions/portal.js +0 -32
- package/dist/ui/components/ApiMonitor.svelte +0 -162
- package/dist/ui/components/ApiMonitor.svelte.d.ts +0 -3
- package/dist/ui/components/Icon.svelte +0 -108
- package/dist/ui/components/Icon.svelte.d.ts +0 -14
- package/dist/ui/components/Kyntharil.svelte +0 -48
- package/dist/ui/components/Kyntharil.svelte.d.ts +0 -6
- package/dist/ui/components/RuneProvider.svelte +0 -198
- package/dist/ui/components/RuneProvider.svelte.d.ts +0 -42
- package/dist/ui/components/Toaster.svelte.d.ts +0 -18
- package/dist/ui/components/money/MoneyDisplay.svelte +0 -69
- package/dist/ui/components/money/MoneyDisplay.svelte.d.ts +0 -21
- package/dist/ui/components/money/MoneyDisplay.svelte.test.d.ts +0 -1
- package/dist/ui/components/money/MoneyDisplay.svelte.test.js +0 -74
- package/dist/ui/components/money/MoneyInput.svelte +0 -131
- package/dist/ui/components/money/MoneyInput.svelte.d.ts +0 -25
- package/dist/ui/components/money/index.d.ts +0 -2
- package/dist/ui/components/money/index.js +0 -3
- package/dist/ui/components/user/UserAvatar.svelte +0 -66
- package/dist/ui/components/user/UserAvatar.svelte.d.ts +0 -13
- package/dist/ui/components/user/UserProfile.svelte +0 -79
- package/dist/ui/components/user/UserProfile.svelte.d.ts +0 -17
- package/dist/ui/components/user/index.d.ts +0 -2
- package/dist/ui/components/user/index.js +0 -3
- package/dist/ui/features/command-palette/CommandPalette.svelte.d.ts +0 -6
- package/dist/ui/features/config/AppSettingSelector.svelte.d.ts +0 -13
- package/dist/ui/features/config/CurrencySelector.svelte +0 -67
- package/dist/ui/features/config/CurrencySelector.svelte.d.ts +0 -8
- package/dist/ui/features/config/LanguageSelector.svelte +0 -66
- package/dist/ui/features/config/LanguageSelector.svelte.d.ts +0 -8
- package/dist/ui/features/config/ThemeSelector.svelte +0 -73
- package/dist/ui/features/config/ThemeSelector.svelte.d.ts +0 -8
- package/dist/ui/features/notifications/NotificationBell.svelte.d.ts +0 -11
- package/dist/ui/features/notifications/index.d.ts +0 -1
- package/dist/ui/features/notifications/index.js +0 -2
- package/dist/ui/features/shortcuts/ShortcutPalette.svelte.d.ts +0 -6
- package/dist/ui/index.d.ts +0 -26
- package/dist/ui/index.js +0 -41
- package/dist/ui/layout/ConnectedNavigationPanel.svelte.d.ts +0 -10
- package/dist/ui/layout/ConnectedWorkspaceStrip.svelte.d.ts +0 -9
- package/dist/ui/layout/ContentArea.svelte.d.ts +0 -8
- package/dist/ui/layout/DetailPanel.svelte.d.ts +0 -7
- package/dist/ui/layout/NavigationPanel.svelte.d.ts +0 -14
- package/dist/ui/layout/WorkspaceLayout.svelte.d.ts +0 -25
- package/dist/ui/layout/WorkspaceStrip.svelte.d.ts +0 -11
- package/dist/ui/layout/index.d.ts +0 -7
- package/dist/ui/layout/index.js +0 -7
- package/dist/ui/paraglide/README.md +0 -94
- package/dist/ui/paraglide/messages/_index.d.ts +0 -87
- package/dist/ui/paraglide/messages/_index.js +0 -88
- package/dist/ui/paraglide/messages/abyss.d.ts +0 -16
- package/dist/ui/paraglide/messages/abyss.js +0 -84
- package/dist/ui/paraglide/messages/acid.d.ts +0 -16
- package/dist/ui/paraglide/messages/acid.js +0 -84
- package/dist/ui/paraglide/messages/active_toasts.d.ts +0 -16
- package/dist/ui/paraglide/messages/active_toasts.js +0 -84
- package/dist/ui/paraglide/messages/aed3.d.ts +0 -17
- package/dist/ui/paraglide/messages/aed3.js +0 -85
- package/dist/ui/paraglide/messages/all_currencies.d.ts +0 -16
- package/dist/ui/paraglide/messages/all_currencies.js +0 -84
- package/dist/ui/paraglide/messages/all_languages.d.ts +0 -16
- package/dist/ui/paraglide/messages/all_languages.js +0 -84
- package/dist/ui/paraglide/messages/all_themes.d.ts +0 -16
- package/dist/ui/paraglide/messages/all_themes.js +0 -84
- package/dist/ui/paraglide/messages/api_status.d.ts +0 -16
- package/dist/ui/paraglide/messages/api_status.js +0 -84
- package/dist/ui/paraglide/messages/app_info.d.ts +0 -16
- package/dist/ui/paraglide/messages/app_info.js +0 -84
- package/dist/ui/paraglide/messages/appearance.d.ts +0 -16
- package/dist/ui/paraglide/messages/appearance.js +0 -84
- package/dist/ui/paraglide/messages/aqua.d.ts +0 -16
- package/dist/ui/paraglide/messages/aqua.js +0 -84
- package/dist/ui/paraglide/messages/ar.d.ts +0 -16
- package/dist/ui/paraglide/messages/ar.js +0 -84
- package/dist/ui/paraglide/messages/autumn.d.ts +0 -16
- package/dist/ui/paraglide/messages/autumn.js +0 -84
- package/dist/ui/paraglide/messages/black.d.ts +0 -16
- package/dist/ui/paraglide/messages/black.js +0 -84
- package/dist/ui/paraglide/messages/brl3.d.ts +0 -17
- package/dist/ui/paraglide/messages/brl3.js +0 -85
- package/dist/ui/paraglide/messages/bumblebee.d.ts +0 -16
- package/dist/ui/paraglide/messages/bumblebee.js +0 -84
- package/dist/ui/paraglide/messages/business.d.ts +0 -16
- package/dist/ui/paraglide/messages/business.js +0 -84
- package/dist/ui/paraglide/messages/cad3.d.ts +0 -17
- package/dist/ui/paraglide/messages/cad3.js +0 -85
- package/dist/ui/paraglide/messages/caramellatte.d.ts +0 -16
- package/dist/ui/paraglide/messages/caramellatte.js +0 -84
- package/dist/ui/paraglide/messages/cmyk.d.ts +0 -16
- package/dist/ui/paraglide/messages/cmyk.js +0 -84
- package/dist/ui/paraglide/messages/cny3.d.ts +0 -17
- package/dist/ui/paraglide/messages/cny3.js +0 -85
- package/dist/ui/paraglide/messages/coffee.d.ts +0 -16
- package/dist/ui/paraglide/messages/coffee.js +0 -84
- package/dist/ui/paraglide/messages/commands_label.d.ts +0 -16
- package/dist/ui/paraglide/messages/commands_label.js +0 -84
- package/dist/ui/paraglide/messages/corporate.d.ts +0 -16
- package/dist/ui/paraglide/messages/corporate.js +0 -84
- package/dist/ui/paraglide/messages/cupcake.d.ts +0 -16
- package/dist/ui/paraglide/messages/cupcake.js +0 -84
- package/dist/ui/paraglide/messages/currency.d.ts +0 -16
- package/dist/ui/paraglide/messages/currency.js +0 -84
- package/dist/ui/paraglide/messages/current_currency.d.ts +0 -16
- package/dist/ui/paraglide/messages/current_currency.js +0 -84
- package/dist/ui/paraglide/messages/current_language.d.ts +0 -16
- package/dist/ui/paraglide/messages/current_language.js +0 -84
- package/dist/ui/paraglide/messages/current_theme.d.ts +0 -16
- package/dist/ui/paraglide/messages/current_theme.js +0 -84
- package/dist/ui/paraglide/messages/currently_in_queue.d.ts +0 -16
- package/dist/ui/paraglide/messages/currently_in_queue.js +0 -84
- package/dist/ui/paraglide/messages/cyberpunk.d.ts +0 -16
- package/dist/ui/paraglide/messages/cyberpunk.js +0 -84
- package/dist/ui/paraglide/messages/dark.d.ts +0 -16
- package/dist/ui/paraglide/messages/dark.js +0 -84
- package/dist/ui/paraglide/messages/de.d.ts +0 -16
- package/dist/ui/paraglide/messages/de.js +0 -84
- package/dist/ui/paraglide/messages/dim.d.ts +0 -16
- package/dist/ui/paraglide/messages/dim.js +0 -84
- package/dist/ui/paraglide/messages/dracula.d.ts +0 -16
- package/dist/ui/paraglide/messages/dracula.js +0 -84
- package/dist/ui/paraglide/messages/emerald.d.ts +0 -16
- package/dist/ui/paraglide/messages/emerald.js +0 -84
- package/dist/ui/paraglide/messages/en.d.ts +0 -16
- package/dist/ui/paraglide/messages/en.js +0 -84
- package/dist/ui/paraglide/messages/es.d.ts +0 -16
- package/dist/ui/paraglide/messages/es.js +0 -84
- package/dist/ui/paraglide/messages/eur3.d.ts +0 -17
- package/dist/ui/paraglide/messages/eur3.js +0 -85
- package/dist/ui/paraglide/messages/extended_controls.d.ts +0 -16
- package/dist/ui/paraglide/messages/extended_controls.js +0 -84
- package/dist/ui/paraglide/messages/fantasy.d.ts +0 -16
- package/dist/ui/paraglide/messages/fantasy.js +0 -84
- package/dist/ui/paraglide/messages/forest.d.ts +0 -16
- package/dist/ui/paraglide/messages/forest.js +0 -84
- package/dist/ui/paraglide/messages/fr.d.ts +0 -16
- package/dist/ui/paraglide/messages/fr.js +0 -84
- package/dist/ui/paraglide/messages/garden.d.ts +0 -16
- package/dist/ui/paraglide/messages/garden.js +0 -84
- package/dist/ui/paraglide/messages/gbp3.d.ts +0 -17
- package/dist/ui/paraglide/messages/gbp3.js +0 -85
- package/dist/ui/paraglide/messages/halloween.d.ts +0 -16
- package/dist/ui/paraglide/messages/halloween.js +0 -84
- package/dist/ui/paraglide/messages/hello_world.d.ts +0 -18
- package/dist/ui/paraglide/messages/hello_world.js +0 -84
- package/dist/ui/paraglide/messages/hi.d.ts +0 -16
- package/dist/ui/paraglide/messages/hi.js +0 -84
- package/dist/ui/paraglide/messages/inr3.d.ts +0 -17
- package/dist/ui/paraglide/messages/inr3.js +0 -85
- package/dist/ui/paraglide/messages/it.d.ts +0 -16
- package/dist/ui/paraglide/messages/it.js +0 -84
- package/dist/ui/paraglide/messages/ja.d.ts +0 -16
- package/dist/ui/paraglide/messages/ja.js +0 -84
- package/dist/ui/paraglide/messages/jpy3.d.ts +0 -17
- package/dist/ui/paraglide/messages/jpy3.js +0 -85
- package/dist/ui/paraglide/messages/ko.d.ts +0 -16
- package/dist/ui/paraglide/messages/ko.js +0 -84
- package/dist/ui/paraglide/messages/krw3.d.ts +0 -17
- package/dist/ui/paraglide/messages/krw3.js +0 -85
- package/dist/ui/paraglide/messages/languages.d.ts +0 -16
- package/dist/ui/paraglide/messages/languages.js +0 -84
- package/dist/ui/paraglide/messages/lemonade.d.ts +0 -16
- package/dist/ui/paraglide/messages/lemonade.js +0 -84
- package/dist/ui/paraglide/messages/light.d.ts +0 -16
- package/dist/ui/paraglide/messages/light.js +0 -84
- package/dist/ui/paraglide/messages/live_store_dashboard.d.ts +0 -16
- package/dist/ui/paraglide/messages/live_store_dashboard.js +0 -84
- package/dist/ui/paraglide/messages/localization.d.ts +0 -16
- package/dist/ui/paraglide/messages/localization.js +0 -84
- package/dist/ui/paraglide/messages/lofi.d.ts +0 -16
- package/dist/ui/paraglide/messages/lofi.js +0 -84
- package/dist/ui/paraglide/messages/luxury.d.ts +0 -16
- package/dist/ui/paraglide/messages/luxury.js +0 -84
- package/dist/ui/paraglide/messages/mxn3.d.ts +0 -17
- package/dist/ui/paraglide/messages/mxn3.js +0 -85
- package/dist/ui/paraglide/messages/name_label.d.ts +0 -16
- package/dist/ui/paraglide/messages/name_label.js +0 -84
- package/dist/ui/paraglide/messages/night.d.ts +0 -16
- package/dist/ui/paraglide/messages/night.js +0 -84
- package/dist/ui/paraglide/messages/nord.d.ts +0 -16
- package/dist/ui/paraglide/messages/nord.js +0 -84
- package/dist/ui/paraglide/messages/pastel.d.ts +0 -16
- package/dist/ui/paraglide/messages/pastel.js +0 -84
- package/dist/ui/paraglide/messages/pt.d.ts +0 -16
- package/dist/ui/paraglide/messages/pt.js +0 -84
- package/dist/ui/paraglide/messages/real_time_monitor_desc.d.ts +0 -16
- package/dist/ui/paraglide/messages/real_time_monitor_desc.js +0 -84
- package/dist/ui/paraglide/messages/registered_in_registry.d.ts +0 -16
- package/dist/ui/paraglide/messages/registered_in_registry.js +0 -84
- package/dist/ui/paraglide/messages/retro.d.ts +0 -16
- package/dist/ui/paraglide/messages/retro.js +0 -84
- package/dist/ui/paraglide/messages/ru.d.ts +0 -16
- package/dist/ui/paraglide/messages/ru.js +0 -84
- package/dist/ui/paraglide/messages/silk.d.ts +0 -16
- package/dist/ui/paraglide/messages/silk.js +0 -84
- package/dist/ui/paraglide/messages/state_label.d.ts +0 -16
- package/dist/ui/paraglide/messages/state_label.js +0 -84
- package/dist/ui/paraglide/messages/sunset.d.ts +0 -16
- package/dist/ui/paraglide/messages/sunset.js +0 -84
- package/dist/ui/paraglide/messages/synthwave.d.ts +0 -16
- package/dist/ui/paraglide/messages/synthwave.js +0 -84
- package/dist/ui/paraglide/messages/system.d.ts +0 -16
- package/dist/ui/paraglide/messages/system.js +0 -84
- package/dist/ui/paraglide/messages/theme.d.ts +0 -16
- package/dist/ui/paraglide/messages/theme.js +0 -84
- package/dist/ui/paraglide/messages/themes.d.ts +0 -16
- package/dist/ui/paraglide/messages/themes.js +0 -84
- package/dist/ui/paraglide/messages/url_label.d.ts +0 -16
- package/dist/ui/paraglide/messages/url_label.js +0 -84
- package/dist/ui/paraglide/messages/usd3.d.ts +0 -17
- package/dist/ui/paraglide/messages/usd3.js +0 -85
- package/dist/ui/paraglide/messages/valentine.d.ts +0 -16
- package/dist/ui/paraglide/messages/valentine.js +0 -84
- package/dist/ui/paraglide/messages/version_label.d.ts +0 -16
- package/dist/ui/paraglide/messages/version_label.js +0 -84
- package/dist/ui/paraglide/messages/vi.d.ts +0 -16
- package/dist/ui/paraglide/messages/vi.js +0 -84
- package/dist/ui/paraglide/messages/winter.d.ts +0 -16
- package/dist/ui/paraglide/messages/winter.js +0 -84
- package/dist/ui/paraglide/messages/wireframe.d.ts +0 -16
- package/dist/ui/paraglide/messages/wireframe.js +0 -84
- package/dist/ui/paraglide/messages/zh.d.ts +0 -16
- package/dist/ui/paraglide/messages/zh.js +0 -84
- package/dist/ui/paraglide/messages.d.ts +0 -2
- package/dist/ui/paraglide/messages.js +0 -4
- package/dist/ui/paraglide/registry.d.ts +0 -21
- package/dist/ui/paraglide/registry.js +0 -31
- package/dist/ui/paraglide/runtime.d.ts +0 -749
- package/dist/ui/paraglide/runtime.js +0 -1777
- package/dist/ui/paraglide/server.d.ts +0 -103
- package/dist/ui/paraglide/server.js +0 -248
- package/dist/ui/primitives/DatePicker.svelte +0 -257
- package/dist/ui/primitives/DatePicker.svelte.d.ts +0 -17
- package/dist/ui/primitives/index.d.ts +0 -1
- package/dist/ui/primitives/index.js +0 -3
- /package/{dist/ui/layout → src/runes/layout/src}/ContentArea.svelte +0 -0
- /package/{dist/ui/layout → src/runes/layout/src}/DetailPanel.svelte +0 -0
- /package/{dist/ui/features → src/runes/palettes/src}/notifications/NotificationBell.svelte +0 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
|
+
import { toSnapshot } from "dinero.js";
|
|
3
|
+
import {
|
|
4
|
+
convertAmount,
|
|
5
|
+
convertMoney,
|
|
6
|
+
createMoney,
|
|
7
|
+
CURRENCY_MAP,
|
|
8
|
+
formatAmount,
|
|
9
|
+
fromMoneySnapshot,
|
|
10
|
+
registerCurrency,
|
|
11
|
+
scaledRate,
|
|
12
|
+
toAdyenMoney,
|
|
13
|
+
toMinorUnit,
|
|
14
|
+
toMoneySnapshot,
|
|
15
|
+
toPaypalMoney,
|
|
16
|
+
toSquareMoney,
|
|
17
|
+
toStripeMoney,
|
|
18
|
+
} from "./money.ts";
|
|
19
|
+
|
|
20
|
+
describe("Money Core Utilities", () => {
|
|
21
|
+
describe("safeAmount()", () => {
|
|
22
|
+
it("should return 0 for null and undefined", () => {
|
|
23
|
+
// testing internal private function via public wrapper createMoney
|
|
24
|
+
expect(toSnapshot(createMoney(null, "USD")).amount).toBe(0);
|
|
25
|
+
expect(toSnapshot(createMoney(undefined, "USD")).amount).toBe(0);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("should return finite numbers unchanged", () => {
|
|
29
|
+
expect(toSnapshot(createMoney(1234, "USD")).amount).toBe(1234);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should return 0 for NaN and Infinity", () => {
|
|
33
|
+
expect(toSnapshot(createMoney(NaN, "USD")).amount).toBe(0);
|
|
34
|
+
expect(toSnapshot(createMoney(Infinity, "USD")).amount).toBe(0);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should handle bigints", () => {
|
|
38
|
+
expect(toSnapshot(createMoney(BigInt(5000), "USD")).amount).toBe(5000);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should handle objects with toString (SurrealDB Decimal)", () => {
|
|
42
|
+
const decimal = { toString: () => "123.45" };
|
|
43
|
+
expect(toSnapshot(createMoney(decimal, "USD")).amount).toBe(123); // Math.round(123.45) = 123
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should handle numeric strings", () => {
|
|
47
|
+
expect(toSnapshot(createMoney("999", "USD")).amount).toBe(999);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe("toMinorUnit()", () => {
|
|
52
|
+
it("should convert major to minor for MXN (exponent 2)", () => {
|
|
53
|
+
expect(toMinorUnit(12900000, "MXN")).toBe(1290000000);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("should convert major to minor for JPY (exponent 0)", () => {
|
|
57
|
+
expect(toMinorUnit(5000, "JPY")).toBe(5000);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("should throw for unknown currency", () => {
|
|
61
|
+
expect(() => toMinorUnit(100, "UNKNOWN")).toThrow();
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("registerCurrency()", () => {
|
|
66
|
+
it("should allow registering a new currency and creating money with it", () => {
|
|
67
|
+
const KWD = { code: "KWD", base: 10, exponent: 3 };
|
|
68
|
+
registerCurrency("KWD", KWD);
|
|
69
|
+
|
|
70
|
+
expect(CURRENCY_MAP["KWD"]).toEqual(KWD);
|
|
71
|
+
|
|
72
|
+
const money = createMoney(1000, "KWD");
|
|
73
|
+
expect(toSnapshot(money).currency.code).toBe("KWD");
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe("formatAmount()", () => {
|
|
78
|
+
it("should format minor units correctly for USD", () => {
|
|
79
|
+
// $1.50
|
|
80
|
+
expect(formatAmount(150, "USD", "en-US")).toMatch(/\$1\.50/);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should format minor units correctly for MXN", () => {
|
|
84
|
+
// MX$1.50 or $1.50 depending on locale
|
|
85
|
+
expect(formatAmount(150, "MXN", "es-MX")).toMatch(/\$1\.50/);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe("Exchange Rates & Conversion", () => {
|
|
90
|
+
describe("scaledRate()", () => {
|
|
91
|
+
it("should convert float to ScaledRate with default precision", () => {
|
|
92
|
+
expect(scaledRate(17.23)).toEqual({ amount: 172300, scale: 4 });
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("should use custom precision", () => {
|
|
96
|
+
expect(scaledRate(0.89, 2)).toEqual({ amount: 89, scale: 2 });
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("should handle integer rates", () => {
|
|
100
|
+
expect(scaledRate(1199, 0)).toEqual({ amount: 1199, scale: 0 });
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe("convertMoney()", () => {
|
|
105
|
+
it("should convert USD to MXN", () => {
|
|
106
|
+
const usd = createMoney(100, "USD");
|
|
107
|
+
const rates = { MXN: scaledRate(20) };
|
|
108
|
+
const mxn = convertMoney(usd, "MXN", rates);
|
|
109
|
+
const snapshot = toSnapshot(mxn);
|
|
110
|
+
expect(snapshot.amount).toBe(20000000);
|
|
111
|
+
expect(snapshot.scale).toBe(6);
|
|
112
|
+
expect(snapshot.currency.code).toBe("MXN");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("should throw for unknown target currency", () => {
|
|
116
|
+
const usd = createMoney(100, "USD");
|
|
117
|
+
expect(() => convertMoney(usd, "UNKNOWN", {})).toThrow();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("should throw for missing rate", () => {
|
|
121
|
+
const usd = createMoney(100, "USD");
|
|
122
|
+
expect(() => convertMoney(usd, "MXN", {})).toThrow();
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe("convertAmount()", () => {
|
|
127
|
+
it("should return same amount if codes match", () => {
|
|
128
|
+
expect(convertAmount(100, "USD", "USD", {})).toBe(100);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("should convert minor units correctly", () => {
|
|
132
|
+
const rates = { MXN: scaledRate(20) };
|
|
133
|
+
expect(convertAmount(100, "USD", "MXN", rates)).toBe(2000);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe("Payment Provider Helpers", () => {
|
|
139
|
+
it("toStripeMoney should return lowercase currency and amount", () => {
|
|
140
|
+
const money = createMoney(150, "USD");
|
|
141
|
+
expect(toStripeMoney(money)).toEqual({ amount: 150, currency: "usd" });
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("toPaypalMoney should return decimal string and uppercase code", () => {
|
|
145
|
+
const money = createMoney(150, "USD");
|
|
146
|
+
expect(toPaypalMoney(money)).toEqual({
|
|
147
|
+
value: "1.50",
|
|
148
|
+
currency_code: "USD",
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("toAdyenMoney should return amount and uppercase code", () => {
|
|
153
|
+
const money = createMoney(150, "USD");
|
|
154
|
+
expect(toAdyenMoney(money)).toEqual({ value: 150, currency: "USD" });
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("toSquareMoney should return bigint amount and code", () => {
|
|
158
|
+
const money = createMoney(150, "USD");
|
|
159
|
+
expect(toSquareMoney(money)).toEqual({
|
|
160
|
+
amount: BigInt(150),
|
|
161
|
+
currency: "USD",
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe("Snapshots", () => {
|
|
167
|
+
it("should round-trip to/from money snapshot", () => {
|
|
168
|
+
const money = createMoney(123, "MXN");
|
|
169
|
+
const snapshot = toMoneySnapshot(money);
|
|
170
|
+
expect(snapshot).toEqual({ amount: 123, currency: "MXN", scale: 2 });
|
|
171
|
+
|
|
172
|
+
const restored = fromMoneySnapshot(snapshot);
|
|
173
|
+
expect(toSnapshot(restored)).toEqual(toSnapshot(money));
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("should preserve elevated scale", () => {
|
|
177
|
+
const _money = createMoney(123, "USD");
|
|
178
|
+
const snapshot = { amount: 12345, currency: "USD", scale: 4 };
|
|
179
|
+
const restored = fromMoneySnapshot(snapshot);
|
|
180
|
+
expect(toSnapshot(restored).scale).toBe(4);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
});
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
// sdk/core/src/money/money.ts
|
|
2
|
+
// Framework-agnostic money utilities using Dinero.js v2
|
|
3
|
+
// Consumers should never import Dinero directly — these helpers encapsulate it.
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
add as dineroAdd,
|
|
7
|
+
AED,
|
|
8
|
+
BRL,
|
|
9
|
+
CAD,
|
|
10
|
+
CNY,
|
|
11
|
+
convert as dineroConvert,
|
|
12
|
+
type Dinero,
|
|
13
|
+
dinero,
|
|
14
|
+
type DineroCurrency,
|
|
15
|
+
type DineroSnapshot,
|
|
16
|
+
EUR,
|
|
17
|
+
GBP,
|
|
18
|
+
INR,
|
|
19
|
+
JPY,
|
|
20
|
+
KRW,
|
|
21
|
+
multiply as dineroMultiply,
|
|
22
|
+
MXN,
|
|
23
|
+
subtract as dineroSubtract,
|
|
24
|
+
toDecimal,
|
|
25
|
+
// ISO 4217 currency definitions
|
|
26
|
+
toSnapshot,
|
|
27
|
+
transformScale as dineroTransformScale,
|
|
28
|
+
USD,
|
|
29
|
+
} from "dinero.js";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Map of ISO 4217 currency codes to Dinero currency objects
|
|
33
|
+
*/
|
|
34
|
+
export const CURRENCY_MAP: Record<string, DineroCurrency<number>> = {
|
|
35
|
+
USD,
|
|
36
|
+
EUR,
|
|
37
|
+
MXN,
|
|
38
|
+
JPY,
|
|
39
|
+
KRW,
|
|
40
|
+
CNY,
|
|
41
|
+
AED,
|
|
42
|
+
GBP,
|
|
43
|
+
CAD,
|
|
44
|
+
BRL,
|
|
45
|
+
INR,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Derived type of all built-in ISO 4217 currency codes.
|
|
50
|
+
* Enables IDE autocomplete while allowing any string for dynamic currencies.
|
|
51
|
+
*/
|
|
52
|
+
export type ISO4217Code = keyof typeof CURRENCY_MAP;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Scaled integer rate for Dinero.js conversion.
|
|
56
|
+
* Avoids floating-point errors by using integer amounts with a scale.
|
|
57
|
+
*/
|
|
58
|
+
export type ScaledRate = { amount: number; scale: number };
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Map of currency codes to their exchange rates relative to a base currency.
|
|
62
|
+
* Rates can be plain numbers (integers) or ScaledRate objects.
|
|
63
|
+
*/
|
|
64
|
+
export type RateMap = Record<string, ScaledRate | number>;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Converts a human-readable float rate (e.g., 17.23) into a ScaledRate.
|
|
68
|
+
* Defaults to 4 decimal places of precision if not specified.
|
|
69
|
+
*/
|
|
70
|
+
export function scaledRate(float: number, precision: number = 4): ScaledRate {
|
|
71
|
+
const factor = Math.pow(10, precision);
|
|
72
|
+
return {
|
|
73
|
+
amount: Math.round(float * factor),
|
|
74
|
+
scale: precision,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Register a new currency in CURRENCY_MAP atomically.
|
|
80
|
+
* Ensures the core registry and any store-level registries stay in sync.
|
|
81
|
+
*/
|
|
82
|
+
export function registerCurrency(
|
|
83
|
+
code: string,
|
|
84
|
+
currency: DineroCurrency<number>,
|
|
85
|
+
): void {
|
|
86
|
+
CURRENCY_MAP[code] = currency;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Normalizes an unknown input into a valid finite number.
|
|
91
|
+
* Handles null, undefined, bigints, and SurrealDB Decimal objects via toString().
|
|
92
|
+
*/
|
|
93
|
+
function toNumber(amount: unknown): number {
|
|
94
|
+
if (amount === null || amount === undefined) return 0;
|
|
95
|
+
if (typeof amount === "number") return Number.isFinite(amount) ? amount : 0;
|
|
96
|
+
if (typeof amount === "bigint") return Number(amount);
|
|
97
|
+
|
|
98
|
+
// Handle SurrealDB Decimal (which has a toString method) or other objects
|
|
99
|
+
if (typeof amount === "object") {
|
|
100
|
+
try {
|
|
101
|
+
// SurrealDB Decimal.toString() returns a numeric string
|
|
102
|
+
const str = String(amount);
|
|
103
|
+
const parsed = parseFloat(str);
|
|
104
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
105
|
+
} catch {
|
|
106
|
+
return 0;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const coerced = Number(amount);
|
|
111
|
+
return Number.isFinite(coerced) ? coerced : 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Normalizes an unknown input into a valid integer for Dinero.js.
|
|
116
|
+
* Exported so consumers can apply the same defensive coercion.
|
|
117
|
+
*/
|
|
118
|
+
export function safeAmount(amount: unknown): number {
|
|
119
|
+
return Math.round(toNumber(amount));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Converts a major-unit amount (e.g., pesos) to a minor-unit integer (e.g., centavos).
|
|
124
|
+
* Uses the currency's exponent from CURRENCY_MAP.
|
|
125
|
+
*/
|
|
126
|
+
export function toMinorUnit(
|
|
127
|
+
amount: unknown,
|
|
128
|
+
currencyCode: ISO4217Code | string,
|
|
129
|
+
): number {
|
|
130
|
+
const currency = CURRENCY_MAP[currencyCode];
|
|
131
|
+
if (!currency) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
`Unknown currency code: ${currencyCode}. Register it first via registerCurrency().`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
const base = Array.isArray(currency.base) ? currency.base[0] : currency.base;
|
|
137
|
+
const factor = Math.pow(Number(base), Number(currency.exponent));
|
|
138
|
+
return Math.round(toNumber(amount) * factor);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Create a Dinero monetary value from an amount.
|
|
143
|
+
* @param amount - Amount (normalized internally via safeAmount)
|
|
144
|
+
* @param currencyCode - ISO 4217 currency code (e.g., "USD")
|
|
145
|
+
*/
|
|
146
|
+
export function createMoney(
|
|
147
|
+
amount: unknown,
|
|
148
|
+
currencyCode: ISO4217Code | string,
|
|
149
|
+
): Dinero<number> {
|
|
150
|
+
const currency = CURRENCY_MAP[currencyCode];
|
|
151
|
+
if (!currency) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
`Unknown currency code: ${currencyCode}. Register it first via registerCurrency().`,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
return dinero({ amount: safeAmount(amount), currency });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Format a Dinero value as a locale-aware string
|
|
161
|
+
* @param money - Dinero monetary value
|
|
162
|
+
* @param locale - BCP 47 locale string (e.g., "en-US", "es-MX")
|
|
163
|
+
* @param currencyCode - ISO 4217 code for Intl.NumberFormat (e.g., "USD")
|
|
164
|
+
*/
|
|
165
|
+
export function formatMoney(
|
|
166
|
+
money: Dinero<number>,
|
|
167
|
+
locale: string = "en-US",
|
|
168
|
+
currencyCode?: string,
|
|
169
|
+
): string {
|
|
170
|
+
return toDecimal(money, ({ value, currency }) => {
|
|
171
|
+
const code = currencyCode ??
|
|
172
|
+
Object.entries(CURRENCY_MAP).find(([, c]) => c.code === currency.code)
|
|
173
|
+
?.[0] ??
|
|
174
|
+
currency.code;
|
|
175
|
+
|
|
176
|
+
return new Intl.NumberFormat(locale, {
|
|
177
|
+
style: "currency",
|
|
178
|
+
currency: code,
|
|
179
|
+
minimumFractionDigits: currency.exponent,
|
|
180
|
+
maximumFractionDigits: currency.exponent,
|
|
181
|
+
}).format(Number(value));
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Format a raw amount as a locale-aware currency string.
|
|
187
|
+
* Convenience wrapper around createMoney + formatMoney.
|
|
188
|
+
*/
|
|
189
|
+
export function formatAmount(
|
|
190
|
+
amount: unknown,
|
|
191
|
+
currencyCode: ISO4217Code | string,
|
|
192
|
+
locale: string = "en-US",
|
|
193
|
+
): string {
|
|
194
|
+
const money = createMoney(amount, currencyCode);
|
|
195
|
+
return formatMoney(money, locale, currencyCode);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Add two monetary values (must be same currency)
|
|
200
|
+
*/
|
|
201
|
+
export function addMoney(a: Dinero<number>, b: Dinero<number>): Dinero<number> {
|
|
202
|
+
return dineroAdd(a, b);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Subtract two monetary values (must be same currency)
|
|
207
|
+
*/
|
|
208
|
+
export function subtractMoney(
|
|
209
|
+
a: Dinero<number>,
|
|
210
|
+
b: Dinero<number>,
|
|
211
|
+
): Dinero<number> {
|
|
212
|
+
return dineroSubtract(a, b);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Multiply a monetary value by a factor
|
|
217
|
+
*/
|
|
218
|
+
export function multiplyMoney(
|
|
219
|
+
money: Dinero<number>,
|
|
220
|
+
factor: number,
|
|
221
|
+
): Dinero<number> {
|
|
222
|
+
return dineroMultiply(money, factor);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Converts a Dinero object to another currency.
|
|
227
|
+
* Both currencies must share the same base (base 10 for all standard ISO currencies).
|
|
228
|
+
*/
|
|
229
|
+
export function convertMoney(
|
|
230
|
+
money: Dinero<number>,
|
|
231
|
+
toCurrencyCode: string,
|
|
232
|
+
rates: RateMap,
|
|
233
|
+
): Dinero<number> {
|
|
234
|
+
const targetCurrency = CURRENCY_MAP[toCurrencyCode];
|
|
235
|
+
if (!targetCurrency) {
|
|
236
|
+
throw new Error(
|
|
237
|
+
`Unknown target currency: ${toCurrencyCode}. Register it first via registerCurrency().`,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const rate = rates[toCurrencyCode];
|
|
242
|
+
if (rate === undefined) {
|
|
243
|
+
throw new Error(`Missing exchange rate for currency: ${toCurrencyCode}`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
return dineroConvert(money, targetCurrency, { [toCurrencyCode]: rate });
|
|
248
|
+
} catch (err) {
|
|
249
|
+
if (err instanceof Error && err.message.includes("base")) {
|
|
250
|
+
throw new Error(
|
|
251
|
+
`Cannot convert between currencies with different bases. Ensure both are base 10.`,
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
throw err;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Converts a raw minor-unit amount between currencies.
|
|
260
|
+
* Returns the original amount if from and to currencies are the same.
|
|
261
|
+
*/
|
|
262
|
+
export function convertAmount(
|
|
263
|
+
amount: number,
|
|
264
|
+
fromCode: string,
|
|
265
|
+
toCode: string,
|
|
266
|
+
rates: RateMap,
|
|
267
|
+
): number {
|
|
268
|
+
if (fromCode === toCode) return amount;
|
|
269
|
+
const targetCurrency = CURRENCY_MAP[toCode];
|
|
270
|
+
if (!targetCurrency) {
|
|
271
|
+
throw new Error(`Unknown target currency: ${toCode}`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const sourceMoney = createMoney(amount, fromCode);
|
|
275
|
+
const convertedMoney = convertMoney(sourceMoney, toCode, rates);
|
|
276
|
+
|
|
277
|
+
// Normalize back to the target currency's standard exponent
|
|
278
|
+
const targetMoney = dineroTransformScale(
|
|
279
|
+
convertedMoney,
|
|
280
|
+
targetCurrency.exponent,
|
|
281
|
+
);
|
|
282
|
+
const snapshot = toSnapshot(targetMoney);
|
|
283
|
+
|
|
284
|
+
return snapshot.amount;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Converts Dinero object to a Stripe-compatible payload.
|
|
289
|
+
*/
|
|
290
|
+
export function toStripeMoney(money: Dinero<number>): {
|
|
291
|
+
amount: number;
|
|
292
|
+
currency: string;
|
|
293
|
+
} {
|
|
294
|
+
const snapshot = toSnapshot(money);
|
|
295
|
+
return {
|
|
296
|
+
amount: snapshot.amount,
|
|
297
|
+
currency: snapshot.currency.code.toLowerCase(),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Converts Dinero object to a PayPal-compatible payload.
|
|
303
|
+
*/
|
|
304
|
+
export function toPaypalMoney(money: Dinero<number>): {
|
|
305
|
+
value: string;
|
|
306
|
+
currency_code: string;
|
|
307
|
+
} {
|
|
308
|
+
const value = toDecimal(money);
|
|
309
|
+
const snapshot = toSnapshot(money);
|
|
310
|
+
return {
|
|
311
|
+
value,
|
|
312
|
+
currency_code: snapshot.currency.code,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Converts Dinero object to an Adyen-compatible payload.
|
|
318
|
+
*/
|
|
319
|
+
export function toAdyenMoney(money: Dinero<number>): {
|
|
320
|
+
value: number;
|
|
321
|
+
currency: string;
|
|
322
|
+
} {
|
|
323
|
+
const snapshot = toSnapshot(money);
|
|
324
|
+
return {
|
|
325
|
+
value: snapshot.amount,
|
|
326
|
+
currency: snapshot.currency.code.toUpperCase(),
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Converts Dinero object to a Square-compatible payload (uses BigInt).
|
|
332
|
+
*/
|
|
333
|
+
export function toSquareMoney(money: Dinero<number>): {
|
|
334
|
+
amount: bigint;
|
|
335
|
+
currency: string;
|
|
336
|
+
} {
|
|
337
|
+
const snapshot = toSnapshot(money);
|
|
338
|
+
return {
|
|
339
|
+
amount: BigInt(snapshot.amount),
|
|
340
|
+
currency: snapshot.currency.code,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Serializes a Dinero object into a plain object snapshot.
|
|
346
|
+
*/
|
|
347
|
+
export function toMoneySnapshot(money: Dinero<number>): {
|
|
348
|
+
amount: number;
|
|
349
|
+
currency: string;
|
|
350
|
+
scale: number;
|
|
351
|
+
} {
|
|
352
|
+
const snapshot = toSnapshot(money);
|
|
353
|
+
return {
|
|
354
|
+
amount: snapshot.amount,
|
|
355
|
+
currency: snapshot.currency.code,
|
|
356
|
+
scale: snapshot.scale,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Restores a Dinero object from a snapshot.
|
|
362
|
+
*/
|
|
363
|
+
export function fromMoneySnapshot(snapshot: {
|
|
364
|
+
amount: number;
|
|
365
|
+
currency: string;
|
|
366
|
+
scale?: number;
|
|
367
|
+
}): Dinero<number> {
|
|
368
|
+
const currency = CURRENCY_MAP[snapshot.currency];
|
|
369
|
+
if (!currency) {
|
|
370
|
+
throw new Error(
|
|
371
|
+
`Unknown currency: ${snapshot.currency}. Register it first.`,
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
return dinero({
|
|
375
|
+
amount: snapshot.amount,
|
|
376
|
+
currency,
|
|
377
|
+
scale: snapshot.scale ?? currency.exponent,
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Re-export types
|
|
382
|
+
export type { Dinero, DineroCurrency, DineroSnapshot };
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
|
+
import {
|
|
3
|
+
CONVERSION_STRATEGIES,
|
|
4
|
+
directConversion,
|
|
5
|
+
inverseConversion,
|
|
6
|
+
resolveRate,
|
|
7
|
+
triangularConversion,
|
|
8
|
+
} from "./strategies.ts";
|
|
9
|
+
|
|
10
|
+
describe("ConversionStrategies", () => {
|
|
11
|
+
describe("resolveRate", () => {
|
|
12
|
+
it("should pass through plain numbers", () => {
|
|
13
|
+
expect(resolveRate(20)).toBe(20);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("should resolve ScaledRate to float", () => {
|
|
17
|
+
expect(resolveRate({ amount: 172300, scale: 4 })).toBeCloseTo(17.23);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should handle zero scale", () => {
|
|
21
|
+
expect(resolveRate({ amount: 1199, scale: 0 })).toBe(1199);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("directConversion", () => {
|
|
26
|
+
it("should multiply amount by rate", () => {
|
|
27
|
+
// 100 USD cents * 20 = 2000 MXN centavos
|
|
28
|
+
expect(directConversion(100, 20)).toBe(2000);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should handle fractional rates", () => {
|
|
32
|
+
// 100 * 0.91 = 91
|
|
33
|
+
expect(directConversion(100, 0.91)).toBe(91);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should handle zero amount", () => {
|
|
37
|
+
expect(directConversion(0, 20)).toBe(0);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("should handle negative amounts", () => {
|
|
41
|
+
expect(directConversion(-100, 20)).toBe(-2000);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("should handle large numbers", () => {
|
|
45
|
+
expect(directConversion(1_000_000, 20)).toBe(20_000_000);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should round to nearest integer", () => {
|
|
49
|
+
// 100 * 17.23 = 1723
|
|
50
|
+
expect(directConversion(100, 17.23)).toBe(1723);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe("inverseConversion", () => {
|
|
55
|
+
it("should divide amount by rate", () => {
|
|
56
|
+
// 2000 MXN / 20 = 100 USD
|
|
57
|
+
expect(inverseConversion(2000, 20)).toBe(100);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("should handle fractional results (rounded)", () => {
|
|
61
|
+
// 100 / 3 ≈ 33.33 → 33
|
|
62
|
+
expect(inverseConversion(100, 3)).toBe(33);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("should throw on zero rate", () => {
|
|
66
|
+
expect(() => inverseConversion(100, 0)).toThrow("rate of zero");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("should handle negative amounts", () => {
|
|
70
|
+
expect(inverseConversion(-2000, 20)).toBe(-100);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("should handle zero amount", () => {
|
|
74
|
+
expect(inverseConversion(0, 20)).toBe(0);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe("triangularConversion", () => {
|
|
79
|
+
it("should convert through base: MXN → USD → EUR", () => {
|
|
80
|
+
// 2000 MXN, 1 USD = 20 MXN, 1 USD = 0.91 EUR
|
|
81
|
+
// 2000 / 20 = 100 USD → 100 * 0.91 = 91 EUR
|
|
82
|
+
expect(triangularConversion(2000, 20, 0.91)).toBe(91);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("should handle zero amount", () => {
|
|
86
|
+
expect(triangularConversion(0, 20, 0.91)).toBe(0);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("should throw when source-to-base rate is zero", () => {
|
|
90
|
+
expect(() => triangularConversion(100, 0, 0.91)).toThrow(
|
|
91
|
+
"source-to-base rate is zero",
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("should handle large cross-rates", () => {
|
|
96
|
+
// 1000 BRL, 1 USD = 5 BRL, 1 USD = 150 JPY
|
|
97
|
+
// 1000 / 5 = 200 USD → 200 * 150 = 30000 JPY
|
|
98
|
+
expect(triangularConversion(1000, 5, 150)).toBe(30000);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("should handle negative amounts", () => {
|
|
102
|
+
expect(triangularConversion(-2000, 20, 0.91)).toBe(-91);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe("CONVERSION_STRATEGIES registry", () => {
|
|
107
|
+
it("should have direct, inverse, and triangular strategies", () => {
|
|
108
|
+
expect(CONVERSION_STRATEGIES).toHaveProperty("direct");
|
|
109
|
+
expect(CONVERSION_STRATEGIES).toHaveProperty("inverse");
|
|
110
|
+
expect(CONVERSION_STRATEGIES).toHaveProperty("triangular");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("direct strategy matches standalone function", () => {
|
|
114
|
+
expect(CONVERSION_STRATEGIES["direct"](100, 20)).toBe(
|
|
115
|
+
directConversion(100, 20),
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("inverse strategy matches standalone function", () => {
|
|
120
|
+
expect(CONVERSION_STRATEGIES["inverse"](2000, 20)).toBe(
|
|
121
|
+
inverseConversion(2000, 20),
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("triangular strategy matches standalone function", () => {
|
|
126
|
+
expect(CONVERSION_STRATEGIES["triangular"](2000, 20, 0.91)).toBe(
|
|
127
|
+
triangularConversion(2000, 20, 0.91),
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("should be extensible", () => {
|
|
132
|
+
CONVERSION_STRATEGIES["custom"] = (
|
|
133
|
+
amount: number,
|
|
134
|
+
rate: number,
|
|
135
|
+
): number => Math.round(amount * rate * 1.01); // 1% fee
|
|
136
|
+
|
|
137
|
+
expect(CONVERSION_STRATEGIES["custom"](100, 20)).toBe(2020);
|
|
138
|
+
|
|
139
|
+
// Clean up
|
|
140
|
+
delete CONVERSION_STRATEGIES["custom"];
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
});
|