@jarenjs/calc 0.34.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/README.md +60 -0
  2. package/dist/types/ast.d.ts +65 -0
  3. package/dist/types/compile.d.ts +52 -0
  4. package/dist/types/component/index.d.ts +259 -0
  5. package/dist/types/component/rates/binance.d.ts +34 -0
  6. package/dist/types/component/rates/coingecko.d.ts +47 -0
  7. package/dist/types/component/rates/index.d.ts +84 -0
  8. package/dist/types/component/rules.d.ts +180 -0
  9. package/dist/types/component/schema.d.ts +38 -0
  10. package/dist/types/env.d.ts +24 -0
  11. package/dist/types/errors.d.ts +18 -0
  12. package/dist/types/index.d.ts +42 -0
  13. package/dist/types/modes/converter.d.ts +46 -0
  14. package/dist/types/modes/financial.d.ts +59 -0
  15. package/dist/types/modes/index.d.ts +38 -0
  16. package/dist/types/modes/programmer.d.ts +50 -0
  17. package/dist/types/modes/scientific.d.ts +28 -0
  18. package/dist/types/modes/standard.d.ts +32 -0
  19. package/dist/types/parser/index.d.ts +32 -0
  20. package/dist/types/plot/plot2d.d.ts +70 -0
  21. package/dist/types/plot/plot3d.d.ts +68 -0
  22. package/dist/types/render/error.d.ts +19 -0
  23. package/dist/types/theme.d.ts +35 -0
  24. package/dist/types/to-expr.d.ts +13 -0
  25. package/dist/types/utils.d.ts +9 -0
  26. package/docs/CALC-FORMAT.md +79 -0
  27. package/package.json +71 -0
  28. package/schemas/financial-inputs.schema.json +15 -0
  29. package/schemas/jaren-calc-ast.schema.json +91 -0
  30. package/schemas/jaren-calc-state.schema.json +52 -0
  31. package/src/ast.js +96 -0
  32. package/src/compile.js +119 -0
  33. package/src/component/index.js +352 -0
  34. package/src/component/rates/binance.js +44 -0
  35. package/src/component/rates/coingecko.js +63 -0
  36. package/src/component/rates/index.js +116 -0
  37. package/src/component/rules.js +118 -0
  38. package/src/component/schema.js +24 -0
  39. package/src/env.js +163 -0
  40. package/src/errors.js +23 -0
  41. package/src/index.js +85 -0
  42. package/src/modes/converter.js +73 -0
  43. package/src/modes/financial.js +89 -0
  44. package/src/modes/index.js +24 -0
  45. package/src/modes/programmer.js +69 -0
  46. package/src/modes/scientific.js +31 -0
  47. package/src/modes/standard.js +39 -0
  48. package/src/parser/index.js +220 -0
  49. package/src/plot/plot2d.js +221 -0
  50. package/src/plot/plot3d.js +177 -0
  51. package/src/render/error.js +25 -0
  52. package/src/theme.js +76 -0
  53. package/src/to-expr.js +84 -0
  54. package/src/utils.js +11 -0
  55. package/styles/calc.css +128 -0
