calcsuite-react 1.0.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 (55) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/LICENSE +26 -0
  3. package/README.md +87 -0
  4. package/dist/calcsuite.js +6699 -0
  5. package/dist/core/calculators/deposits.d.ts +2 -0
  6. package/dist/core/calculators/investments.d.ts +2 -0
  7. package/dist/core/calculators/loans.d.ts +2 -0
  8. package/dist/core/calculators/retirement_us.d.ts +2 -0
  9. package/dist/core/calculators/returns.d.ts +2 -0
  10. package/dist/core/calculators/scientific.d.ts +2 -0
  11. package/dist/core/calculators/tax.d.ts +2 -0
  12. package/dist/core/calculators/utility.d.ts +2 -0
  13. package/dist/core/currency.d.ts +20 -0
  14. package/dist/core/decimal.d.ts +9 -0
  15. package/dist/core/finance.d.ts +30 -0
  16. package/dist/core/format.d.ts +16 -0
  17. package/dist/core/kit.d.ts +109 -0
  18. package/dist/core/liveRates.d.ts +11 -0
  19. package/dist/core/loan.d.ts +70 -0
  20. package/dist/core/registry.d.ts +10 -0
  21. package/dist/core/sci/evaluator.d.ts +14 -0
  22. package/dist/core/sci/index.d.ts +22 -0
  23. package/dist/core/sci/parser.d.ts +32 -0
  24. package/dist/core/sci/tokenizer.d.ts +10 -0
  25. package/dist/export/index.d.ts +56 -0
  26. package/dist/index.d.ts +49 -0
  27. package/dist/settings/SettingsContext.d.ts +20 -0
  28. package/dist/settings/settings.d.ts +72 -0
  29. package/dist/transport/client.d.ts +54 -0
  30. package/dist/transport/fx.d.ts +23 -0
  31. package/dist/transport/payload.d.ts +21 -0
  32. package/dist/transport/types.d.ts +198 -0
  33. package/dist/ui/CalculatorPanel.d.ts +14 -0
  34. package/dist/ui/Chart.d.ts +4 -0
  35. package/dist/ui/CommandPalette.d.ts +13 -0
  36. package/dist/ui/CurrencyConverter.d.ts +1 -0
  37. package/dist/ui/Dialog.d.ts +8 -0
  38. package/dist/ui/ExportMenu.d.ts +4 -0
  39. package/dist/ui/HistoryPanel.d.ts +6 -0
  40. package/dist/ui/IntegrationPanel.d.ts +8 -0
  41. package/dist/ui/Launcher.d.ts +34 -0
  42. package/dist/ui/LoanEmiPanel.d.ts +1 -0
  43. package/dist/ui/ResultCard.d.ts +4 -0
  44. package/dist/ui/SaveButton.d.ts +4 -0
  45. package/dist/ui/ScheduleTable.d.ts +4 -0
  46. package/dist/ui/SciCalculator.d.ts +4 -0
  47. package/dist/ui/SettingsPanel.d.ts +1 -0
  48. package/dist/ui/Shell.d.ts +5 -0
  49. package/dist/ui/fields.d.ts +9 -0
  50. package/dist/ui/history.d.ts +15 -0
  51. package/dist/ui/icons.d.ts +8 -0
  52. package/dist/ui/overlayStack.d.ts +3 -0
  53. package/dist/ui/themePresets.d.ts +19 -0
  54. package/package.json +94 -0
  55. package/src/theme.css +751 -0
