rune-lab 0.3.0 → 0.4.0
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/README.md +1 -1
- package/dist/core/design-tokens/props.d.ts +52 -0
- package/dist/core/design-tokens/props.d.ts.map +1 -0
- package/dist/core/design-tokens/props.js +34 -0
- package/dist/core/exchange-rate/strategies.d.ts +52 -0
- package/dist/core/exchange-rate/strategies.d.ts.map +1 -0
- package/dist/core/exchange-rate/strategies.js +72 -0
- package/dist/core/index.d.ts +8 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +8 -3
- package/dist/core/internal/message-resolver.d.ts +1 -1
- package/dist/core/internal/message-resolver.d.ts.map +1 -1
- package/dist/core/layout/types.d.ts +60 -0
- package/dist/core/layout/types.d.ts.map +1 -0
- package/dist/core/layout/types.js +4 -0
- package/dist/core/money/index.d.ts +1 -1
- package/dist/core/money/index.d.ts.map +1 -1
- package/dist/core/money/index.js +1 -1
- package/dist/core/money/money-primitive.d.ts +101 -0
- package/dist/core/money/money-primitive.d.ts.map +1 -0
- package/dist/core/money/money-primitive.js +161 -0
- package/dist/core/money/money.d.ts +74 -2
- package/dist/core/money/money.d.ts.map +1 -1
- package/dist/core/money/money.js +120 -2
- package/dist/core/shortcuts/types.d.ts +60 -0
- package/dist/core/shortcuts/types.d.ts.map +1 -0
- package/dist/core/shortcuts/types.js +4 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +6 -4
- package/dist/state/api.svelte.js +2 -2
- package/dist/state/app.svelte.js +1 -1
- package/dist/state/auth/index.d.ts +2 -2
- package/dist/state/auth/index.js +1 -1
- package/dist/state/auth/session.svelte.d.ts +1 -1
- package/dist/state/auth/session.svelte.js +7 -5
- package/dist/state/auth/types.d.ts +1 -1
- package/dist/state/cart.svelte.d.ts +1 -1
- package/dist/state/cart.svelte.js +1 -1
- package/dist/state/commands.svelte.d.ts +7 -7
- package/dist/state/commands.svelte.js +1 -1
- package/dist/state/composables/useMoney.d.ts +19 -3
- package/dist/state/composables/useMoney.js +70 -6
- package/dist/state/composables/useMoneyFilter.d.ts +20 -0
- package/dist/state/composables/useMoneyFilter.js +81 -0
- package/dist/state/composables/usePersistence.d.ts +1 -1
- package/dist/state/composables/usePersistence.js +1 -1
- package/dist/state/composables/useRuneLab.d.ts +1 -1
- package/dist/state/composables/useRuneLab.js +2 -2
- package/dist/state/composables/useShortcuts.d.ts +33 -0
- package/dist/state/composables/useShortcuts.js +75 -0
- package/dist/state/context.d.ts +1 -0
- package/dist/state/context.js +1 -0
- package/dist/state/createConfigStore.svelte.d.ts +4 -31
- package/dist/state/createConfigStore.svelte.js +62 -51
- package/dist/state/currency.svelte.d.ts +13 -9
- package/dist/state/currency.svelte.js +26 -10
- package/dist/state/currency.test.d.ts +1 -0
- package/dist/state/currency.test.js +35 -0
- package/dist/state/exchange-rate.svelte.d.ts +43 -0
- package/dist/state/exchange-rate.svelte.js +145 -0
- package/dist/state/exchange-rate.test.d.ts +1 -0
- package/dist/state/exchange-rate.test.js +75 -0
- package/dist/state/index.d.ts +26 -19
- package/dist/state/index.js +25 -18
- package/dist/state/language.svelte.d.ts +3 -10
- package/dist/state/language.svelte.js +4 -5
- package/dist/state/layout.svelte.d.ts +1 -1
- package/dist/state/layout.svelte.js +4 -4
- package/dist/state/persistence/drivers.d.ts +1 -1
- package/dist/state/persistence/drivers.js +9 -7
- package/dist/state/persistence/drivers.test.d.ts +1 -0
- package/dist/state/persistence/drivers.test.js +79 -0
- package/dist/state/persistence/provider.d.ts +23 -0
- package/dist/state/persistence/provider.js +43 -0
- package/dist/state/persistence/provider.test.d.ts +1 -0
- package/dist/state/persistence/provider.test.js +51 -0
- package/dist/state/registry/index.d.ts +44 -0
- package/dist/state/registry/index.js +58 -0
- package/dist/state/registry/registry.test.d.ts +1 -0
- package/dist/state/registry/registry.test.js +112 -0
- package/dist/state/registry/types.d.ts +20 -0
- package/dist/state/registry/types.js +3 -0
- package/dist/state/shortcuts.svelte.js +4 -4
- package/dist/state/theme.svelte.d.ts +3 -10
- package/dist/state/theme.svelte.js +8 -8
- package/dist/state/toast-bridge.d.ts +1 -1
- package/dist/state/toast.svelte.js +1 -1
- package/dist/ui/components/ApiMonitor.svelte +2 -2
- package/dist/ui/components/Icon.svelte +1 -1
- package/dist/ui/components/RuneProvider.svelte +28 -8
- package/dist/ui/components/RuneProvider.svelte.d.ts +12 -5
- package/dist/ui/components/Toaster.svelte +1 -1
- package/dist/ui/components/money/MoneyDisplay.svelte +91 -18
- package/dist/ui/components/money/MoneyDisplay.svelte.d.ts +15 -3
- package/dist/ui/components/money/MoneyDisplay.svelte.test.d.ts +1 -1
- package/dist/ui/components/money/MoneyDisplay.svelte.test.js +45 -2
- package/dist/ui/components/money/MoneyInput.svelte +123 -42
- package/dist/ui/components/money/MoneyInput.svelte.d.ts +14 -5
- package/dist/ui/features/command-palette/CommandPalette.svelte +3 -3
- package/dist/ui/features/config/APP_CONFIGURATIONS.d.ts +29 -0
- package/dist/ui/features/config/APP_CONFIGURATIONS.js +38 -0
- package/dist/ui/features/config/CurrencySelector.svelte +10 -36
- package/dist/ui/features/config/LanguageSelector.svelte +10 -33
- package/dist/ui/features/config/ResourceSelector.svelte +92 -0
- package/dist/ui/features/config/ResourceSelector.svelte.d.ts +25 -0
- package/dist/ui/features/config/ThemeSelector.svelte +11 -34
- package/dist/ui/features/shortcuts/ShortcutBinder.svelte +17 -0
- package/dist/ui/features/shortcuts/ShortcutBinder.svelte.d.ts +7 -0
- package/dist/ui/features/shortcuts/ShortcutPalette.svelte +3 -3
- package/dist/ui/index.d.ts +5 -1
- package/dist/ui/index.js +8 -3
- package/dist/ui/layout/ConnectedNavigationPanel.svelte +7 -8
- package/dist/ui/layout/ConnectedNavigationPanel.svelte.d.ts +1 -1
- package/dist/ui/layout/ConnectedWorkspaceStrip.svelte +5 -3
- package/dist/ui/layout/ConnectedWorkspaceStrip.svelte.d.ts +1 -1
- package/dist/ui/layout/NavigationPanel.svelte +1 -1
- package/dist/ui/layout/NavigationPanel.svelte.d.ts +1 -1
- package/dist/ui/layout/WorkspaceLayout.svelte +9 -1
- package/dist/ui/layout/WorkspaceLayout.svelte.d.ts +7 -0
- package/dist/ui/layout/WorkspaceStrip.svelte +1 -1
- package/dist/ui/layout/WorkspaceStrip.svelte.d.ts +1 -1
- package/dist/ui/layout/connection-factory.d.ts +50 -0
- package/dist/ui/layout/connection-factory.js +58 -0
- package/dist/ui/layout/index.d.ts +2 -2
- package/dist/ui/layout/index.js +1 -1
- package/dist/ui/paraglide/README.md +53 -0
- package/dist/ui/paraglide/runtime.d.ts +105 -124
- package/dist/ui/paraglide/runtime.js +162 -127
- package/dist/ui/paraglide/server.d.ts +6 -17
- package/dist/ui/paraglide/server.js +11 -20
- package/dist/ui/primitives/DatePicker.svelte +1 -1
- package/package.json +8 -8
- package/dist/state/daisyui.d.ts +0 -4
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<h1 align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/Yrrrrrf/rune-lab/main/static/img/rune.png" alt="Rune Lab Icon" width="128" height="128"
|
|
2
|
+
<img src="https://raw.githubusercontent.com/Yrrrrrf/rune-lab/main/src/sdk/ui/static/img/rune.png" alt="Rune Lab Icon" width="128" height="128" descripti``on="Icon representing the Svelte Runes system">
|
|
3
3
|
<div align="center">Rune Lab</div>
|
|
4
4
|
</h1>
|
|
5
5
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** Standard size scale used across all components. */
|
|
2
|
+
export type SizeToken = "xs" | "sm" | "md" | "lg" | "xl";
|
|
3
|
+
/** Mixin interface for components that accept a size prop. */
|
|
4
|
+
export interface WithSizing {
|
|
5
|
+
/** Visual size variant. Defaults to "md" if omitted. */
|
|
6
|
+
size?: SizeToken;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Semantic variant tokens aligned with DaisyUI's theme system.
|
|
10
|
+
* Kept as a union type for TypeScript inference; consumers can extend via
|
|
11
|
+
* `string & {}` if they need custom variants.
|
|
12
|
+
*/
|
|
13
|
+
export type VariantToken = "primary" | "secondary" | "accent" | "neutral" | "ghost" | "info" | "success" | "warning" | "error";
|
|
14
|
+
/** Mixin interface for components that accept a variant prop. */
|
|
15
|
+
export interface WithVariant {
|
|
16
|
+
/** Semantic color variant. */
|
|
17
|
+
variant?: VariantToken;
|
|
18
|
+
}
|
|
19
|
+
/** Mixin interface for components that accept a custom CSS class. */
|
|
20
|
+
export interface WithClass {
|
|
21
|
+
/** Additional CSS class string to merge with component classes. */
|
|
22
|
+
class?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Convenience intersection of all design-token mixins.
|
|
26
|
+
* Use when a component supports size, variant, and custom class.
|
|
27
|
+
*/
|
|
28
|
+
export type WithDesignTokens = WithSizing & WithVariant & WithClass;
|
|
29
|
+
/**
|
|
30
|
+
* Maps a size token to a CSS class string using a component-specific map.
|
|
31
|
+
* Returns the mapped class or an empty string for unknown tokens.
|
|
32
|
+
*
|
|
33
|
+
* @param size - The size token to resolve
|
|
34
|
+
* @param classMap - Component-specific mapping of tokens to CSS classes
|
|
35
|
+
* @param fallback - Fallback size if `size` is undefined (default: "md")
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* const btnSizeMap = { xs: "btn-xs", sm: "btn-sm", md: "btn-md", lg: "btn-lg", xl: "btn-xl" };
|
|
40
|
+
* resolveSize("lg", btnSizeMap); // "btn-lg"
|
|
41
|
+
* resolveSize(undefined, btnSizeMap); // "btn-md"
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveSize(size: SizeToken | undefined, classMap: Partial<Record<SizeToken, string>>, fallback?: SizeToken): string;
|
|
45
|
+
/**
|
|
46
|
+
* Maps a variant token to a CSS class string using a component-specific map.
|
|
47
|
+
*
|
|
48
|
+
* @param variant - The variant token to resolve
|
|
49
|
+
* @param classMap - Component-specific mapping of tokens to CSS classes
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolveVariant(variant: VariantToken | undefined, classMap: Partial<Record<VariantToken, string>>): string;
|
|
52
|
+
//# sourceMappingURL=props.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"props.d.ts","sourceRoot":"","sources":["../../../src/sdk/core/src/design-tokens/props.ts"],"names":[],"mappings":"AAMA,sDAAsD;AACtD,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEzD,8DAA8D;AAC9D,MAAM,WAAW,UAAU;IACzB,wDAAwD;IACxD,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAID;;;;GAIG;AACH,MAAM,MAAM,YAAY,GACpB,SAAS,GACT,WAAW,GACX,QAAQ,GACR,SAAS,GACT,OAAO,GACP,MAAM,GACN,SAAS,GACT,SAAS,GACT,OAAO,CAAC;AAEZ,iEAAiE;AACjE,MAAM,WAAW,WAAW;IAC1B,8BAA8B;IAC9B,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAID,qEAAqE;AACrE,MAAM,WAAW,SAAS;IACxB,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,WAAW,GAAG,SAAS,CAAC;AAIpE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,SAAS,GAAG,SAAS,EAC3B,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,EAC5C,QAAQ,GAAE,SAAgB,GACzB,MAAM,CAGR;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,YAAY,GAAG,SAAS,EACjC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,GAC9C,MAAM,CAGR"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// sdk/core/src/design-tokens/props.ts
|
|
2
|
+
// Canonical shared prop interfaces for design tokens.
|
|
3
|
+
// No CSS-framework-specific logic here — class maps live in sdk/ui.
|
|
4
|
+
// ── Resolution Utilities ───────────────────────────────────────────────────────
|
|
5
|
+
/**
|
|
6
|
+
* Maps a size token to a CSS class string using a component-specific map.
|
|
7
|
+
* Returns the mapped class or an empty string for unknown tokens.
|
|
8
|
+
*
|
|
9
|
+
* @param size - The size token to resolve
|
|
10
|
+
* @param classMap - Component-specific mapping of tokens to CSS classes
|
|
11
|
+
* @param fallback - Fallback size if `size` is undefined (default: "md")
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const btnSizeMap = { xs: "btn-xs", sm: "btn-sm", md: "btn-md", lg: "btn-lg", xl: "btn-xl" };
|
|
16
|
+
* resolveSize("lg", btnSizeMap); // "btn-lg"
|
|
17
|
+
* resolveSize(undefined, btnSizeMap); // "btn-md"
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function resolveSize(size, classMap, fallback = "md") {
|
|
21
|
+
const token = size ?? fallback;
|
|
22
|
+
return classMap[token] ?? "";
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Maps a variant token to a CSS class string using a component-specific map.
|
|
26
|
+
*
|
|
27
|
+
* @param variant - The variant token to resolve
|
|
28
|
+
* @param classMap - Component-specific mapping of tokens to CSS classes
|
|
29
|
+
*/
|
|
30
|
+
export function resolveVariant(variant, classMap) {
|
|
31
|
+
if (!variant)
|
|
32
|
+
return "";
|
|
33
|
+
return classMap[variant] ?? "";
|
|
34
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ScaledRate } from "../money/money.ts";
|
|
2
|
+
/**
|
|
3
|
+
* A pure function that converts a minor-unit amount using a rate.
|
|
4
|
+
* All strategy functions must be deterministic and side-effect-free.
|
|
5
|
+
*/
|
|
6
|
+
export type ConversionFn = (amount: number, ...rates: number[]) => number;
|
|
7
|
+
/**
|
|
8
|
+
* Resolves a ScaledRate or plain number into a float rate value.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveRate(rate: ScaledRate | number): number;
|
|
11
|
+
/**
|
|
12
|
+
* Direct conversion: base currency → target currency.
|
|
13
|
+
* Multiplies the amount by the rate.
|
|
14
|
+
*
|
|
15
|
+
* @param amount - Minor-unit amount in the source currency
|
|
16
|
+
* @param rate - Conversion rate (1 source = `rate` target)
|
|
17
|
+
* @returns Minor-unit amount in the target currency
|
|
18
|
+
*/
|
|
19
|
+
export declare function directConversion(amount: number, rate: number): number;
|
|
20
|
+
/**
|
|
21
|
+
* Inverse conversion: target currency → base currency.
|
|
22
|
+
* Divides the amount by the rate.
|
|
23
|
+
*
|
|
24
|
+
* @param amount - Minor-unit amount in the source currency
|
|
25
|
+
* @param rate - Conversion rate of source relative to base (1 base = `rate` source)
|
|
26
|
+
* @returns Minor-unit amount in the base currency
|
|
27
|
+
*/
|
|
28
|
+
export declare function inverseConversion(amount: number, rate: number): number;
|
|
29
|
+
/**
|
|
30
|
+
* Triangular conversion: source → base → target (cross-rate).
|
|
31
|
+
* First divides by the source-to-base rate, then multiplies by the base-to-target rate.
|
|
32
|
+
*
|
|
33
|
+
* @param amount - Minor-unit amount in the source currency
|
|
34
|
+
* @param rateSourceToBase - Rate of source relative to base (1 base = `rate` source)
|
|
35
|
+
* @param rateBaseToTarget - Rate of base to target (1 base = `rate` target)
|
|
36
|
+
* @returns Minor-unit amount in the target currency
|
|
37
|
+
*/
|
|
38
|
+
export declare function triangularConversion(amount: number, rateSourceToBase: number, rateBaseToTarget: number): number;
|
|
39
|
+
/**
|
|
40
|
+
* Named map of conversion strategies.
|
|
41
|
+
* Consumer code and the ExchangeRateStore can select a strategy by name.
|
|
42
|
+
* Extensible: register additional strategies before store initialization.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* CONVERSION_STRATEGIES["crypto"] = (amount, rate) => {
|
|
47
|
+
* // 18-decimal precision logic
|
|
48
|
+
* };
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare const CONVERSION_STRATEGIES: Record<string, ConversionFn>;
|
|
52
|
+
//# sourceMappingURL=strategies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strategies.d.ts","sourceRoot":"","sources":["../../../src/sdk/core/src/exchange-rate/strategies.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIpD;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC;AAE1E;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,CAG7D;AAID;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAErE;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKtE;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,EACxB,gBAAgB,EAAE,MAAM,GACvB,MAAM,CAQR;AAID;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAM9D,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// sdk/core/src/exchange-rate/strategies.ts
|
|
2
|
+
// Pure conversion strategy functions — no store or context dependencies.
|
|
3
|
+
// Extracted from ExchangeRateStore to enable independent testing and extensibility.
|
|
4
|
+
/**
|
|
5
|
+
* Resolves a ScaledRate or plain number into a float rate value.
|
|
6
|
+
*/
|
|
7
|
+
export function resolveRate(rate) {
|
|
8
|
+
if (typeof rate === "number")
|
|
9
|
+
return rate;
|
|
10
|
+
return rate.amount / Math.pow(10, rate.scale);
|
|
11
|
+
}
|
|
12
|
+
// ── Strategy Functions ─────────────────────────────────────────────────────────
|
|
13
|
+
/**
|
|
14
|
+
* Direct conversion: base currency → target currency.
|
|
15
|
+
* Multiplies the amount by the rate.
|
|
16
|
+
*
|
|
17
|
+
* @param amount - Minor-unit amount in the source currency
|
|
18
|
+
* @param rate - Conversion rate (1 source = `rate` target)
|
|
19
|
+
* @returns Minor-unit amount in the target currency
|
|
20
|
+
*/
|
|
21
|
+
export function directConversion(amount, rate) {
|
|
22
|
+
return Math.round(amount * rate);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Inverse conversion: target currency → base currency.
|
|
26
|
+
* Divides the amount by the rate.
|
|
27
|
+
*
|
|
28
|
+
* @param amount - Minor-unit amount in the source currency
|
|
29
|
+
* @param rate - Conversion rate of source relative to base (1 base = `rate` source)
|
|
30
|
+
* @returns Minor-unit amount in the base currency
|
|
31
|
+
*/
|
|
32
|
+
export function inverseConversion(amount, rate) {
|
|
33
|
+
if (rate === 0) {
|
|
34
|
+
throw new Error("Cannot perform inverse conversion with a rate of zero.");
|
|
35
|
+
}
|
|
36
|
+
return Math.round(amount / rate);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Triangular conversion: source → base → target (cross-rate).
|
|
40
|
+
* First divides by the source-to-base rate, then multiplies by the base-to-target rate.
|
|
41
|
+
*
|
|
42
|
+
* @param amount - Minor-unit amount in the source currency
|
|
43
|
+
* @param rateSourceToBase - Rate of source relative to base (1 base = `rate` source)
|
|
44
|
+
* @param rateBaseToTarget - Rate of base to target (1 base = `rate` target)
|
|
45
|
+
* @returns Minor-unit amount in the target currency
|
|
46
|
+
*/
|
|
47
|
+
export function triangularConversion(amount, rateSourceToBase, rateBaseToTarget) {
|
|
48
|
+
if (rateSourceToBase === 0) {
|
|
49
|
+
throw new Error("Cannot perform triangular conversion: source-to-base rate is zero.");
|
|
50
|
+
}
|
|
51
|
+
const amountInBase = amount / rateSourceToBase;
|
|
52
|
+
return Math.round(amountInBase * rateBaseToTarget);
|
|
53
|
+
}
|
|
54
|
+
// ── Strategy Registry ──────────────────────────────────────────────────────────
|
|
55
|
+
/**
|
|
56
|
+
* Named map of conversion strategies.
|
|
57
|
+
* Consumer code and the ExchangeRateStore can select a strategy by name.
|
|
58
|
+
* Extensible: register additional strategies before store initialization.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* CONVERSION_STRATEGIES["crypto"] = (amount, rate) => {
|
|
63
|
+
* // 18-decimal precision logic
|
|
64
|
+
* };
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export const CONVERSION_STRATEGIES = {
|
|
68
|
+
direct: directConversion,
|
|
69
|
+
inverse: inverseConversion,
|
|
70
|
+
// triangularConversion takes 2 rates — wrapped for the registry signature
|
|
71
|
+
triangular: (amount, ...rates) => triangularConversion(amount, rates[0], rates[1]),
|
|
72
|
+
};
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
export * from "./internal/message-resolver";
|
|
2
|
-
export * from "./persistence/types";
|
|
3
|
-
export * from "./money/index";
|
|
1
|
+
export * from "./internal/message-resolver.ts";
|
|
2
|
+
export * from "./persistence/types.ts";
|
|
3
|
+
export * from "./money/index.ts";
|
|
4
|
+
export { type MoneyJSON, MoneyPrimitive } from "./money/money-primitive.ts";
|
|
5
|
+
export * from "./exchange-rate/strategies.ts";
|
|
6
|
+
export * from "./design-tokens/props.ts";
|
|
7
|
+
export * from "./layout/types.ts";
|
|
8
|
+
export * from "./shortcuts/types.ts";
|
|
4
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/core/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sdk/core/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sdk/core/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gCAAgC,CAAC;AAE/C,cAAc,wBAAwB,CAAC;AAEvC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,KAAK,SAAS,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5E,cAAc,+BAA+B,CAAC;AAE9C,cAAc,0BAA0B,CAAC;AAEzC,cAAc,mBAAmB,CAAC;AAElC,cAAc,sBAAsB,CAAC"}
|
package/dist/core/index.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
export * from "./internal/message-resolver";
|
|
2
|
-
export * from "./persistence/types";
|
|
3
|
-
export * from "./money/index";
|
|
1
|
+
export * from "./internal/message-resolver.js";
|
|
2
|
+
export * from "./persistence/types.js";
|
|
3
|
+
export * from "./money/index.js";
|
|
4
|
+
export { MoneyPrimitive } from "./money/money-primitive.js";
|
|
5
|
+
export * from "./exchange-rate/strategies.js";
|
|
6
|
+
export * from "./design-tokens/props.js";
|
|
7
|
+
export * from "./layout/types.js";
|
|
8
|
+
export * from "./shortcuts/types.js";
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Enables dynamic message function calls for config selectors.
|
|
5
5
|
* Creates a resolver that dynamically looks up and calls message functions.
|
|
6
6
|
*/
|
|
7
|
-
type MessageBundle = Record<string, (...args:
|
|
7
|
+
type MessageBundle = Record<string, (...args: unknown[]) => string>;
|
|
8
8
|
interface MessageResolverConfig<T> {
|
|
9
9
|
/**
|
|
10
10
|
* Function to extract the message key from an option
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-resolver.d.ts","sourceRoot":"","sources":["../../../src/sdk/core/src/internal/message-resolver.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AAEH,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"message-resolver.d.ts","sourceRoot":"","sources":["../../../src/sdk/core/src/internal/message-resolver.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AAEH,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,MAAM,CAAC,CAAC;AAEpE,UAAU,qBAAqB,CAAC,CAAC;IAC/B;;;OAGG;IACH,YAAY,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,CAAC;IAEpC;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;CAC1C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,QAAQ,EAAE,aAAa,EACvB,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAC,IAExB,QAAQ,CAAC,KAAG,MAAM,CAmB3B;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAExE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,CAAC,EAAE,EACZ,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAC/B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQxB"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Describes a single zone within a workspace layout.
|
|
3
|
+
* Zones are rendered in array order by the LayoutEngine in sdk/ui.
|
|
4
|
+
*/
|
|
5
|
+
export interface LayoutZone {
|
|
6
|
+
/** Unique identifier for this zone (e.g., "navigation", "content", "detail"). */
|
|
7
|
+
id: string;
|
|
8
|
+
/**
|
|
9
|
+
* Component reference to render in this zone.
|
|
10
|
+
* Typed as `unknown` at the core level — the UI layer resolves this
|
|
11
|
+
* to a concrete Svelte component reference.
|
|
12
|
+
*/
|
|
13
|
+
component?: unknown;
|
|
14
|
+
/**
|
|
15
|
+
* Store binding key. When set, the LayoutEngine will pass the corresponding
|
|
16
|
+
* store from the registry as a prop to the component.
|
|
17
|
+
*/
|
|
18
|
+
storeKey?: string;
|
|
19
|
+
/**
|
|
20
|
+
* CSS width value for this zone (e.g., "280px", "1fr", "auto").
|
|
21
|
+
* @default "auto"
|
|
22
|
+
*/
|
|
23
|
+
width?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Whether the zone can be collapsed by the user.
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
collapsible?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Store boolean path that controls whether this zone is visible.
|
|
31
|
+
* When set, the zone is only rendered when the referenced boolean is `true`.
|
|
32
|
+
* @example "layout.showNavigation"
|
|
33
|
+
*/
|
|
34
|
+
conditional?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Minimum width before the zone collapses automatically.
|
|
37
|
+
* Only relevant when `collapsible` is `true`.
|
|
38
|
+
*/
|
|
39
|
+
minWidth?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Position of the zone relative to the content area.
|
|
42
|
+
* @default "start"
|
|
43
|
+
*/
|
|
44
|
+
position?: "start" | "end";
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Complete layout configuration — an ordered array of zones.
|
|
48
|
+
* The LayoutEngine renders zones in array order (left-to-right by default).
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* const dashboardLayout: LayoutConfig = [
|
|
53
|
+
* { id: "navigation", width: "280px", collapsible: true },
|
|
54
|
+
* { id: "content", width: "1fr" },
|
|
55
|
+
* { id: "detail", width: "320px", collapsible: true, conditional: "layout.showDetail" },
|
|
56
|
+
* ];
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export type LayoutConfig = LayoutZone[];
|
|
60
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/sdk/core/src/layout/types.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,iFAAiF;IACjF,EAAE,EAAE,MAAM,CAAC;IAEX;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;CAC5B;AAID;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,YAAY,GAAG,UAAU,EAAE,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { addMoney, createMoney, CURRENCY_MAP, type Dinero, type DineroCurrency, formatAmount, formatMoney, type ISO4217Code, multiplyMoney, registerCurrency, safeAmount, subtractMoney, toMinorUnit, } from "./money";
|
|
1
|
+
export { addMoney, convertAmount, convertMoney, createMoney, CURRENCY_MAP, type Dinero, type DineroCurrency, type DineroSnapshot, formatAmount, formatMoney, fromMoneySnapshot, type ISO4217Code, multiplyMoney, type RateMap, registerCurrency, safeAmount, type ScaledRate, scaledRate, subtractMoney, toAdyenMoney, toMinorUnit, toMoneySnapshot, toPaypalMoney, toSquareMoney, toStripeMoney, } from "./money.ts";
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/sdk/core/src/money/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,KAAK,MAAM,EACX,KAAK,cAAc,EACnB,YAAY,EACZ,WAAW,EACX,KAAK,WAAW,EAChB,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/sdk/core/src/money/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,KAAK,MAAM,EACX,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,YAAY,EACZ,WAAW,EACX,iBAAiB,EACjB,KAAK,WAAW,EAChB,aAAa,EACb,KAAK,OAAO,EACZ,gBAAgB,EAChB,UAAU,EACV,KAAK,UAAU,EACf,UAAU,EACV,aAAa,EACb,YAAY,EACZ,WAAW,EACX,eAAe,EACf,aAAa,EACb,aAAa,EACb,aAAa,GACd,MAAM,YAAY,CAAC"}
|
package/dist/core/money/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// sdk/core/src/money/index.ts
|
|
2
|
-
export { addMoney, createMoney, CURRENCY_MAP, formatAmount, formatMoney, multiplyMoney, registerCurrency, safeAmount, subtractMoney, toMinorUnit, } from "./money";
|
|
2
|
+
export { addMoney, convertAmount, convertMoney, createMoney, CURRENCY_MAP, formatAmount, formatMoney, fromMoneySnapshot, multiplyMoney, registerCurrency, safeAmount, scaledRate, subtractMoney, toAdyenMoney, toMinorUnit, toMoneySnapshot, toPaypalMoney, toSquareMoney, toStripeMoney, } from "./money.js";
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { type ISO4217Code } from "./money.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Serializable representation of a MoneyPrimitive.
|
|
4
|
+
* Used for persistence drivers and JSON round-trips.
|
|
5
|
+
*/
|
|
6
|
+
export interface MoneyJSON {
|
|
7
|
+
amount: number;
|
|
8
|
+
currencyCode: string;
|
|
9
|
+
scale: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Immutable Value Object that encapsulates a monetary amount, its currency,
|
|
13
|
+
* and the scale (number of decimal places).
|
|
14
|
+
*
|
|
15
|
+
* Amounts are always stored in minor units (e.g., cents for USD, centavos for MXN).
|
|
16
|
+
* The scale is derived from the currency's exponent in CURRENCY_MAP.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const price = MoneyPrimitive.fromMajor(12.99, "USD");
|
|
21
|
+
* price.minor; // 1299
|
|
22
|
+
* price.major; // 12.99
|
|
23
|
+
* price.format("en-US"); // "$12.99"
|
|
24
|
+
*
|
|
25
|
+
* const json = price.toJSON();
|
|
26
|
+
* const restored = MoneyPrimitive.fromJSON(json);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare class MoneyPrimitive {
|
|
30
|
+
/** Raw amount in minor units (e.g., cents). */
|
|
31
|
+
readonly amount: number;
|
|
32
|
+
/** ISO 4217 currency code. */
|
|
33
|
+
readonly currencyCode: string;
|
|
34
|
+
/** Number of decimal places for this currency (e.g., 2 for USD, 0 for JPY). */
|
|
35
|
+
readonly scale: number;
|
|
36
|
+
private constructor();
|
|
37
|
+
/**
|
|
38
|
+
* Create a MoneyPrimitive from a minor-unit integer amount.
|
|
39
|
+
* @param amount - Amount in minor units (e.g., 1299 for $12.99)
|
|
40
|
+
* @param currencyCode - ISO 4217 currency code
|
|
41
|
+
*/
|
|
42
|
+
static fromMinor(amount: number, currencyCode: ISO4217Code | string): MoneyPrimitive;
|
|
43
|
+
/**
|
|
44
|
+
* Create a MoneyPrimitive from a major-unit float amount.
|
|
45
|
+
* @param amount - Amount in major units (e.g., 12.99 for $12.99)
|
|
46
|
+
* @param currencyCode - ISO 4217 currency code
|
|
47
|
+
*/
|
|
48
|
+
static fromMajor(amount: number, currencyCode: ISO4217Code | string): MoneyPrimitive;
|
|
49
|
+
/**
|
|
50
|
+
* Restore a MoneyPrimitive from its JSON representation.
|
|
51
|
+
*/
|
|
52
|
+
static fromJSON(json: MoneyJSON): MoneyPrimitive;
|
|
53
|
+
/** Amount in minor units (alias for `amount`). */
|
|
54
|
+
get minor(): number;
|
|
55
|
+
/** Amount in major units (e.g., 12.99 for 1299 cents). */
|
|
56
|
+
get major(): number;
|
|
57
|
+
/**
|
|
58
|
+
* Format this monetary value as a locale-aware currency string.
|
|
59
|
+
* @param locale - BCP 47 locale string (default: "en-US")
|
|
60
|
+
*/
|
|
61
|
+
format(locale?: string): string;
|
|
62
|
+
/**
|
|
63
|
+
* Add another MoneyPrimitive (must be same currency).
|
|
64
|
+
* Returns a new MoneyPrimitive.
|
|
65
|
+
*/
|
|
66
|
+
add(other: MoneyPrimitive): MoneyPrimitive;
|
|
67
|
+
/**
|
|
68
|
+
* Subtract another MoneyPrimitive (must be same currency).
|
|
69
|
+
* Returns a new MoneyPrimitive.
|
|
70
|
+
*/
|
|
71
|
+
subtract(other: MoneyPrimitive): MoneyPrimitive;
|
|
72
|
+
/**
|
|
73
|
+
* Multiply by a scalar factor. Returns a new MoneyPrimitive.
|
|
74
|
+
*/
|
|
75
|
+
multiply(factor: number): MoneyPrimitive;
|
|
76
|
+
/**
|
|
77
|
+
* Value equality — two MoneyPrimitives are equal if they have the same
|
|
78
|
+
* amount, currency code, and scale.
|
|
79
|
+
*/
|
|
80
|
+
equals(other: MoneyPrimitive): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Returns true if this amount is zero.
|
|
83
|
+
*/
|
|
84
|
+
isZero(): boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Returns true if this amount is negative.
|
|
87
|
+
*/
|
|
88
|
+
isNegative(): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Serialize to a plain JSON-compatible object.
|
|
91
|
+
*/
|
|
92
|
+
toJSON(): MoneyJSON;
|
|
93
|
+
toString(): string;
|
|
94
|
+
private assertSameCurrency;
|
|
95
|
+
/**
|
|
96
|
+
* Resolves the scale (exponent) for a currency code from CURRENCY_MAP.
|
|
97
|
+
* Throws if the currency is not registered.
|
|
98
|
+
*/
|
|
99
|
+
private static resolveScale;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=money-primitive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money-primitive.d.ts","sourceRoot":"","sources":["../../../src/sdk/core/src/money/money-primitive.ts"],"names":[],"mappings":"AAIA,OAAO,EAAgB,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAE5D;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,cAAc;IACzB,+CAA+C;IAC/C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,8BAA8B;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,OAAO;IAQP;;;;OAIG;IACH,MAAM,CAAC,SAAS,CACd,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,WAAW,GAAG,MAAM,GACjC,cAAc;IAKjB;;;;OAIG;IACH,MAAM,CAAC,SAAS,CACd,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,WAAW,GAAG,MAAM,GACjC,cAAc;IAMjB;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,cAAc;IAMhD,kDAAkD;IAClD,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,0DAA0D;IAC1D,IAAI,KAAK,IAAI,MAAM,CAGlB;IAID;;;OAGG;IACH,MAAM,CAAC,MAAM,GAAE,MAAgB,GAAG,MAAM;IAWxC;;;OAGG;IACH,GAAG,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc;IAS1C;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc;IAS/C;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc;IAUxC;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO;IAQtC;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACH,UAAU,IAAI,OAAO;IAMrB;;OAEG;IACH,MAAM,IAAI,SAAS;IAQnB,QAAQ,IAAI,MAAM;IAMlB,OAAO,CAAC,kBAAkB;IAQ1B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;CAS5B"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// sdk/core/src/money/money-primitive.ts
|
|
2
|
+
// Immutable Value Object for monetary amounts.
|
|
3
|
+
// Zero Svelte dependencies — pure TypeScript class.
|
|
4
|
+
import { CURRENCY_MAP } from "./money.js";
|
|
5
|
+
/**
|
|
6
|
+
* Immutable Value Object that encapsulates a monetary amount, its currency,
|
|
7
|
+
* and the scale (number of decimal places).
|
|
8
|
+
*
|
|
9
|
+
* Amounts are always stored in minor units (e.g., cents for USD, centavos for MXN).
|
|
10
|
+
* The scale is derived from the currency's exponent in CURRENCY_MAP.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* const price = MoneyPrimitive.fromMajor(12.99, "USD");
|
|
15
|
+
* price.minor; // 1299
|
|
16
|
+
* price.major; // 12.99
|
|
17
|
+
* price.format("en-US"); // "$12.99"
|
|
18
|
+
*
|
|
19
|
+
* const json = price.toJSON();
|
|
20
|
+
* const restored = MoneyPrimitive.fromJSON(json);
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export class MoneyPrimitive {
|
|
24
|
+
/** Raw amount in minor units (e.g., cents). */
|
|
25
|
+
amount;
|
|
26
|
+
/** ISO 4217 currency code. */
|
|
27
|
+
currencyCode;
|
|
28
|
+
/** Number of decimal places for this currency (e.g., 2 for USD, 0 for JPY). */
|
|
29
|
+
scale;
|
|
30
|
+
constructor(amount, currencyCode, scale) {
|
|
31
|
+
this.amount = Math.round(amount);
|
|
32
|
+
this.currencyCode = currencyCode;
|
|
33
|
+
this.scale = scale;
|
|
34
|
+
}
|
|
35
|
+
// ── Factories ────────────────────────────────────────────────────────────
|
|
36
|
+
/**
|
|
37
|
+
* Create a MoneyPrimitive from a minor-unit integer amount.
|
|
38
|
+
* @param amount - Amount in minor units (e.g., 1299 for $12.99)
|
|
39
|
+
* @param currencyCode - ISO 4217 currency code
|
|
40
|
+
*/
|
|
41
|
+
static fromMinor(amount, currencyCode) {
|
|
42
|
+
const scale = MoneyPrimitive.resolveScale(currencyCode);
|
|
43
|
+
return new MoneyPrimitive(amount, currencyCode, scale);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Create a MoneyPrimitive from a major-unit float amount.
|
|
47
|
+
* @param amount - Amount in major units (e.g., 12.99 for $12.99)
|
|
48
|
+
* @param currencyCode - ISO 4217 currency code
|
|
49
|
+
*/
|
|
50
|
+
static fromMajor(amount, currencyCode) {
|
|
51
|
+
const scale = MoneyPrimitive.resolveScale(currencyCode);
|
|
52
|
+
const factor = Math.pow(10, scale);
|
|
53
|
+
return new MoneyPrimitive(Math.round(amount * factor), currencyCode, scale);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Restore a MoneyPrimitive from its JSON representation.
|
|
57
|
+
*/
|
|
58
|
+
static fromJSON(json) {
|
|
59
|
+
return new MoneyPrimitive(json.amount, json.currencyCode, json.scale);
|
|
60
|
+
}
|
|
61
|
+
// ── Computed Getters ─────────────────────────────────────────────────────
|
|
62
|
+
/** Amount in minor units (alias for `amount`). */
|
|
63
|
+
get minor() {
|
|
64
|
+
return this.amount;
|
|
65
|
+
}
|
|
66
|
+
/** Amount in major units (e.g., 12.99 for 1299 cents). */
|
|
67
|
+
get major() {
|
|
68
|
+
if (this.scale === 0)
|
|
69
|
+
return this.amount;
|
|
70
|
+
return this.amount / Math.pow(10, this.scale);
|
|
71
|
+
}
|
|
72
|
+
// ── Formatting ───────────────────────────────────────────────────────────
|
|
73
|
+
/**
|
|
74
|
+
* Format this monetary value as a locale-aware currency string.
|
|
75
|
+
* @param locale - BCP 47 locale string (default: "en-US")
|
|
76
|
+
*/
|
|
77
|
+
format(locale = "en-US") {
|
|
78
|
+
return new Intl.NumberFormat(locale, {
|
|
79
|
+
style: "currency",
|
|
80
|
+
currency: this.currencyCode,
|
|
81
|
+
minimumFractionDigits: this.scale,
|
|
82
|
+
maximumFractionDigits: this.scale,
|
|
83
|
+
}).format(this.major);
|
|
84
|
+
}
|
|
85
|
+
// ── Arithmetic (returns new instances — immutability) ────────────────────
|
|
86
|
+
/**
|
|
87
|
+
* Add another MoneyPrimitive (must be same currency).
|
|
88
|
+
* Returns a new MoneyPrimitive.
|
|
89
|
+
*/
|
|
90
|
+
add(other) {
|
|
91
|
+
this.assertSameCurrency(other);
|
|
92
|
+
return new MoneyPrimitive(this.amount + other.amount, this.currencyCode, this.scale);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Subtract another MoneyPrimitive (must be same currency).
|
|
96
|
+
* Returns a new MoneyPrimitive.
|
|
97
|
+
*/
|
|
98
|
+
subtract(other) {
|
|
99
|
+
this.assertSameCurrency(other);
|
|
100
|
+
return new MoneyPrimitive(this.amount - other.amount, this.currencyCode, this.scale);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Multiply by a scalar factor. Returns a new MoneyPrimitive.
|
|
104
|
+
*/
|
|
105
|
+
multiply(factor) {
|
|
106
|
+
return new MoneyPrimitive(Math.round(this.amount * factor), this.currencyCode, this.scale);
|
|
107
|
+
}
|
|
108
|
+
// ── Comparison ───────────────────────────────────────────────────────────
|
|
109
|
+
/**
|
|
110
|
+
* Value equality — two MoneyPrimitives are equal if they have the same
|
|
111
|
+
* amount, currency code, and scale.
|
|
112
|
+
*/
|
|
113
|
+
equals(other) {
|
|
114
|
+
return (this.amount === other.amount &&
|
|
115
|
+
this.currencyCode === other.currencyCode &&
|
|
116
|
+
this.scale === other.scale);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Returns true if this amount is zero.
|
|
120
|
+
*/
|
|
121
|
+
isZero() {
|
|
122
|
+
return this.amount === 0;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Returns true if this amount is negative.
|
|
126
|
+
*/
|
|
127
|
+
isNegative() {
|
|
128
|
+
return this.amount < 0;
|
|
129
|
+
}
|
|
130
|
+
// ── Serialization ────────────────────────────────────────────────────────
|
|
131
|
+
/**
|
|
132
|
+
* Serialize to a plain JSON-compatible object.
|
|
133
|
+
*/
|
|
134
|
+
toJSON() {
|
|
135
|
+
return {
|
|
136
|
+
amount: this.amount,
|
|
137
|
+
currencyCode: this.currencyCode,
|
|
138
|
+
scale: this.scale,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
toString() {
|
|
142
|
+
return `MoneyPrimitive(${this.amount} ${this.currencyCode} scale=${this.scale})`;
|
|
143
|
+
}
|
|
144
|
+
// ── Private Helpers ──────────────────────────────────────────────────────
|
|
145
|
+
assertSameCurrency(other) {
|
|
146
|
+
if (this.currencyCode !== other.currencyCode) {
|
|
147
|
+
throw new Error(`Currency mismatch: cannot operate on ${this.currencyCode} and ${other.currencyCode}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Resolves the scale (exponent) for a currency code from CURRENCY_MAP.
|
|
152
|
+
* Throws if the currency is not registered.
|
|
153
|
+
*/
|
|
154
|
+
static resolveScale(currencyCode) {
|
|
155
|
+
const currency = CURRENCY_MAP[currencyCode];
|
|
156
|
+
if (!currency) {
|
|
157
|
+
throw new Error(`Unknown currency code: ${currencyCode}. Register it first via registerCurrency().`);
|
|
158
|
+
}
|
|
159
|
+
return currency.exponent;
|
|
160
|
+
}
|
|
161
|
+
}
|