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,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
|
+
});
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// sdk/core/src/money/money-primitive.ts
|
|
2
|
+
// Immutable Value Object for monetary amounts.
|
|
3
|
+
// Zero Svelte dependencies — pure TypeScript class.
|
|
4
|
+
|
|
5
|
+
import { CURRENCY_MAP, type ISO4217Code } from "./money.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Serializable representation of a MoneyPrimitive.
|
|
9
|
+
* Used for persistence drivers and JSON round-trips.
|
|
10
|
+
*/
|
|
11
|
+
export interface MoneyJSON {
|
|
12
|
+
amount: number;
|
|
13
|
+
currencyCode: string;
|
|
14
|
+
scale: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Immutable Value Object that encapsulates a monetary amount, its currency,
|
|
19
|
+
* and the scale (number of decimal places).
|
|
20
|
+
*
|
|
21
|
+
* Amounts are always stored in minor units (e.g., cents for USD, centavos for MXN).
|
|
22
|
+
* The scale is derived from the currency's exponent in CURRENCY_MAP.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const price = MoneyPrimitive.fromMajor(12.99, "USD");
|
|
27
|
+
* price.minor; // 1299
|
|
28
|
+
* price.major; // 12.99
|
|
29
|
+
* price.format("en-US"); // "$12.99"
|
|
30
|
+
*
|
|
31
|
+
* const json = price.toJSON();
|
|
32
|
+
* const restored = MoneyPrimitive.fromJSON(json);
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export class MoneyPrimitive {
|
|
36
|
+
/** Raw amount in minor units (e.g., cents). */
|
|
37
|
+
readonly amount: number;
|
|
38
|
+
/** ISO 4217 currency code. */
|
|
39
|
+
readonly currencyCode: string;
|
|
40
|
+
/** Number of decimal places for this currency (e.g., 2 for USD, 0 for JPY). */
|
|
41
|
+
readonly scale: number;
|
|
42
|
+
|
|
43
|
+
private constructor(amount: number, currencyCode: string, scale: number) {
|
|
44
|
+
this.amount = Math.round(amount);
|
|
45
|
+
this.currencyCode = currencyCode;
|
|
46
|
+
this.scale = scale;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ── Factories ────────────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Create a MoneyPrimitive from a minor-unit integer amount.
|
|
53
|
+
* @param amount - Amount in minor units (e.g., 1299 for $12.99)
|
|
54
|
+
* @param currencyCode - ISO 4217 currency code
|
|
55
|
+
*/
|
|
56
|
+
static fromMinor(
|
|
57
|
+
amount: number,
|
|
58
|
+
currencyCode: ISO4217Code | string,
|
|
59
|
+
): MoneyPrimitive {
|
|
60
|
+
const scale = MoneyPrimitive.resolveScale(currencyCode);
|
|
61
|
+
return new MoneyPrimitive(amount, currencyCode, scale);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Create a MoneyPrimitive from a major-unit float amount.
|
|
66
|
+
* @param amount - Amount in major units (e.g., 12.99 for $12.99)
|
|
67
|
+
* @param currencyCode - ISO 4217 currency code
|
|
68
|
+
*/
|
|
69
|
+
static fromMajor(
|
|
70
|
+
amount: number,
|
|
71
|
+
currencyCode: ISO4217Code | string,
|
|
72
|
+
): MoneyPrimitive {
|
|
73
|
+
const scale = MoneyPrimitive.resolveScale(currencyCode);
|
|
74
|
+
const factor = Math.pow(10, scale);
|
|
75
|
+
return new MoneyPrimitive(Math.round(amount * factor), currencyCode, scale);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Restore a MoneyPrimitive from its JSON representation.
|
|
80
|
+
*/
|
|
81
|
+
static fromJSON(json: MoneyJSON): MoneyPrimitive {
|
|
82
|
+
return new MoneyPrimitive(json.amount, json.currencyCode, json.scale);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ── Computed Getters ─────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
/** Amount in minor units (alias for `amount`). */
|
|
88
|
+
get minor(): number {
|
|
89
|
+
return this.amount;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Amount in major units (e.g., 12.99 for 1299 cents). */
|
|
93
|
+
get major(): number {
|
|
94
|
+
if (this.scale === 0) return this.amount;
|
|
95
|
+
return this.amount / Math.pow(10, this.scale);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ── Formatting ───────────────────────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Format this monetary value as a locale-aware currency string.
|
|
102
|
+
* @param locale - BCP 47 locale string (default: "en-US")
|
|
103
|
+
*/
|
|
104
|
+
format(locale: string = "en-US"): string {
|
|
105
|
+
return new Intl.NumberFormat(locale, {
|
|
106
|
+
style: "currency",
|
|
107
|
+
currency: this.currencyCode,
|
|
108
|
+
minimumFractionDigits: this.scale,
|
|
109
|
+
maximumFractionDigits: this.scale,
|
|
110
|
+
}).format(this.major);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ── Arithmetic (returns new instances — immutability) ────────────────────
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Add another MoneyPrimitive (must be same currency).
|
|
117
|
+
* Returns a new MoneyPrimitive.
|
|
118
|
+
*/
|
|
119
|
+
add(other: MoneyPrimitive): MoneyPrimitive {
|
|
120
|
+
this.assertSameCurrency(other);
|
|
121
|
+
return new MoneyPrimitive(
|
|
122
|
+
this.amount + other.amount,
|
|
123
|
+
this.currencyCode,
|
|
124
|
+
this.scale,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Subtract another MoneyPrimitive (must be same currency).
|
|
130
|
+
* Returns a new MoneyPrimitive.
|
|
131
|
+
*/
|
|
132
|
+
subtract(other: MoneyPrimitive): MoneyPrimitive {
|
|
133
|
+
this.assertSameCurrency(other);
|
|
134
|
+
return new MoneyPrimitive(
|
|
135
|
+
this.amount - other.amount,
|
|
136
|
+
this.currencyCode,
|
|
137
|
+
this.scale,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Multiply by a scalar factor. Returns a new MoneyPrimitive.
|
|
143
|
+
*/
|
|
144
|
+
multiply(factor: number): MoneyPrimitive {
|
|
145
|
+
return new MoneyPrimitive(
|
|
146
|
+
Math.round(this.amount * factor),
|
|
147
|
+
this.currencyCode,
|
|
148
|
+
this.scale,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ── Comparison ───────────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Value equality — two MoneyPrimitives are equal if they have the same
|
|
156
|
+
* amount, currency code, and scale.
|
|
157
|
+
*/
|
|
158
|
+
equals(other: MoneyPrimitive): boolean {
|
|
159
|
+
return (
|
|
160
|
+
this.amount === other.amount &&
|
|
161
|
+
this.currencyCode === other.currencyCode &&
|
|
162
|
+
this.scale === other.scale
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Returns true if this amount is zero.
|
|
168
|
+
*/
|
|
169
|
+
isZero(): boolean {
|
|
170
|
+
return this.amount === 0;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Returns true if this amount is negative.
|
|
175
|
+
*/
|
|
176
|
+
isNegative(): boolean {
|
|
177
|
+
return this.amount < 0;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ── Serialization ────────────────────────────────────────────────────────
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Serialize to a plain JSON-compatible object.
|
|
184
|
+
*/
|
|
185
|
+
toJSON(): MoneyJSON {
|
|
186
|
+
return {
|
|
187
|
+
amount: this.amount,
|
|
188
|
+
currencyCode: this.currencyCode,
|
|
189
|
+
scale: this.scale,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
toString(): string {
|
|
194
|
+
return `MoneyPrimitive(${this.amount} ${this.currencyCode} scale=${this.scale})`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ── Private Helpers ──────────────────────────────────────────────────────
|
|
198
|
+
|
|
199
|
+
private assertSameCurrency(other: MoneyPrimitive): void {
|
|
200
|
+
if (this.currencyCode !== other.currencyCode) {
|
|
201
|
+
throw new Error(
|
|
202
|
+
`Currency mismatch: cannot operate on ${this.currencyCode} and ${other.currencyCode}`,
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Resolves the scale (exponent) for a currency code from CURRENCY_MAP.
|
|
209
|
+
* Throws if the currency is not registered.
|
|
210
|
+
*/
|
|
211
|
+
private static resolveScale(currencyCode: string): number {
|
|
212
|
+
const currency = CURRENCY_MAP[currencyCode];
|
|
213
|
+
if (!currency) {
|
|
214
|
+
throw new Error(
|
|
215
|
+
`Unknown currency code: ${currencyCode}. Register it first via registerCurrency().`,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
return currency.exponent;
|
|
219
|
+
}
|
|
220
|
+
}
|