@86d-store/multi-currency 0.1.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.
Files changed (94) hide show
  1. package/README.md +234 -0
  2. package/dist/admin/components/_shared.d.ts +28 -0
  3. package/dist/admin/components/_shared.d.ts.map +1 -0
  4. package/dist/admin/components/_shared.js +15 -0
  5. package/dist/admin/components/currency-detail.d.ts +6 -0
  6. package/dist/admin/components/currency-detail.d.ts.map +1 -0
  7. package/dist/admin/components/currency-detail.js +26 -0
  8. package/dist/admin/components/currency-form.d.ts +6 -0
  9. package/dist/admin/components/currency-form.d.ts.map +1 -0
  10. package/dist/admin/components/currency-form.js +95 -0
  11. package/dist/admin/components/currency-list.d.ts +2 -0
  12. package/dist/admin/components/currency-list.d.ts.map +1 -0
  13. package/dist/admin/components/currency-list.js +35 -0
  14. package/dist/admin/endpoints/bulk-update-rates.d.ts +15 -0
  15. package/dist/admin/endpoints/bulk-update-rates.d.ts.map +1 -0
  16. package/dist/admin/endpoints/bulk-update-rates.js +17 -0
  17. package/dist/admin/endpoints/create-currency.d.ts +34 -0
  18. package/dist/admin/endpoints/create-currency.d.ts.map +1 -0
  19. package/dist/admin/endpoints/create-currency.js +34 -0
  20. package/dist/admin/endpoints/delete-currency.d.ts +16 -0
  21. package/dist/admin/endpoints/delete-currency.d.ts.map +1 -0
  22. package/dist/admin/endpoints/delete-currency.js +17 -0
  23. package/dist/admin/endpoints/delete-price-override.d.ts +10 -0
  24. package/dist/admin/endpoints/delete-price-override.d.ts.map +1 -0
  25. package/dist/admin/endpoints/delete-price-override.js +11 -0
  26. package/dist/admin/endpoints/get-currency.d.ts +16 -0
  27. package/dist/admin/endpoints/get-currency.d.ts.map +1 -0
  28. package/dist/admin/endpoints/get-currency.js +14 -0
  29. package/dist/admin/endpoints/list-currencies.d.ts +6 -0
  30. package/dist/admin/endpoints/list-currencies.d.ts.map +1 -0
  31. package/dist/admin/endpoints/list-currencies.js +7 -0
  32. package/dist/admin/endpoints/list-price-overrides.d.ts +10 -0
  33. package/dist/admin/endpoints/list-price-overrides.d.ts.map +1 -0
  34. package/dist/admin/endpoints/list-price-overrides.js +11 -0
  35. package/dist/admin/endpoints/rate-history.d.ts +11 -0
  36. package/dist/admin/endpoints/rate-history.d.ts.map +1 -0
  37. package/dist/admin/endpoints/rate-history.js +17 -0
  38. package/dist/admin/endpoints/routes.d.ts +180 -0
  39. package/dist/admin/endpoints/routes.d.ts.map +1 -0
  40. package/dist/admin/endpoints/routes.js +26 -0
  41. package/dist/admin/endpoints/set-base-currency.d.ts +16 -0
  42. package/dist/admin/endpoints/set-base-currency.d.ts.map +1 -0
  43. package/dist/admin/endpoints/set-base-currency.js +14 -0
  44. package/dist/admin/endpoints/set-price-override.d.ts +13 -0
  45. package/dist/admin/endpoints/set-price-override.d.ts.map +1 -0
  46. package/dist/admin/endpoints/set-price-override.js +16 -0
  47. package/dist/admin/endpoints/update-currency.d.ts +35 -0
  48. package/dist/admin/endpoints/update-currency.d.ts.map +1 -0
  49. package/dist/admin/endpoints/update-currency.js +27 -0
  50. package/dist/admin/endpoints/update-rate.d.ts +18 -0
  51. package/dist/admin/endpoints/update-rate.d.ts.map +1 -0
  52. package/dist/admin/endpoints/update-rate.js +22 -0
  53. package/dist/capabilities.d.ts +56 -0
  54. package/dist/capabilities.d.ts.map +1 -0
  55. package/dist/capabilities.js +16 -0
  56. package/dist/index.d.ts +16 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +69 -0
  59. package/dist/schema.d.ts +94 -0
  60. package/dist/schema.d.ts.map +1 -0
  61. package/dist/schema.js +50 -0
  62. package/dist/service-impl.d.ts +5 -0
  63. package/dist/service-impl.d.ts.map +1 -0
  64. package/dist/service-impl.js +369 -0
  65. package/dist/service.d.ts +139 -0
  66. package/dist/service.d.ts.map +1 -0
  67. package/dist/service.js +1 -0
  68. package/dist/store/components/_hooks.d.ts +6 -0
  69. package/dist/store/components/_hooks.d.ts.map +1 -0
  70. package/dist/store/components/_hooks.js +10 -0
  71. package/dist/store/components/currency-selector.d.ts +10 -0
  72. package/dist/store/components/currency-selector.d.ts.map +1 -0
  73. package/dist/store/components/currency-selector.js +22 -0
  74. package/dist/store/components/currency-selector.mdx +55 -0
  75. package/dist/store/components/mdx.d.ts +8 -0
  76. package/dist/store/components/mdx.d.ts.map +1 -0
  77. package/dist/store/components/mdx.js +7 -0
  78. package/dist/store/components/price-display.d.ts +14 -0
  79. package/dist/store/components/price-display.d.ts.map +1 -0
  80. package/dist/store/components/price-display.js +30 -0
  81. package/dist/store/components/price-display.mdx +12 -0
  82. package/dist/store/endpoints/convert-price.d.ts +22 -0
  83. package/dist/store/endpoints/convert-price.d.ts.map +1 -0
  84. package/dist/store/endpoints/convert-price.js +26 -0
  85. package/dist/store/endpoints/get-product-price.d.ts +22 -0
  86. package/dist/store/endpoints/get-product-price.d.ts.map +1 -0
  87. package/dist/store/endpoints/get-product-price.js +26 -0
  88. package/dist/store/endpoints/list-currencies.d.ts +6 -0
  89. package/dist/store/endpoints/list-currencies.d.ts.map +1 -0
  90. package/dist/store/endpoints/list-currencies.js +7 -0
  91. package/dist/store/endpoints/routes.d.ts +48 -0
  92. package/dist/store/endpoints/routes.d.ts.map +1 -0
  93. package/dist/store/endpoints/routes.js +8 -0
  94. package/package.json +108 -0