package/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # @jarenjs/calc
2
+
3
+ A native, headless **multi-mode calculator** for the jarenjs stack:
4
+ standard / scientific / programmer / financial / converter, with **x·y and
5
+ x·y·z plotting rendered as pure-vnode SVG** through `@jarenjs/view`. The
6
+ calculator *is* an `@jarenjs/app` document (state is JSON, keys are data
7
+ bindings, transitions are copy-on-write RFC-6902 patches), and it is built
8
+ on `@jarenjs/core` — it holds no formulas or unit factors of its own.
9
+
10
+ Zero runtime dependencies beyond `@jarenjs/*`. No `eval`, no `new
11
+ Function` (CSP-safe). SSR-able and deterministic.
12
+
13
+ ## Two layers
14
+
15
+ - **Engine** (`@jarenjs/calc`) — pure functions over data. A two-stage
16
+ expression compiler `parseExpression(text) → ExprAST → compileExpr →
17
+ (scope) => value`, its canonical printer `toExpression` (a round-trip
18
+ fixed point), and `plot2d`/`plot3d`/`calcToVnode` (pure-vnode SVG).
19
+ Imports only `@jarenjs/core` + `@jarenjs/view`.
20
+ - **Component** (`@jarenjs/calc/component`) — the `@jarenjs/app` glue:
21
+ `createCalcComponent()` → `{ initialState, actions, mode, rules,
22
+ effects, subs, viewModel, createApp }`, the keypad/panel JSLT view, the
23
+ financial `@jarenjs/forms` panel, and the live crypto/fiat rates layer.
24
+
25
+ ```js
26
+ import { evaluate, parseExpression, toExpression, plot2d, toSvgString } from '@jarenjs/calc';
27
+
28
+ evaluate('sin(pi/2) + 2^10').value; // 1025
29
+ toExpression(parseExpression('a-(b-c)')); // "a - (b - c)"
30
+ toSvgString(plot2d('sin(x)', { domain: [-6.28, 6.28] })); // standalone SVG
31
+ ```
32
+
33
+ ```js
34
+ import { createCalcComponent } from '@jarenjs/calc/component';
35
+ const calc = createCalcComponent({ provider: 'coingecko' });
36
+ const app = calc.createApp({ node: document.getElementById('calc') });
37
+ ```
38
+
39
+ ## The numeric kernel lives in `@jarenjs/core`
40
+
41
+ Reusable primitives were pushed *down* into core, so any package can use
42
+ them: `@jarenjs/core/math` (transcendentals, `word.js` BigInt word math,
43
+ `solve.js` root finders, `mat4`/`project.js` 3D kernel, `format.js`),
44
+ `@jarenjs/core/finance` (TVM, cash-flow, amortization, interest,
45
+ depreciation, bond, indicators, returns) and `@jarenjs/core/convert`
46
+ (fixed-factor dimensional conversion + the pure `convertCurrency` rate-table
47
+ primitive). The calculator's financial mode calls `core/finance`; its
48
+ converter calls `core/convert`. Core is pure and never fetches.
49
+
50
+ ## Live currency (the maturity example)
51
+
52
+ The converter's currency dimension takes live crypto+fiat rates from free
53
+ public tickers (CoinGecko default, Binance alternative) through an
54
+ `@jarenjs/app` effect + a `when`-gated polling subscription. The **pure
55
+ conversion** is `@jarenjs/core/convert`'s `convertCurrency`; only the
56
+ *fetch* is component-side. A static fallback table keeps the converter,
57
+ SSR and offline tests working with no network; failures route to an error
58
+ action.
59
+
60
+ See `docs/CALC-FORMAT.md` and `ARCHITECTURE.md` for the full contract.
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @file The expression AST. Monomorphic node
3
+ * constructors — one plain-object shape per kind — so the compiler and
4
+ * printer branch on a single `type` tag and the reconciler/`deepEqual`
5
+ * round-trip stays cheap. The AST is **geometry-free**: it carries no
6
+ * layout, no source formatting (not even a literal's original radix), so
7
+ * that `parseExpression(toExpression(ast))` deep-equals `ast`. Meaning is
8
+ * imposed later — by `compileExpr` (evaluation) or the plotter
9
+ * (projection), never by the parser.
10
+ */
11
+ export declare const CALC_AST_VERSION = "0.13.0";
12
+ /** Numeric literal (value only — the radix/format is not preserved). */
13
+ export declare function num(value: any): {
14
+ type: string;
15
+ value: number;
16
+ };
17
+ /** A named constant (`pi`, `e`, `phi`, `tau`, `inf`, `nan`). */
18
+ export declare function constant(name: any): {
19
+ type: string;
20
+ name: any;
21
+ };
22
+ /** A variable / free identifier (`x`, `y`, `ans`, `mem`). */
23
+ export declare function variable(name: any): {
24
+ type: string;
25
+ name: any;
26
+ };
27
+ /** A prefix unary node (`-`, `+`, `~`). */
28
+ export declare function unary(op: any, arg: any): {
29
+ type: string;
30
+ op: any;
31
+ arg: any;
32
+ };
33
+ /** A postfix node (`!` factorial, `%` percent). */
34
+ export declare function postfix(op: any, arg: any): {
35
+ type: string;
36
+ op: any;
37
+ arg: any;
38
+ };
39
+ /** A binary node (`+ - * / ^ & | << >>`). */
40
+ export declare function binary(op: any, left: any, right: any): {
41
+ type: string;
42
+ op: any;
43
+ left: any;
44
+ right: any;
45
+ };
46
+ /** A function call (`sin(x)`, `log(2, 8)`, `xor(a, b)`). */
47
+ export declare function call(name: any, args: any): {
48
+ type: string;
49
+ name: any;
50
+ args: any;
51
+ };
52
+ /**
53
+ * Free identifiers that always denote a constant rather than a variable.
54
+ * @type {Record<string, number>}
55
+ */
56
+ export declare const CONSTANTS: Record<string, number>;
57
+ /** Is `name` a known constant identifier? */
58
+ export declare function isConstant(name: any): boolean;
59
+ /**
60
+ * Structural equality for ASTs (used by the round-trip fixed-point
61
+ * tests). NaN compares equal to NaN so `nan` literals round-trip.
62
+ * @param {any} a @param {any} b
63
+ * @returns {boolean}
64
+ */
65
+ export declare function astEqual(a: any, b: any): boolean;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @file `compileExpr(ast, opts) → (scope) => number`. The second stage of
3
+ * the two-stage compiler: every dispatch decision — which operator
4
+ * closure, which function, whether an identifier is a constant — is made
5
+ * once, here, and baked into a nested closure. Evaluation then does no
6
+ * lookups on the hot path. No `eval`, no `new Function` (CSP-safe).
7
+ *
8
+ * `evaluate(source, scope?, opts?)` is the error-safe front door: it
9
+ * parses, compiles and runs, returning a tagged `{ ok, value } | { ok:
10
+ * false, error }` result so the app/render path never throws.
11
+ */
12
+ /**
13
+ * Compile an AST against an environment.
14
+ * @param {any} ast
15
+ * @param {{ env?: any }} [opts]
16
+ * @returns {(scope?: any) => number}
17
+ */
18
+ export declare function compileExpr(ast: any, opts?: {
19
+ env?: any;
20
+ }): (scope?: any) => number;
21
+ export type EvalOk = {
22
+ ok: true;
23
+ value: number;
24
+ };
25
+ export type EvalErr = {
26
+ ok: false;
27
+ error: {
28
+ message: string;
29
+ line?: number;
30
+ column?: number;
31
+ };
32
+ };
33
+ /**
34
+ * @typedef {object} EvalOk
35
+ * @property {true} ok
36
+ * @property {number} value
37
+ */
38
+ /**
39
+ * @typedef {object} EvalErr
40
+ * @property {false} ok
41
+ * @property {{ message: string, line?: number, column?: number }} error
42
+ */
43
+ /**
44
+ * Parse + compile + evaluate a source expression, error-safe.
45
+ * @param {string} source
46
+ * @param {any} [scope]
47
+ * @param {{ env?: any }} [opts]
48
+ * @returns {EvalOk | EvalErr}
49
+ */
50
+ export declare function evaluate(source: string, scope?: any, opts?: {
51
+ env?: any;
52
+ }): EvalOk | EvalErr;
@@ -0,0 +1,259 @@
1
+ /**
2
+ * @file The Calculator COMPONENT — part two of the package. Everything
3
+ * here is presentation + app glue; the engine knows none of it. `createCalcComponent(options)` returns the
4
+ * pieces the site (and a standalone `createApp`) compose into one
5
+ * `@jarenjs/app` document:
6
+ *
7
+ * - `initialState()` — the plain-JSON `$.calc` slice (immutable, COW).
8
+ * - `actions` — key/mode/plot/converter action query documents + the
9
+ * financial `@jarenjs/forms` write actions.
10
+ * - `mode` + `rules` — the JSLT `calculator` view (keypad, display, tape,
11
+ * mode menu, plot panel, financial form, converter).
12
+ * - `effects` / `subs` / `subEntry` — the live-rates effect + `when`-gated
13
+ * poll; the pure conversion stays in `@jarenjs/core/convert`.
14
+ * - `viewModel(state)` (a.k.a. `contributeCalcViewModel`) — the derivation
15
+ * boundary: display string, live result, four-base views, plot vnode,
16
+ * forms model, converter options — none of it stored in state.
17
+ * - `createApp()` — a standalone app document for reuse outside the site.
18
+ *
19
+ * The boundary is one-way: the component imports the engine, never the
20
+ * reverse.
21
+ */
22
+ /**
23
+ * Namespaced form action names so the financial panel never collides with
24
+ * another `@jarenjs/forms` instance on the same page (e.g. the website's
25
+ * schema playground, which also spreads `createFormActions`).
26
+ */
27
+ export declare const FORM_ACTIONS: {
28
+ input: string;
29
+ check: string;
30
+ number: string;
31
+ add: string;
32
+ remove: string;
33
+ };
34
+ /** The financial form's view rules (mode 'calculator'), reusable by the site. */
35
+ export declare const calcFormViewRules: any[];
36
+ /**
37
+ * The initial `$.calc` state slice.
38
+ * @returns {any}
39
+ */
40
+ export declare function calcInitialState(): any;
41
+ /**
42
+ * The action query documents. `dataPointer` for the financial form is
43
+ * `/calc/fin`.
44
+ * @returns {Record<string, any>}
45
+ */
46
+ export declare const calcActions: {
47
+ 'calc/key': {
48
+ patch: {
49
+ op: string;
50
+ path: string;
51
+ value: {
52
+ $concat: string[];
53
+ };
54
+ }[];
55
+ };
56
+ 'calc/clear': {
57
+ patch: {
58
+ op: string;
59
+ path: string;
60
+ value: string;
61
+ }[];
62
+ };
63
+ 'calc/set-entry': {
64
+ patch: {
65
+ op: string;
66
+ path: string;
67
+ value: string;
68
+ }[];
69
+ };
70
+ 'calc/back': {
71
+ effects: {
72
+ run: string;
73
+ with: {
74
+ entry: string;
75
+ };
76
+ }[];
77
+ };
78
+ 'calc/equals': {
79
+ effects: {
80
+ run: string;
81
+ with: {
82
+ entry: string;
83
+ mode: string;
84
+ angleMode: string;
85
+ wordBits: string;
86
+ signed: string;
87
+ ans: string;
88
+ memory: string;
89
+ };
90
+ }[];
91
+ };
92
+ 'calc/commit': {
93
+ patch: ({
94
+ op: string;
95
+ path: string;
96
+ value: string;
97
+ } | {
98
+ op: string;
99
+ path: string;
100
+ value: {
101
+ expr: string;
102
+ result: string;
103
+ };
104
+ })[];
105
+ };
106
+ 'calc/mode': {
107
+ patch: {
108
+ op: string;
109
+ path: string;
110
+ value: string;
111
+ }[];
112
+ };
113
+ 'calc/angle': {
114
+ patch: {
115
+ op: string;
116
+ path: string;
117
+ value: string;
118
+ }[];
119
+ };
120
+ 'calc/base': {
121
+ patch: {
122
+ op: string;
123
+ path: string;
124
+ value: string;
125
+ }[];
126
+ };
127
+ 'calc/word': {
128
+ patch: {
129
+ op: string;
130
+ path: string;
131
+ value: {
132
+ $number: string;
133
+ };
134
+ }[];
135
+ };
136
+ 'calc/sign': {
137
+ patch: {
138
+ op: string;
139
+ path: string;
140
+ value: {
141
+ $if: (string | boolean)[];
142
+ };
143
+ }[];
144
+ };
145
+ 'calc/mem-add': {
146
+ patch: {
147
+ op: string;
148
+ path: string;
149
+ value: {
150
+ $add: string[];
151
+ };
152
+ }[];
153
+ };
154
+ 'calc/mem-clear': {
155
+ patch: {
156
+ op: string;
157
+ path: string;
158
+ value: number;
159
+ }[];
160
+ };
161
+ 'calc/plot-expr': {
162
+ patch: {
163
+ op: string;
164
+ path: string;
165
+ value: string;
166
+ }[];
167
+ };
168
+ 'calc/plot-kind': {
169
+ patch: {
170
+ op: string;
171
+ path: string;
172
+ value: string;
173
+ }[];
174
+ };
175
+ 'calc/conv-dim': {
176
+ patch: {
177
+ op: string;
178
+ path: string;
179
+ value: string;
180
+ }[];
181
+ };
182
+ 'calc/conv-from': {
183
+ patch: {
184
+ op: string;
185
+ path: string;
186
+ value: string;
187
+ }[];
188
+ };
189
+ 'calc/conv-to': {
190
+ patch: {
191
+ op: string;
192
+ path: string;
193
+ value: string;
194
+ }[];
195
+ };
196
+ 'calc/conv-value': {
197
+ patch: {
198
+ op: string;
199
+ path: string;
200
+ value: {
201
+ $number: string;
202
+ };
203
+ }[];
204
+ };
205
+ 'calc/conv-swap': {
206
+ patch: {
207
+ op: string;
208
+ path: string;
209
+ value: string;
210
+ }[];
211
+ };
212
+ 'calc/rates-refresh': {
213
+ effects: {
214
+ run: string;
215
+ with: {
216
+ force: boolean;
217
+ };
218
+ }[];
219
+ };
220
+ 'calc/rates-ok': {
221
+ patch: {
222
+ op: string;
223
+ path: string;
224
+ value: string;
225
+ }[];
226
+ };
227
+ 'calc/rates-err': {
228
+ patch: {
229
+ op: string;
230
+ path: string;
231
+ value: string;
232
+ }[];
233
+ };
234
+ };
235
+ /** All calculator view rules (the calculator UI + the financial form). */
236
+ export declare const calcViewRules: any[];
237
+ export { createRatesLayer, FALLBACK_RATES, CURRENCY_CODES } from './rates/index.js';
238
+ /** The `calc-edit` (backspace) and `calc-eval` (=) JS effect handlers. */
239
+ export declare const calcEditEffects: {
240
+ 'calc-edit': (props: any, dispatch: any) => void;
241
+ 'calc-eval': (props: any, dispatch: any) => void;
242
+ };
243
+ /**
244
+ * The viewModel derivation for the calculator (`contributeCalcViewModel`).
245
+ * Pure: state in, UI document out, no dispatching.
246
+ * @param {any} state
247
+ * @param {{ theme?: any }} [options] plot theme (name, overrides, or
248
+ * `'host'` to follow the embedding host's tokens)
249
+ * @returns {any}
250
+ */
251
+ export declare function contributeCalcViewModel(state: any, options?: {
252
+ theme?: any;
253
+ }): any;
254
+ /**
255
+ * Create the calculator component.
256
+ * @param {import('./rates/index.js').RatesLayerOptions} [options]
257
+ * @returns {any}
258
+ */
259
+ export declare function createCalcComponent(options?: import('./rates/index.js').RatesLayerOptions): any;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @file The Binance rate adapter (alternative provider). Reads
3
+ * `/api/v3/ticker/price` (crypto USDT pairs), no key. Same one-way rule:
4
+ * fetch + normalize only; conversion is core's `convertCurrency`.
5
+ */
6
+ /**
7
+ * Normalize a Binance ticker list into `{ base, rates, at }`. Only
8
+ * `*USDT` pairs are used; the base is USDT (≈ USD, also mapped to USD 1).
9
+ * @param {Array<{symbol:string, price:string|number}>} data @param {number} at
10
+ * @returns {{ base: string, rates: Record<string, number>, at: number }}
11
+ */
12
+ export declare function normalizeBinance(data: Array<{
13
+ symbol: string;
14
+ price: string | number;
15
+ }>, at?: number): {
16
+ base: string;
17
+ rates: Record<string, number>;
18
+ at: number;
19
+ };
20
+ /**
21
+ * Fetch and normalize Binance rates.
22
+ * @param {string[]} codes
23
+ * @param {{ fetch?: typeof globalThis.fetch, endpoint?: string, at?: number }} [opts]
24
+ * @returns {Promise<{ base: string, rates: Record<string, number>, at: number }>}
25
+ */
26
+ export declare function fetchBinance(codes: string[], opts?: {
27
+ fetch?: typeof globalThis.fetch;
28
+ endpoint?: string;
29
+ at?: number;
30
+ }): Promise<{
31
+ base: string;
32
+ rates: Record<string, number>;
33
+ at: number;
34
+ }>;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @file The CoinGecko rate adapter. The **only**
3
+ * network code path for the default provider — it fetches and normalizes,
4
+ * nothing more. The pure conversion is `@jarenjs/core/convert`'s
5
+ * `convertCurrency`, never here. No API key; fiat *and* crypto in one
6
+ * call, everything priced through a USD base. `fetch` is injectable for
7
+ * tests; `endpoint` is overridable.
8
+ */
9
+ /** Known CoinGecko ids ↔ ticker symbols. */
10
+ export declare const COINGECKO_IDS: {
11
+ bitcoin: string;
12
+ ethereum: string;
13
+ tether: string;
14
+ binancecoin: string;
15
+ solana: string;
16
+ cardano: string;
17
+ ripple: string;
18
+ dogecoin: string;
19
+ };
20
+ /**
21
+ * Normalize a `/simple/price` response into the common
22
+ * `{ base, rates, at }` shape. Crypto prices are USD directly; fiat rates
23
+ * are derived through the bitcoin pivot (rate[FIAT] = btc_usd / btc_fiat
24
+ * = value of one FIAT unit in USD).
25
+ * @param {any} data @param {string[]} codes @param {number} at
26
+ * @returns {{ base: string, rates: Record<string, number>, at: number }}
27
+ */
28
+ export declare function normalizeCoinGecko(data: any, codes: string[], at?: number): {
29
+ base: string;
30
+ rates: Record<string, number>;
31
+ at: number;
32
+ };
33
+ /**
34
+ * Fetch and normalize rates for `codes`.
35
+ * @param {string[]} codes
36
+ * @param {{ fetch?: typeof globalThis.fetch, endpoint?: string, at?: number }} [opts]
37
+ * @returns {Promise<{ base: string, rates: Record<string, number>, at: number }>}
38
+ */
39
+ export declare function fetchCoinGecko(codes: string[], opts?: {
40
+ fetch?: typeof globalThis.fetch;
41
+ endpoint?: string;
42
+ at?: number;
43
+ }): Promise<{
44
+ base: string;
45
+ rates: Record<string, number>;
46
+ at: number;
47
+ }>;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * @file The rates layer — the component's impure
3
+ * half. It owns the `rates-fetch` effect and the `rates-poll`
4
+ * subscription, choosing a provider adapter, debouncing to a min refresh
5
+ * interval, and routing success/failure to app actions. It normalizes a
6
+ * rate table into `$.calc.rates`; the **conversion itself** is
7
+ * `@jarenjs/core/convert`'s pure `convertCurrency`, never here.
8
+ *
9
+ * Resilience is the point: a static `FALLBACK_RATES` table keeps the
10
+ * converter, SSR and offline tests working with no network; live rates
11
+ * layer on top only when a fetch succeeds; failures route to an error
12
+ * action.
13
+ */
14
+ export { fetchCoinGecko, normalizeCoinGecko, COINGECKO_IDS } from './coingecko.js';
15
+ export { fetchBinance, normalizeBinance } from './binance.js';
16
+ /** The currency codes the converter offers (fiat + crypto). */
17
+ export declare const CURRENCY_CODES: string[];
18
+ /**
19
+ * The static last-resort table (value of one unit in USD). Deterministic,
20
+ * so tests / SSR / offline all render a real conversion with no network.
21
+ * @type {{ base: string, rates: Record<string, number>, at: number, stale: boolean, status: string }}
22
+ */
23
+ export declare const FALLBACK_RATES: {
24
+ base: string;
25
+ rates: Record<string, number>;
26
+ at: number;
27
+ stale: boolean;
28
+ status: string;
29
+ };
30
+ export type RatesLayerOptions = {
31
+ provider?: 'coingecko' | 'binance' | ((codes: string[], o: any) => Promise<any>);
32
+ /**
33
+ * minimum interval between live fetches (debounce)
34
+ */
35
+ refreshMs?: number;
36
+ fetch?: typeof globalThis.fetch;
37
+ endpoint?: string;
38
+ codes?: string[];
39
+ /**
40
+ * clock (injectable for tests)
41
+ */
42
+ now?: () => number;
43
+ fallbackRates?: any;
44
+ };
45
+ /**
46
+ * @typedef {object} RatesLayerOptions
47
+ * @property {'coingecko'|'binance'|((codes:string[],o:any)=>Promise<any>)} [provider]
48
+ * @property {number} [refreshMs] minimum interval between live fetches (debounce)
49
+ * @property {typeof globalThis.fetch} [fetch]
50
+ * @property {string} [endpoint]
51
+ * @property {string[]} [codes]
52
+ * @property {() => number} [now] clock (injectable for tests)
53
+ * @property {any} [fallbackRates]
54
+ */
55
+ /**
56
+ * Build the rates layer: the `rates-fetch` effect handler, the
57
+ * `rates-poll` subscription handler, the subscription entry and the
58
+ * fallback table.
59
+ * @param {RatesLayerOptions} [options]
60
+ */
61
+ export declare function createRatesLayer(options?: RatesLayerOptions): {
62
+ effects: {
63
+ /** `{ run: 'rates-fetch', with?: { force?: boolean } }`. */
64
+ 'rates-fetch': (props: any, dispatch: any) => void;
65
+ };
66
+ subs: {
67
+ /**
68
+ * `rates-poll`: kicks an immediate fetch and repeats every
69
+ * `refreshMs`; the cleanup stops the timer. The app only starts it
70
+ * while the `when` gate holds (converter + currency).
71
+ */
72
+ 'rates-poll': (props: any, dispatch: any) => () => void;
73
+ };
74
+ subEntry: {
75
+ run: string;
76
+ when: {
77
+ $and: {
78
+ $eq: string[];
79
+ }[];
80
+ };
81
+ };
82
+ fallbackRates: any;
83
+ codes: string[];
84
+ };