@@ -0,0 +1,23 @@
1
+ import type { FxRateProvider } from './types';
2
+ export interface RateSnapshot {
3
+ base: string;
4
+ timestamp: string;
5
+ rates: Record<string, string>;
6
+ lastUpdated: number;
7
+ stale: boolean;
8
+ }
9
+ export interface FxCacheOptions {
10
+ ttlMs?: number;
11
+ /** Injected/offline seed: base → rates. Used when the provider can't be reached. */
12
+ seed?: Record<string, Record<string, string>>;
13
+ now?: () => number;
14
+ }
15
+ export declare function createFxCache(provider: FxRateProvider | undefined, options?: FxCacheOptions): {
16
+ getRates: (base: string, symbols: string[]) => Promise<RateSnapshot>;
17
+ /** Non-throwing peek at whatever is cached, if anything. */
18
+ peek: (base: string, symbols: string[]) => RateSnapshot | undefined;
19
+ readonly lastUpdated: Date | null;
20
+ isStale(base: string, symbols: string[]): boolean;
21
+ clear: () => void;
22
+ };
23
+ export type FxCache = ReturnType<typeof createFxCache>;
@@ -0,0 +1,21 @@
1
+ import type { KeyCase, PayloadContext, PayloadEnvelope, PayloadField, SaveContext } from './types';
2
+ /** Apply keyCase to each dot-path segment (so 'meta.tenantId' cases both). */
3
+ export declare function caseKey(path: string, mode: KeyCase): string;
4
+ export interface BuildResult {
5
+ body: Record<string, unknown>;
6
+ warnings: string[];
7
+ }
8
+ /**
9
+ * Assemble the outgoing body from PayloadField rows + envelope controls.
10
+ * Returns the body directly (not wrapped in a result object) so callers and
11
+ * tests can use it as-is; warnings are surfaced via console.warn.
12
+ */
13
+ export declare function buildPayload(fields: PayloadField[], envelope: PayloadEnvelope, ctx: PayloadContext): Record<string, unknown>;
14
+ /** True when this envelope will emit unsafe JS-number values. */
15
+ export declare function hasNumberEncodingRisk(env: PayloadEnvelope): boolean;
16
+ export declare const DEFAULT_ENVELOPE: PayloadEnvelope;
17
+ /**
18
+ * Build the exact `POST {save}` body from §9.3. All numbers become strings so
19
+ * a Decimal never round-trips through a JSON float.
20
+ */
21
+ export declare function buildSavePayload(ctx: SaveContext): Record<string, unknown>;
@@ -0,0 +1,198 @@
1
+ import type { Region, Settings } from '../settings/settings';
2
+ export type ISODate = string;
3
+ export interface TransportConfig {
4
+ baseUrl: string;
5
+ endpoints: {
6
+ save?: string;
7
+ update?: string;
8
+ get?: string;
9
+ list?: string;
10
+ delete?: string;
11
+ upload?: string;
12
+ uploadUrl?: string;
13
+ rates?: string;
14
+ settings?: string;
15
+ };
16
+ /** Auth is a callback so tokens are never stored by FinCalc. */
17
+ getAuth?: () => Promise<Record<string, string>> | Record<string, string>;
18
+ /** Host-supplied fetch so their interceptors, tracing and proxies apply. */
19
+ fetch?: typeof fetch;
20
+ headers?: Record<string, string> | (() => Record<string, string>);
21
+ credentials?: RequestCredentials;
22
+ timeoutMs?: number;
23
+ retry?: RetryConfig;
24
+ idempotency?: IdempotencyConfig;
25
+ offlineQueue?: OfflineQueueConfig;
26
+ upload: UploadConfig;
27
+ transformRequest?: (body: unknown, ctx: RequestCtx) => unknown;
28
+ transformResponse?: (raw: unknown, ctx: RequestCtx) => unknown;
29
+ onRequest?: (ctx: RequestCtx) => void;
30
+ onResponse?: (ctx: ResponseCtx) => void;
31
+ onError?: (err: TransportError, ctx: RequestCtx) => void;
32
+ onProgress?: (p: {
33
+ loaded: number;
34
+ total: number;
35
+ pct: number;
36
+ }) => void;
37
+ }
38
+ export interface RetryConfig {
39
+ attempts: number;
40
+ backoff: 'fixed' | 'exponential';
41
+ baseDelayMs: number;
42
+ retryOn: number[];
43
+ respectRetryAfter: boolean;
44
+ }
45
+ export interface IdempotencyConfig {
46
+ enabled: boolean;
47
+ headerName: string;
48
+ keyFrom: 'inputsHash' | 'uuid';
49
+ }
50
+ export interface OfflineQueueConfig {
51
+ enabled: boolean;
52
+ storage: 'memory' | 'localStorage' | 'indexedDB';
53
+ maxItems: number;
54
+ flushOn: 'reconnect' | 'manual' | 'interval';
55
+ intervalMs?: number;
56
+ }
57
+ export interface UploadConfig {
58
+ strategy: 'multipart' | 'presigned' | 'base64Json';
59
+ fieldName: string;
60
+ extraFields?: Record<string, string> | (() => Record<string, string>);
61
+ maxBytes: number;
62
+ accept: string[];
63
+ filenameTemplate: string;
64
+ chunked?: {
65
+ enabled: boolean;
66
+ chunkBytes: number;
67
+ };
68
+ checksum?: 'none' | 'sha256';
69
+ beforeUpload?: (file: Blob, meta: UploadMeta) => Promise<boolean> | boolean;
70
+ }
71
+ export interface UploadMeta {
72
+ calculationId?: string;
73
+ calculator: string;
74
+ kind: string;
75
+ inputsHash: string;
76
+ filename?: string;
77
+ contentType?: string;
78
+ bytes?: number;
79
+ checksum?: string;
80
+ }
81
+ export interface Attachment {
82
+ id: string;
83
+ kind: string;
84
+ filename: string;
85
+ bytes: number;
86
+ url: string | null;
87
+ }
88
+ export interface RequestCtx {
89
+ endpoint: string;
90
+ method: string;
91
+ url: string;
92
+ /** Headers with auth already redacted — safe to log. */
93
+ headers: Record<string, string>;
94
+ body?: unknown;
95
+ attempt: number;
96
+ }
97
+ export interface ResponseCtx extends RequestCtx {
98
+ status: number;
99
+ ok: boolean;
100
+ durationMs: number;
101
+ data: unknown;
102
+ }
103
+ export declare class TransportError extends Error {
104
+ code: string;
105
+ status: number;
106
+ /** The request that failed, auth redacted. */
107
+ request?: RequestCtx;
108
+ constructor(message: string, opts?: {
109
+ code: string;
110
+ status?: number;
111
+ request?: RequestCtx;
112
+ });
113
+ }
114
+ export interface FxRateProvider {
115
+ getRates(base: string, symbols: string[]): Promise<{
116
+ timestamp: string;
117
+ rates: Record<string, string>;
118
+ }>;
119
+ getHistorical?(base: string, symbols: string[], date: ISODate): Promise<{
120
+ timestamp: string;
121
+ rates: Record<string, string>;
122
+ }>;
123
+ }
124
+ export type PayloadSource = {
125
+ kind: 'static';
126
+ value: string | number | boolean | null;
127
+ } | {
128
+ kind: 'input';
129
+ path: string;
130
+ } | {
131
+ kind: 'output';
132
+ path: string;
133
+ } | {
134
+ kind: 'setting';
135
+ path: string;
136
+ } | {
137
+ kind: 'context';
138
+ path: string;
139
+ } | {
140
+ kind: 'token';
141
+ name: string;
142
+ } | {
143
+ kind: 'callback';
144
+ fn: (ctx: PayloadContext) => unknown;
145
+ };
146
+ export interface PayloadField {
147
+ key: string;
148
+ source: PayloadSource;
149
+ type: 'string' | 'number' | 'boolean' | 'date' | 'json';
150
+ required: boolean;
151
+ transform?: 'none' | 'toFixed2' | 'toMinorUnits' | 'upper' | 'lower' | 'trim';
152
+ omitWhenEmpty: boolean;
153
+ }
154
+ export type KeyCase = 'asIs' | 'camel' | 'snake' | 'kebab';
155
+ export interface PayloadEnvelope {
156
+ mode: 'flat' | 'wrapped';
157
+ wrapperKey?: string;
158
+ keyCase: KeyCase;
159
+ dateFormat?: string;
160
+ nullHandling: 'omit' | 'null';
161
+ numberEncoding: 'string' | 'number';
162
+ include: {
163
+ inputs: boolean;
164
+ outputs: boolean;
165
+ schedule: boolean;
166
+ settingsSnapshot: boolean;
167
+ meta: boolean;
168
+ };
169
+ }
170
+ /** Everything the payload builder can read from. */
171
+ export interface PayloadContext {
172
+ inputs: Record<string, unknown>;
173
+ outputs: Record<string, unknown>;
174
+ settings: Record<string, unknown>;
175
+ context: Record<string, unknown>;
176
+ tokens: Record<string, string>;
177
+ schedule?: unknown;
178
+ }
179
+ /** Input to the default §9.3 save-payload builder. */
180
+ export interface SaveContext {
181
+ calculator: string;
182
+ region: Region;
183
+ settings: Settings;
184
+ inputs: Record<string, unknown>;
185
+ outputs: Record<string, string | number | null>;
186
+ inputsHash: string;
187
+ title?: string;
188
+ tags?: string[];
189
+ computedAt?: string;
190
+ formula?: string;
191
+ assumptions?: string[];
192
+ warnings?: string[];
193
+ attachments?: Attachment[];
194
+ coreVersion?: string;
195
+ clientId?: string;
196
+ locale?: string;
197
+ clientTz?: string;
198
+ }
@@ -0,0 +1,14 @@
1
+ import { type ComponentType } from 'react';
2
+ import type { CalculatorDef, ResultView, Values } from '../core/kit';
3
+ export declare function CalculatorPanel({ def: defProp, id, seed, customRegistry, onResult, actions, }: {
4
+ /** A calculator definition, or use `id` to resolve a registered one. */
5
+ def?: CalculatorDef;
6
+ /** Registered calculator id, e.g. "loan.emi", "invest.sip". Resolved via the registry. */
7
+ id?: string;
8
+ seed?: Values;
9
+ customRegistry?: Record<string, ComponentType<{
10
+ def: CalculatorDef;
11
+ }>>;
12
+ onResult?: (r: ResultView | null, values: Values) => void;
13
+ actions?: (r: ResultView | null, values: Values) => React.ReactNode;
14
+ }): import("react").JSX.Element;
@@ -0,0 +1,4 @@
1
+ import type { ChartSeries } from '../core/kit';
2
+ export declare function Chart({ chart }: {
3
+ chart: ChartSeries;
4
+ }): import("react").JSX.Element;
@@ -0,0 +1,13 @@
1
+ import type { CalculatorDef } from '../core/kit';
2
+ export interface PaletteAction {
3
+ id: string;
4
+ label: string;
5
+ hint?: string;
6
+ run: () => void;
7
+ }
8
+ export declare function CommandPalette({ calculators, actions, onPick, onClose, }: {
9
+ calculators: CalculatorDef[];
10
+ actions: PaletteAction[];
11
+ onPick: (id: string) => void;
12
+ onClose: () => void;
13
+ }): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ export declare function CurrencyConverter(): import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ import { type ReactNode } from 'react';
2
+ export declare function Dialog({ open, onClose, children, title, size, }: {
3
+ open: boolean;
4
+ onClose: () => void;
5
+ children: ReactNode;
6
+ title?: string;
7
+ size?: 'md' | 'lg' | 'xl' | 'full';
8
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,4 @@
1
+ import { type ExportPayload } from '../export';
2
+ export declare function ExportMenu({ payload }: {
3
+ payload: ExportPayload;
4
+ }): import("react").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import type { HistoryItem } from './history';
2
+ export declare function HistoryPanel({ items, onRestore, onClear, }: {
3
+ items: HistoryItem[];
4
+ onRestore: (item: HistoryItem) => void;
5
+ onClear: () => void;
6
+ }): import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ import type { TransportConfig } from '../transport/types';
2
+ export interface IntegrationPanelProps {
3
+ /** Config the host passed as a prop — any field it set renders read-only. */
4
+ hostConfig?: Partial<TransportConfig>;
5
+ /** Host token resolver. When present the token row is read-only, never an input. */
6
+ getToken?: () => string | Promise<string>;
7
+ }
8
+ export declare function IntegrationPanel({ hostConfig, getToken }?: IntegrationPanelProps): import("react").JSX.Element;
@@ -0,0 +1,34 @@
1
+ import { type CSSProperties, type ReactNode } from 'react';
2
+ /** Handed to a custom `trigger` / function-child so you can open the dialog from any UI. */
3
+ export interface LauncherApi {
4
+ open: boolean;
5
+ openDialog: () => void;
6
+ close: () => void;
7
+ toggle: () => void;
8
+ }
9
+ export interface LauncherProps {
10
+ /** 'fab' floating button · 'inline' in-flow button · 'headless' you render the trigger. */
11
+ variant?: 'fab' | 'inline' | 'headless';
12
+ position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
13
+ hotkey?: string | null;
14
+ defaultOpen?: boolean;
15
+ /** Controlled open state (pair with `onOpenChange`). */
16
+ open?: boolean;
17
+ onOpenChange?: (open: boolean) => void;
18
+ label?: string;
19
+ /** Any icon/content for the built-in button — an emoji, an <svg>, an <img>, a component. */
20
+ icon?: ReactNode;
21
+ /** Passthrough styling for the built-in button. */
22
+ className?: string;
23
+ style?: CSSProperties;
24
+ /**
25
+ * Render your OWN trigger (any button/element/UI). Receives `{ open, toggle, openDialog, close }`.
26
+ * When provided — or with `variant="headless"` and a function child — the built-in button is not rendered.
27
+ */
28
+ trigger?: (api: LauncherApi) => ReactNode;
29
+ children?: ReactNode | ((api: LauncherApi) => ReactNode);
30
+ /** Dialog sizing / title. */
31
+ dialogSize?: 'md' | 'lg' | 'xl' | 'full';
32
+ dialogTitle?: string;
33
+ }
34
+ export declare function Launcher({ variant, position, hotkey, defaultOpen, open: openProp, onOpenChange, label, icon, className, style, trigger, children, dialogSize, dialogTitle, }: LauncherProps): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ export declare function LoanEmiPanel(): import("react").JSX.Element;
@@ -0,0 +1,4 @@
1
+ import type { ResultView } from '../core/kit';
2
+ export declare function ResultCard({ view }: {
3
+ view: ResultView;
4
+ }): import("react").JSX.Element;
@@ -0,0 +1,4 @@
1
+ import { type HistoryItem } from './history';
2
+ export declare function SaveButton({ item }: {
3
+ item: Omit<HistoryItem, 'at'>;
4
+ }): import("react").JSX.Element;
@@ -0,0 +1,4 @@
1
+ import type { ScheduleView } from '../core/kit';
2
+ export declare function ScheduleTable({ schedule }: {
3
+ schedule: ScheduleView;
4
+ }): import("react").JSX.Element;
@@ -0,0 +1,4 @@
1
+ import type { CalculatorDef } from '../core/kit';
2
+ export declare function SciCalculator({ def }: {
3
+ def: CalculatorDef;
4
+ }): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ export declare function SettingsPanel(): import("react").JSX.Element;
@@ -0,0 +1,5 @@
1
+ import type { CalculatorDef } from '../core/kit';
2
+ export declare function Shell({ onClose }: {
3
+ onClose?: () => void;
4
+ }): import("react").JSX.Element;
5
+ export type { CalculatorDef };
@@ -0,0 +1,9 @@
1
+ import type { FieldSchema, Values, FieldValue } from '../core/kit';
2
+ import type { Region } from '../settings/settings';
3
+ export declare function labelFor(f: FieldSchema, region: Region): string;
4
+ export declare function Field({ f, value, onChange, values, }: {
5
+ f: FieldSchema;
6
+ value: FieldValue | undefined;
7
+ onChange: (v: FieldValue) => void;
8
+ values: Values;
9
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,15 @@
1
+ import type { Values } from '../core/kit';
2
+ export interface HistoryItem {
3
+ id: string;
4
+ title: string;
5
+ primary: string;
6
+ values: Values;
7
+ at: number;
8
+ }
9
+ export declare function saveHistory(item: Omit<HistoryItem, 'at'>): void;
10
+ export declare function clearHistory(): void;
11
+ export declare function useHistory(): {
12
+ items: HistoryItem[];
13
+ save: (item: Omit<HistoryItem, "at">) => void;
14
+ clear: () => void;
15
+ };
@@ -0,0 +1,8 @@
1
+ type IconProps = {
2
+ size?: number;
3
+ };
4
+ export declare function GearIcon({ size }: IconProps): import("react").JSX.Element;
5
+ export declare function SunIcon({ size }: IconProps): import("react").JSX.Element;
6
+ export declare function MoonIcon({ size }: IconProps): import("react").JSX.Element;
7
+ export declare function HistoryIcon({ size }: IconProps): import("react").JSX.Element;
8
+ export {};
@@ -0,0 +1,3 @@
1
+ export declare const pushOverlay: () => void;
2
+ export declare const popOverlay: () => void;
3
+ export declare const hasOverlay: () => boolean;
@@ -0,0 +1,19 @@
1
+ export type AccentPreset = 'default' | 'cyan' | 'purple' | 'blue' | 'orange' | 'gray';
2
+ export interface Accent {
3
+ id: AccentPreset;
4
+ label: string;
5
+ main: string;
6
+ light: string;
7
+ dark: string;
8
+ ink: string;
9
+ }
10
+ export declare const ACCENTS: Accent[];
11
+ export declare const accentByMain: (main: string) => Accent | undefined;
12
+ export declare const FONT_FALLBACK = "-apple-system, 'Segoe UI', system-ui, sans-serif";
13
+ export interface FontChoice {
14
+ id: string;
15
+ label: string;
16
+ stack: string;
17
+ }
18
+ export declare const FONTS: FontChoice[];
19
+ export declare const fontStack: (id: string) => string;
package/package.json ADDED
@@ -0,0 +1,94 @@
1
+ {
2
+ "name": "calcsuite-react",
3
+ "version": "1.0.0",
4
+ "description": "Decimal-exact, offline-first financial calculator library for React — India (₹) & US ($). ~55 calculators, scientific calculator, live FX, export, launcher/dialog. The financial-calculator module of AceCBM (Ace Cloud Business Management).",
5
+ "keywords": [
6
+ "finance",
7
+ "calculator",
8
+ "emi",
9
+ "loan",
10
+ "sip",
11
+ "tax",
12
+ "react",
13
+ "decimal",
14
+ "india",
15
+ "us",
16
+ "acecbm"
17
+ ],
18
+ "license": "MIT",
19
+ "author": "AceCBM (Ace Cloud Business Management)",
20
+ "homepage": "https://github.com/ashish13377/calcsuite#readme",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/ashish13377/calcsuite.git"
24
+ },
25
+ "bugs": {
26
+ "url": "https://github.com/ashish13377/calcsuite/issues"
27
+ },
28
+ "type": "module",
29
+ "sideEffects": [
30
+ "**/*.css"
31
+ ],
32
+ "main": "./dist/calcsuite.js",
33
+ "module": "./dist/calcsuite.js",
34
+ "types": "./dist/index.d.ts",
35
+ "exports": {
36
+ ".": {
37
+ "types": "./dist/index.d.ts",
38
+ "import": "./dist/calcsuite.js"
39
+ },
40
+ "./theme.css": "./src/theme.css"
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "src/theme.css",
45
+ "README.md",
46
+ "LICENSE",
47
+ "CHANGELOG.md"
48
+ ],
49
+ "engines": {
50
+ "node": ">=18"
51
+ },
52
+ "publishConfig": {
53
+ "access": "public"
54
+ },
55
+ "scripts": {
56
+ "dev": "vite",
57
+ "demo:build": "tsc -b && vite build",
58
+ "build": "vite build -c vite.lib.config.ts && npm run build:types",
59
+ "build:types": "tsc -p tsconfig.lib.json",
60
+ "preview": "vite preview",
61
+ "test": "vitest run",
62
+ "typecheck": "tsc -b",
63
+ "prepack": "npm run build",
64
+ "prepublishOnly": "npm run test"
65
+ },
66
+ "peerDependencies": {
67
+ "react": "^18.3.1 || ^19.0.0",
68
+ "react-dom": "^18.3.1 || ^19.0.0"
69
+ },
70
+ "peerDependenciesMeta": {
71
+ "jspdf": {
72
+ "optional": true
73
+ },
74
+ "xlsx": {
75
+ "optional": true
76
+ }
77
+ },
78
+ "dependencies": {
79
+ "decimal.js": "^10.4.3"
80
+ },
81
+ "devDependencies": {
82
+ "@types/react": "^18.3.12",
83
+ "@types/react-dom": "^18.3.1",
84
+ "@vitejs/plugin-react": "^4.3.4",
85
+ "happy-dom": "^20.13.2",
86
+ "jspdf": "^4.2.1",
87
+ "react": "^18.3.1",
88
+ "react-dom": "^18.3.1",
89
+ "typescript": "^5.6.3",
90
+ "vite": "^5.4.11",
91
+ "vitest": "^2.1.8",
92
+ "xlsx": "^0.18.5"
93
+ }
94
+ }