exprify 1.0.3 → 1.0.6

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.
@@ -1,8 +1,9 @@
1
1
  export function createFunctionRegistry(initial = {}) {
2
+ // Object.create(null) avoids prototype pollution (no inherited properties)
2
3
  const store = Object.create(null);
3
4
 
4
5
  for (const key in initial) {
5
- if (typeof initial[key] === "function") {
6
+ if (typeof initial[key] === 'function') {
6
7
  store[key] = initial[key];
7
8
  }
8
9
  }
@@ -11,58 +12,64 @@ export function createFunctionRegistry(initial = {}) {
11
12
  getAllFunctionsName() {
12
13
  return Object.keys(store);
13
14
  },
14
- // register new formula
15
+
16
+ /**
17
+ * @param {string} name
18
+ * @param {any} fn
19
+ */
15
20
  register(name, fn) {
16
- if (typeof name !== "string" || !name) {
17
- throw new Error("Formula name must be a non-empty string");
21
+ if (typeof name !== 'string' || !name) {
22
+ throw new Error('Formula name must be a non-empty string');
18
23
  }
19
24
 
20
- if (typeof fn !== "function") {
25
+ if (typeof fn !== 'function') {
21
26
  throw new Error(`Formula "${name}" must be callable`);
22
27
  }
23
28
 
24
29
  store[name] = fn;
25
30
  },
26
31
 
27
- // get formula
32
+ /**
33
+ * @param {string} name
34
+ */
28
35
  get(name) {
29
36
  return store[name];
30
37
  },
31
38
 
32
- // check existence
39
+ /**
40
+ * @param {any} name
41
+ */
33
42
  has(name) {
34
43
  return Object.prototype.hasOwnProperty.call(store, name);
35
44
  },
36
45
 
37
- // remove formula
46
+ /**
47
+ * @param {string | number} name
48
+ */
38
49
  remove(name) {
39
50
  delete store[name];
40
51
  },
41
52
 
42
- // list all
43
53
  all() {
44
54
  return { ...store };
45
55
  },
46
56
 
47
- // clear registry
48
57
  clear() {
49
58
  for (const key in store) {
50
59
  delete store[key];
51
60
  }
52
61
  },
53
62
 
54
- // extend multiple
55
63
  extend(formulas = {}) {
56
64
  for (const name in formulas) {
57
- if (typeof formulas[name] === "function") {
65
+ if (typeof formulas[name] === 'function') {
58
66
  store[name] = formulas[name];
59
67
  }
60
68
  }
61
69
  },
62
70
 
63
- // clone (for scoped instances)
64
71
  clone() {
65
- return createFormulaRegistry(store);
66
- }
72
+ return createFunctionRegistry(store);
73
+ },
67
74
  };
68
- }
75
+ }
package/src/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import Exprify from "./core/Exprify.js";
1
+ import Exprify from './core/exprify.js';
2
2
  export default Exprify;
@@ -0,0 +1,31 @@
1
+ import { ExprDecimal } from '../utils/decimal.js';
2
+
3
+ /**
4
+ * @param {any} value
5
+ */
6
+ export function bigNumber(value) {
7
+ if (ExprDecimal.isDecimal(value)) {
8
+ return value;
9
+ }
10
+ if (typeof value === 'number' || typeof value === 'string' || typeof value === 'bigint') {
11
+ return new ExprDecimal(value);
12
+ }
13
+ throw new Error('bignumber() expects a number, string, or bigint');
14
+ }
15
+
16
+ /**
17
+ * @param {unknown} v
18
+ */
19
+ export function isBigNumber(v) {
20
+ return ExprDecimal.isDecimal(v);
21
+ }
22
+
23
+ /**
24
+ * @param {ExprDecimal} v
25
+ */
26
+ export function formatBigNumber(v) {
27
+ if (!ExprDecimal.isDecimal(v)) {
28
+ return String(v);
29
+ }
30
+ return v.toString();
31
+ }
@@ -0,0 +1,112 @@
1
+ const gcd = (/** @type {number} */ a, /** @type {number} */ b) => {
2
+ a = Math.abs(a);
3
+ b = Math.abs(b);
4
+ while (b) {
5
+ [a, b] = [b, a % b];
6
+ }
7
+ return a;
8
+ };
9
+
10
+ /**
11
+ * @param {any} n
12
+ */
13
+ export function fraction(n, d = 1) {
14
+ if (typeof n !== 'number' || typeof d !== 'number') {
15
+ throw new Error('Fraction requires numeric arguments');
16
+ }
17
+ if (!Number.isInteger(n) || !Number.isInteger(d)) {
18
+ throw new Error('Fraction requires integer arguments');
19
+ }
20
+ if (d === 0) {
21
+ throw new Error('Fraction denominator cannot be zero');
22
+ }
23
+ if (d < 0) {
24
+ n = -n;
25
+ d = -d;
26
+ }
27
+ const g = gcd(n, d);
28
+ return { n: n / g, d: d / g };
29
+ }
30
+
31
+ /**
32
+ * @param {any} v
33
+ */
34
+ export function isFraction(v) {
35
+ return v && typeof v === 'object' && 'n' in v && 'd' in v && !('re' in v) && !('unit' in v);
36
+ }
37
+
38
+ /**
39
+ * @param {{ n: number; d: number; }} a
40
+ * @param {{ d: number; n: number; }} b
41
+ */
42
+ export function addFrac(a, b) {
43
+ return fraction(a.n * b.d + b.n * a.d, a.d * b.d);
44
+ }
45
+
46
+ /**
47
+ * @param {{ n: number; d: number; }} a
48
+ * @param {{ d: number; n: number; }} b
49
+ */
50
+ export function subFrac(a, b) {
51
+ return fraction(a.n * b.d - b.n * a.d, a.d * b.d);
52
+ }
53
+
54
+ /**
55
+ * @param {{ n: number; d: number; }} a
56
+ * @param {{ n: number; d: number; }} b
57
+ */
58
+ export function mulFrac(a, b) {
59
+ return fraction(a.n * b.n, a.d * b.d);
60
+ }
61
+
62
+ /**
63
+ * @param {{ n: number; d: number; }} a
64
+ * @param {{ d: number; n: number; }} b
65
+ */
66
+ export function divFrac(a, b) {
67
+ return fraction(a.n * b.d, a.d * b.n);
68
+ }
69
+
70
+ /**
71
+ * @param {{ n: number; d: number; }} a
72
+ * @param {any} exp
73
+ */
74
+ export function powFrac(a, exp) {
75
+ if (!Number.isInteger(exp) || exp < 0) {
76
+ return null;
77
+ }
78
+ return fraction(a.n ** exp, a.d ** exp);
79
+ }
80
+
81
+ /**
82
+ * @param {{ n: any; }} v
83
+ */
84
+ export function numer(v) {
85
+ if (!isFraction(v)) {
86
+ throw new Error('numer() expects a fraction');
87
+ }
88
+ return v.n;
89
+ }
90
+
91
+ /**
92
+ * @param {{ d: any; }} v
93
+ */
94
+ export function denom(v) {
95
+ if (!isFraction(v)) {
96
+ throw new Error('denom() expects a fraction');
97
+ }
98
+ return v.d;
99
+ }
100
+
101
+ /**
102
+ * @param {{ d: number; n: any; }} v
103
+ */
104
+ export function formatFraction(v) {
105
+ if (!isFraction(v)) {
106
+ return String(v);
107
+ }
108
+ if (v.d === 1) {
109
+ return String(v.n);
110
+ }
111
+ return `${v.n}/${v.d}`;
112
+ }
@@ -1,38 +1,52 @@
1
- const isValidNumberPair = (a, b) =>
2
- (typeof a === typeof b) &&
3
- (typeof a === 'number' || typeof a === 'bigint');
1
+ const isValidNumberPair = (/** @type {any} */ a, /** @type {any} */ b) =>
2
+ typeof a === typeof b && (typeof a === 'number' || typeof a === 'bigint');
4
3
 
5
4
  export const mathOperations = Object.freeze({
6
- power: function(a, b) {
7
- if (isValidNumberPair(a, b)) return a ** b;
8
- throw new Error("Invalid types for ^");
5
+ power: function (/** @type {number} */ a, /** @type {number} */ b) {
6
+ if (isValidNumberPair(a, b)) {
7
+ return a ** b;
8
+ }
9
+ throw new Error('Invalid types for ^');
9
10
  },
10
11
 
11
- multiply: function(a, b) {
12
- if (isValidNumberPair(a, b)) return a * b;
13
- throw new Error("Invalid types for *");
12
+ multiply: function (/** @type {number} */ a, /** @type {number} */ b) {
13
+ if (isValidNumberPair(a, b)) {
14
+ return a * b;
15
+ }
16
+ throw new Error('Invalid types for *');
14
17
  },
15
18
 
16
- divide: function(a, b) {
19
+ divide: function (/** @type {number} */ a, /** @type {number} */ b) {
17
20
  if (isValidNumberPair(a, b)) {
18
- if (b === 0) throw new Error("Division by zero");
21
+ if (b === 0) {
22
+ throw new Error('Division by zero');
23
+ }
19
24
  return a / b;
20
25
  }
21
- throw new Error("Invalid types for /");
26
+ throw new Error('Invalid types for /');
22
27
  },
23
28
 
24
- add: function(a, b) {
25
- if (isValidNumberPair(a, b)) return a + b;
26
- if (typeof a === 'string' && typeof b === 'string') return a + b;
27
- throw new Error("Invalid types for +");
29
+ add: function (/** @type {string} */ a, /** @type {string} */ b) {
30
+ if (isValidNumberPair(a, b)) {
31
+ return a + b;
32
+ }
33
+ if (typeof a === 'string' && typeof b === 'string') {
34
+ return a + b;
35
+ }
36
+ throw new Error('Invalid types for +');
28
37
  },
29
- subtract: function(a, b) {
30
- if (isValidNumberPair(a, b)) return a - b;
31
- throw new Error("Invalid types for -");
38
+
39
+ subtract: function (/** @type {number} */ a, /** @type {number} */ b) {
40
+ if (isValidNumberPair(a, b)) {
41
+ return a - b;
42
+ }
43
+ throw new Error('Invalid types for -');
32
44
  },
33
45
 
34
- modulus: function(a, b) {
35
- if (isValidNumberPair(a, b)) return a % b;
36
- throw new Error("Invalid types for %");
37
- }
38
- });
46
+ modulus: function (/** @type {number} */ a, /** @type {number} */ b) {
47
+ if (isValidNumberPair(a, b)) {
48
+ return a % b;
49
+ }
50
+ throw new Error('Invalid types for %');
51
+ },
52
+ });