@@ -0,0 +1,139 @@
1
+ import type { ModuleController } from "@86d-store/core/types/module";
2
+ export type SymbolPosition = "before" | "after";
3
+ export type RoundingMode = "round" | "ceil" | "floor";
4
+ export type Currency = {
5
+ id: string;
6
+ code: string;
7
+ name: string;
8
+ symbol: string;
9
+ decimalPlaces: number;
10
+ exchangeRate: number;
11
+ isBase: boolean;
12
+ isActive: boolean;
13
+ symbolPosition: SymbolPosition;
14
+ thousandsSeparator: string;
15
+ decimalSeparator: string;
16
+ roundingMode: RoundingMode;
17
+ sortOrder: number;
18
+ createdAt: Date;
19
+ updatedAt: Date;
20
+ };
21
+ export type ExchangeRateHistory = {
22
+ id: string;
23
+ currencyCode: string;
24
+ rate: number;
25
+ source: string;
26
+ recordedAt: Date;
27
+ };
28
+ export type PriceOverride = {
29
+ id: string;
30
+ productId: string;
31
+ currencyCode: string;
32
+ price: number;
33
+ compareAtPrice?: number | undefined;
34
+ createdAt: Date;
35
+ updatedAt: Date;
36
+ };
37
+ export type ConvertedPrice = {
38
+ amount: number;
39
+ currency: Currency;
40
+ formatted: string;
41
+ };
42
+ export type MultiCurrencyController = ModuleController & {
43
+ /** Create a new currency */
44
+ create(params: {
45
+ code: string;
46
+ name: string;
47
+ symbol: string;
48
+ decimalPlaces?: number | undefined;
49
+ exchangeRate?: number | undefined;
50
+ isBase?: boolean | undefined;
51
+ isActive?: boolean | undefined;
52
+ symbolPosition?: SymbolPosition | undefined;
53
+ thousandsSeparator?: string | undefined;
54
+ decimalSeparator?: string | undefined;
55
+ roundingMode?: RoundingMode | undefined;
56
+ sortOrder?: number | undefined;
57
+ }): Promise<Currency>;
58
+ /** Get currency by ID */
59
+ getById(id: string): Promise<Currency | null>;
60
+ /** Get currency by ISO code */
61
+ getByCode(code: string): Promise<Currency | null>;
62
+ /** Update a currency */
63
+ update(id: string, params: {
64
+ name?: string | undefined;
65
+ symbol?: string | undefined;
66
+ decimalPlaces?: number | undefined;
67
+ exchangeRate?: number | undefined;
68
+ isActive?: boolean | undefined;
69
+ symbolPosition?: SymbolPosition | undefined;
70
+ thousandsSeparator?: string | undefined;
71
+ decimalSeparator?: string | undefined;
72
+ roundingMode?: RoundingMode | undefined;
73
+ sortOrder?: number | undefined;
74
+ }): Promise<Currency | null>;
75
+ /** Delete a currency (cannot delete the base currency) */
76
+ delete(id: string): Promise<{
77
+ deleted: boolean;
78
+ error?: string;
79
+ }>;
80
+ /** List all currencies */
81
+ list(params?: {
82
+ activeOnly?: boolean | undefined;
83
+ }): Promise<Currency[]>;
84
+ /** Get the store's base currency */
85
+ getBaseCurrency(): Promise<Currency | null>;
86
+ /** Set a currency as the base currency */
87
+ setBaseCurrency(id: string): Promise<Currency | null>;
88
+ /** Update exchange rate and record history */
89
+ updateRate(params: {
90
+ currencyCode: string;
91
+ rate: number;
92
+ source?: string | undefined;
93
+ }): Promise<Currency | null>;
94
+ /** Bulk update exchange rates */
95
+ bulkUpdateRates(rates: Array<{
96
+ currencyCode: string;
97
+ rate: number;
98
+ source?: string | undefined;
99
+ }>): Promise<{
100
+ updated: number;
101
+ errors: string[];
102
+ }>;
103
+ /** Get rate history for a currency */
104
+ getRateHistory(params: {
105
+ currencyCode: string;
106
+ limit?: number | undefined;
107
+ }): Promise<ExchangeRateHistory[]>;
108
+ /** Convert an amount from base currency to target currency */
109
+ convert(params: {
110
+ amount: number;
111
+ to: string;
112
+ from?: string | undefined;
113
+ }): Promise<ConvertedPrice | null>;
114
+ /** Format an amount in a given currency */
115
+ formatPrice(amount: number, currencyCode: string): Promise<string | null>;
116
+ /** Set a fixed price override for a product in a given currency */
117
+ setPriceOverride(params: {
118
+ productId: string;
119
+ currencyCode: string;
120
+ price: number;
121
+ compareAtPrice?: number | undefined;
122
+ }): Promise<PriceOverride>;
123
+ /** Get price override for a product in a given currency */
124
+ getPriceOverride(productId: string, currencyCode: string): Promise<PriceOverride | null>;
125
+ /** List all price overrides for a product */
126
+ listPriceOverrides(productId: string): Promise<PriceOverride[]>;
127
+ /** Delete a price override */
128
+ deletePriceOverride(id: string): Promise<void>;
129
+ /**
130
+ * Get the best price for a product in a given currency.
131
+ * Uses price override if available, otherwise converts from base.
132
+ */
133
+ getProductPrice(params: {
134
+ productId: string;
135
+ basePriceInCents: number;
136
+ currencyCode: string;
137
+ }): Promise<ConvertedPrice | null>;
138
+ };
139
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ export declare function useCurrencyApi(): {
2
+ listCurrencies: import("@86d-store/core/client/types").AnyEndpointHook;
3
+ convert: import("@86d-store/core/client/types").AnyEndpointHook;
4
+ productPrice: import("@86d-store/core/client/types").AnyEndpointHook;
5
+ };
6
+ //# sourceMappingURL=_hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_hooks.d.ts","sourceRoot":"","sources":["../../../src/store/components/_hooks.ts"],"names":[],"mappings":"AAIA,wBAAgB,cAAc;;;;EAQ7B"}
@@ -0,0 +1,10 @@
1
+ "use client";
2
+ import { useModuleClient } from "@86d-store/core/client/provider";
3
+ export function useCurrencyApi() {
4
+ const client = useModuleClient();
5
+ return {
6
+ listCurrencies: client.module("multi-currency").store["/currencies"],
7
+ convert: client.module("multi-currency").store["/currencies/convert"],
8
+ productPrice: client.module("multi-currency").store["/currencies/product-price"],
9
+ };
10
+ }
@@ -0,0 +1,10 @@
1
+ export interface CurrencySelectorProps {
2
+ /** Currently selected currency code (ISO 4217) */
3
+ value?: string | undefined;
4
+ /** Called when user selects a different currency */
5
+ onChange?: ((code: string) => void) | undefined;
6
+ /** Show compact mode (code only, no name) */
7
+ compact?: boolean | undefined;
8
+ }
9
+ export declare function CurrencySelector({ value, onChange, compact, }: CurrencySelectorProps): import("react").JSX.Element | null;
10
+ //# sourceMappingURL=currency-selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"currency-selector.d.ts","sourceRoot":"","sources":["../../../src/store/components/currency-selector.tsx"],"names":[],"mappings":"AAgBA,MAAM,WAAW,qBAAqB;IACrC,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,oDAAoD;IACpD,QAAQ,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAChD,6CAA6C;IAC7C,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC9B;AAED,wBAAgB,gBAAgB,CAAC,EAChC,KAAK,EACL,QAAQ,EACR,OAAe,GACf,EAAE,qBAAqB,sCAiCvB"}
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { useCurrencyApi } from "./_hooks";
5
+ import CurrencySelectorTemplate from "./currency-selector.mdx";
6
+ export function CurrencySelector({ value, onChange, compact = false, }) {
7
+ const api = useCurrencyApi();
8
+ const [isOpen, setIsOpen] = useState(false);
9
+ const { data, isLoading } = api.listCurrencies.useQuery();
10
+ const currencies = data?.currencies ?? [];
11
+ const selected = currencies.find((c) => c.code === value);
12
+ const baseCurrency = currencies.find((c) => c.isBase);
13
+ const active = selected ?? baseCurrency ?? currencies[0];
14
+ const handleSelect = (code) => {
15
+ setIsOpen(false);
16
+ onChange?.(code);
17
+ };
18
+ if (isLoading || currencies.length <= 1) {
19
+ return null;
20
+ }
21
+ return (_jsx(CurrencySelectorTemplate, { currencies: currencies, active: active, isOpen: isOpen, onToggle: () => setIsOpen((prev) => !prev), onSelect: handleSelect, compact: compact }));
22
+ }
@@ -0,0 +1,55 @@
1
+ <div className="relative inline-block text-left">
2
+ <button
3
+ type="button"
4
+ onClick={props.onToggle}
5
+ className="inline-flex items-center gap-1.5 rounded-lg border border-input bg-background px-3 py-1.5 font-medium text-foreground text-sm transition-colors hover:bg-muted"
6
+ aria-expanded={props.isOpen}
7
+ aria-haspopup="listbox"
8
+ >
9
+ <span>{props.active?.symbol}</span>
10
+ <span>{props.active?.code}</span>
11
+ {!props.compact && (
12
+ <span className="hidden text-muted-foreground sm:inline">
13
+ {props.active?.name}
14
+ </span>
15
+ )}
16
+ <svg
17
+ className={`h-3.5 w-3.5 text-muted-foreground transition-transform ${props.isOpen ? "rotate-180" : ""}`}
18
+ fill="none"
19
+ viewBox="0 0 24 24"
20
+ stroke="currentColor"
21
+ strokeWidth={2}
22
+ >
23
+ <path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
24
+ </svg>
25
+ </button>
26
+
27
+ {props.isOpen && (
28
+ <ul
29
+ role="listbox"
30
+ className="absolute right-0 z-50 mt-1 max-h-60 w-48 overflow-auto rounded-lg border border-border bg-background py-1 shadow-lg"
31
+ >
32
+ {props.currencies?.map((c) => (
33
+ <li key={c.code}>
34
+ <button
35
+ type="button"
36
+ role="option"
37
+ aria-selected={c.code === props.active?.code}
38
+ onClick={() => props.onSelect(c.code)}
39
+ className={`flex w-full items-center gap-2 px-3 py-2 text-left text-sm transition-colors hover:bg-muted ${
40
+ c.code === props.active?.code
41
+ ? "bg-muted font-medium text-foreground"
42
+ : "text-muted-foreground"
43
+ }`}
44
+ >
45
+ <span className="w-6 text-center">{c.symbol}</span>
46
+ <span>{c.code}</span>
47
+ <span className="truncate text-muted-foreground text-xs">
48
+ {c.name}
49
+ </span>
50
+ </button>
51
+ </li>
52
+ ))}
53
+ </ul>
54
+ )}
55
+ </div>
@@ -0,0 +1,8 @@
1
+ import { CurrencySelector } from "./currency-selector";
2
+ import { PriceDisplay } from "./price-display";
3
+ declare const _default: {
4
+ CurrencySelector: typeof CurrencySelector;
5
+ PriceDisplay: typeof PriceDisplay;
6
+ };
7
+ export default _default;
8
+ //# sourceMappingURL=mdx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mdx.d.ts","sourceRoot":"","sources":["../../../src/store/components/mdx.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;;;;;AAE/C,wBAG0B"}
@@ -0,0 +1,7 @@
1
+ "use client";
2
+ import { CurrencySelector } from "./currency-selector";
3
+ import { PriceDisplay } from "./price-display";
4
+ export default {
5
+ CurrencySelector,
6
+ PriceDisplay,
7
+ };
@@ -0,0 +1,14 @@
1
+ export interface PriceDisplayProps {
2
+ /** Product ID for price override lookup */
3
+ productId?: string | undefined;
4
+ /** Base price in cents (smallest unit of base currency) */
5
+ basePriceInCents: number;
6
+ /** Target currency code (ISO 4217) */
7
+ currencyCode?: string | undefined;
8
+ /** Compare-at price in cents (for sale display) */
9
+ compareAtPriceInCents?: number | undefined;
10
+ /** Additional CSS class for the container */
11
+ className?: string | undefined;
12
+ }
13
+ export declare function PriceDisplay({ productId, basePriceInCents, currencyCode, compareAtPriceInCents, className, }: PriceDisplayProps): import("react").JSX.Element;
14
+ //# sourceMappingURL=price-display.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"price-display.d.ts","sourceRoot":"","sources":["../../../src/store/components/price-display.tsx"],"names":[],"mappings":"AAWA,MAAM,WAAW,iBAAiB;IACjC,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,2DAA2D;IAC3D,gBAAgB,EAAE,MAAM,CAAC;IACzB,sCAAsC;IACtC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,mDAAmD;IACnD,qBAAqB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B;AAED,wBAAgB,YAAY,CAAC,EAC5B,SAAS,EACT,gBAAgB,EAChB,YAAY,EACZ,qBAAqB,EACrB,SAAS,GACT,EAAE,iBAAiB,+BAmDnB"}
@@ -0,0 +1,30 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useCurrencyApi } from "./_hooks";
4
+ import PriceDisplayTemplate from "./price-display.mdx";
5
+ export function PriceDisplay({ productId, basePriceInCents, currencyCode, compareAtPriceInCents, className, }) {
6
+ const api = useCurrencyApi();
7
+ const productPriceMutation = api.productPrice.useMutation();
8
+ const convertMutation = api.convert.useMutation();
9
+ const priceQuery = currencyCode
10
+ ? productId
11
+ ? productPriceMutation
12
+ : convertMutation
13
+ : { data: undefined, isLoading: false };
14
+ // If no currency code, show base price
15
+ if (!currencyCode) {
16
+ const formatted = new Intl.NumberFormat("en-US", {
17
+ style: "currency",
18
+ currency: "USD",
19
+ }).format(basePriceInCents / 100);
20
+ const compareFormatted = compareAtPriceInCents
21
+ ? new Intl.NumberFormat("en-US", {
22
+ style: "currency",
23
+ currency: "USD",
24
+ }).format(compareAtPriceInCents / 100)
25
+ : undefined;
26
+ return (_jsx(PriceDisplayTemplate, { price: formatted, compareAtPrice: compareFormatted, isLoading: false, className: className }));
27
+ }
28
+ const price = priceQuery.data;
29
+ return (_jsx(PriceDisplayTemplate, { price: price?.formatted, compareAtPrice: undefined, isLoading: priceQuery.isLoading, className: className }));
30
+ }
@@ -0,0 +1,12 @@
1
+ {props.isLoading ? (
2
+ <span className={`inline-block h-5 w-16 animate-pulse rounded bg-muted ${props.className ?? ""}`} />
3
+ ) : (
4
+ <span className={props.className}>
5
+ {props.compareAtPrice && (
6
+ <span className="mr-1.5 text-muted-foreground line-through">
7
+ {props.compareAtPrice}
8
+ </span>
9
+ )}
10
+ <span className="font-medium text-foreground">{props.price}</span>
11
+ </span>
12
+ )}
@@ -0,0 +1,22 @@
1
+ import { z } from "zod";
2
+ export declare const convertPrice: import("better-call").StrictEndpoint<"/currencies/convert", {
3
+ method: "POST";
4
+ body: z.ZodObject<{
5
+ amount: z.ZodNumber;
6
+ to: z.ZodString;
7
+ from: z.ZodOptional<z.ZodString>;
8
+ }, z.core.$strip>;
9
+ }, {
10
+ error: string;
11
+ status: number;
12
+ amount?: never;
13
+ formatted?: never;
14
+ currency?: never;
15
+ } | {
16
+ amount: number;
17
+ formatted: string;
18
+ currency: string;
19
+ error?: never;
20
+ status?: never;
21
+ }>;
22
+ //# sourceMappingURL=convert-price.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert-price.d.ts","sourceRoot":"","sources":["../../../src/store/endpoints/convert-price.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;EA6BxB,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { createStoreEndpoint } from "@86d-store/core/api";
2
+ import { z } from "zod";
3
+ export const convertPrice = createStoreEndpoint("/currencies/convert", {
4
+ method: "POST",
5
+ body: z.object({
6
+ amount: z.number().nonnegative(),
7
+ to: z.string().min(3).max(3),
8
+ from: z.string().min(3).max(3).optional(),
9
+ }),
10
+ }, async (ctx) => {
11
+ const controller = ctx.context.controllers
12
+ .multiCurrency;
13
+ const result = await controller.convert({
14
+ amount: ctx.body.amount,
15
+ to: ctx.body.to,
16
+ ...(ctx.body.from ? { from: ctx.body.from } : {}),
17
+ });
18
+ if (!result) {
19
+ return { error: "Currency not found", status: 404 };
20
+ }
21
+ return {
22
+ amount: result.amount,
23
+ formatted: result.formatted,
24
+ currency: result.currency.code,
25
+ };
26
+ });
@@ -0,0 +1,22 @@
1
+ import { z } from "zod";
2
+ export declare const getProductPrice: import("better-call").StrictEndpoint<"/currencies/product-price", {
3
+ method: "POST";
4
+ body: z.ZodObject<{
5
+ productId: z.ZodString;
6
+ basePriceInCents: z.ZodNumber;
7
+ currencyCode: z.ZodString;
8
+ }, z.core.$strip>;
9
+ }, {
10
+ error: string;
11
+ status: number;
12
+ amount?: never;
13
+ formatted?: never;
14
+ currency?: never;
15
+ } | {
16
+ amount: number;
17
+ formatted: string;
18
+ currency: string;
19
+ error?: never;
20
+ status?: never;
21
+ }>;
22
+ //# sourceMappingURL=get-product-price.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-product-price.d.ts","sourceRoot":"","sources":["../../../src/store/endpoints/get-product-price.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;EA6B3B,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { createStoreEndpoint } from "@86d-store/core/api";
2
+ import { z } from "zod";
3
+ export const getProductPrice = createStoreEndpoint("/currencies/product-price", {
4
+ method: "POST",
5
+ body: z.object({
6
+ productId: z.string().min(1).max(100),
7
+ basePriceInCents: z.number().int().nonnegative(),
8
+ currencyCode: z.string().min(3).max(3),
9
+ }),
10
+ }, async (ctx) => {
11
+ const controller = ctx.context.controllers
12
+ .multiCurrency;
13
+ const result = await controller.getProductPrice({
14
+ productId: ctx.body.productId,
15
+ basePriceInCents: ctx.body.basePriceInCents,
16
+ currencyCode: ctx.body.currencyCode,
17
+ });
18
+ if (!result) {
19
+ return { error: "Currency not found", status: 404 };
20
+ }
21
+ return {
22
+ amount: result.amount,
23
+ formatted: result.formatted,
24
+ currency: result.currency.code,
25
+ };
26
+ });
@@ -0,0 +1,6 @@
1
+ export declare const listCurrencies: import("better-call").StrictEndpoint<"/currencies", {
2
+ method: "GET";
3
+ }, {
4
+ currencies: import("../..").Currency[];
5
+ }>;
6
+ //# sourceMappingURL=list-currencies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-currencies.d.ts","sourceRoot":"","sources":["../../../src/store/endpoints/list-currencies.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,cAAc;;;;EAS1B,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { createStoreEndpoint } from "@86d-store/core/api";
2
+ export const listCurrencies = createStoreEndpoint("/currencies", { method: "GET" }, async (ctx) => {
3
+ const controller = ctx.context.controllers
4
+ .multiCurrency;
5
+ const currencies = await controller.list({ activeOnly: true });
6
+ return { currencies };
7
+ });
@@ -0,0 +1,48 @@
1
+ export declare const storeEndpoints: {
2
+ "/currencies": import("better-call").StrictEndpoint<"/currencies", {
3
+ method: "GET";
4
+ }, {
5
+ currencies: import("../..").Currency[];
6
+ }>;
7
+ "/currencies/convert": import("better-call").StrictEndpoint<"/currencies/convert", {
8
+ method: "POST";
9
+ body: import("zod").ZodObject<{
10
+ amount: import("zod").ZodNumber;
11
+ to: import("zod").ZodString;
12
+ from: import("zod").ZodOptional<import("zod").ZodString>;
13
+ }, import("zod/v4/core").$strip>;
14
+ }, {
15
+ error: string;
16
+ status: number;
17
+ amount?: never;
18
+ formatted?: never;
19
+ currency?: never;
20
+ } | {
21
+ amount: number;
22
+ formatted: string;
23
+ currency: string;
24
+ error?: never;
25
+ status?: never;
26
+ }>;
27
+ "/currencies/product-price": import("better-call").StrictEndpoint<"/currencies/product-price", {
28
+ method: "POST";
29
+ body: import("zod").ZodObject<{
30
+ productId: import("zod").ZodString;
31
+ basePriceInCents: import("zod").ZodNumber;
32
+ currencyCode: import("zod").ZodString;
33
+ }, import("zod/v4/core").$strip>;
34
+ }, {
35
+ error: string;
36
+ status: number;
37
+ amount?: never;
38
+ formatted?: never;
39
+ currency?: never;
40
+ } | {
41
+ amount: number;
42
+ formatted: string;
43
+ currency: string;
44
+ error?: never;
45
+ status?: never;
46
+ }>;
47
+ };
48
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/store/endpoints/routes.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAI1B,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { convertPrice } from "./convert-price";
2
+ import { getProductPrice } from "./get-product-price";
3
+ import { listCurrencies } from "./list-currencies";
4
+ export const storeEndpoints = {
5
+ "/currencies": listCurrencies,
6
+ "/currencies/convert": convertPrice,
7
+ "/currencies/product-price": getProductPrice,
8
+ };
package/package.json ADDED
@@ -0,0 +1,108 @@
1
+ {
2
+ "name": "@86d-store/multi-currency",
3
+ "version": "0.1.0",
4
+ "description": "Multi-currency support module for 86d commerce platform",
5
+ "keywords": [
6
+ "commerce",
7
+ "multi-currency",
8
+ "exchange-rates",
9
+ "internationalization",
10
+ "86d"
11
+ ],
12
+ "homepage": "https://86d.app",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/86d-store/86d.git",
16
+ "directory": "modules/multi-currency"
17
+ },
18
+ "license": "MIT",
19
+ "author": "86d <chat@86d.app>",
20
+ "type": "module",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "default": "./dist/index.js"
25
+ },
26
+ "./*": "./dist/*",
27
+ "./admin/components/currency-detail": {
28
+ "types": "./dist/admin/components/currency-detail.d.ts",
29
+ "default": "./dist/admin/components/currency-detail.js"
30
+ },
31
+ "./admin/components/currency-form": {
32
+ "types": "./dist/admin/components/currency-form.d.ts",
33
+ "default": "./dist/admin/components/currency-form.js"
34
+ },
35
+ "./admin/components/currency-list": {
36
+ "types": "./dist/admin/components/currency-list.d.ts",
37
+ "default": "./dist/admin/components/currency-list.js"
38
+ },
39
+ "./capabilities": {
40
+ "types": "./dist/capabilities.d.ts",
41
+ "default": "./dist/capabilities.js"
42
+ },
43
+ "./components": {
44
+ "types": "./dist/store/components/mdx.d.ts",
45
+ "default": "./dist/store/components/mdx.js"
46
+ }
47
+ },
48
+ "files": [
49
+ "dist",
50
+ "!dist/**/__tests__",
51
+ "!dist/**/__tests__/**",
52
+ "!dist/**/*.test.js",
53
+ "!dist/**/*.test.d.ts",
54
+ "!dist/**/*.test.d.ts.map",
55
+ "README.md"
56
+ ],
57
+ "scripts": {
58
+ "build": "86d module build",
59
+ "check": "biome check src",
60
+ "check:fix": "biome check --write --unsafe src",
61
+ "clean": "git clean -xdf .cache .turbo dist node_modules",
62
+ "dev": "tsc",
63
+ "test": "vitest run",
64
+ "test:watch": "vitest watch",
65
+ "typecheck": "tsc --noEmit --emitDeclarationOnly false"
66
+ },
67
+ "dependencies": {
68
+ "@86d-store/core": "0.1.0"
69
+ },
70
+ "peerDependencies": {
71
+ "react": "19.2.8"
72
+ },
73
+ "peerDependenciesMeta": {
74
+ "react": {
75
+ "optional": true
76
+ }
77
+ },
78
+ "publishConfig": {
79
+ "access": "public",
80
+ "exports": {
81
+ ".": {
82
+ "types": "./dist/index.d.ts",
83
+ "default": "./dist/index.js"
84
+ },
85
+ "./*": "./dist/*",
86
+ "./admin/components/currency-detail": {
87
+ "types": "./dist/admin/components/currency-detail.d.ts",
88
+ "default": "./dist/admin/components/currency-detail.js"
89
+ },
90
+ "./admin/components/currency-form": {
91
+ "types": "./dist/admin/components/currency-form.d.ts",
92
+ "default": "./dist/admin/components/currency-form.js"
93
+ },
94
+ "./admin/components/currency-list": {
95
+ "types": "./dist/admin/components/currency-list.d.ts",
96
+ "default": "./dist/admin/components/currency-list.js"
97
+ },
98
+ "./capabilities": {
99
+ "types": "./dist/capabilities.d.ts",
100
+ "default": "./dist/capabilities.js"
101
+ },
102
+ "./components": {
103
+ "types": "./dist/store/components/mdx.d.ts",
104
+ "default": "./dist/store/components/mdx.js"
105
+ }
106
+ }
107
+ }
108
+ }