fable 3.1.61 → 3.1.63

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/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "fable",
3
- "version": "3.1.61",
3
+ "version": "3.1.63",
4
4
  "description": "A service dependency injection, configuration and logging library.",
5
5
  "main": "source/Fable.js",
6
6
  "scripts": {
7
7
  "start": "node source/Fable.js",
8
- "coverage": "./node_modules/.bin/nyc --reporter=lcov --reporter=text-lcov ./node_modules/mocha/bin/_mocha -- -u tdd -R spec",
9
- "test": "./node_modules/.bin/mocha -u tdd -R spec",
8
+ "coverage": "npx quack coverage",
9
+ "test": "npx quack test",
10
10
  "build": "npx quack build",
11
11
  "docker-dev-build": "docker build ./ -f Dockerfile_LUXURYCode -t fable-image:local",
12
12
  "docker-dev-run": "docker run -it -d --name fable-dev -p 30001:8080 -p 38086:8086 -v \"$PWD/.config:/home/coder/.config\" -v \"$PWD:/home/coder/fable\" -u \"$(id -u):$(id -g)\" -e \"DOCKER_USER=$USER\" fable-image:local",
13
13
  "docker-dev-shell": "docker exec -it fable-dev /bin/bash",
14
- "tests": "./node_modules/mocha/bin/_mocha -u tdd --exit -R spec --grep"
14
+ "tests": "npx quack test -g"
15
15
  },
16
16
  "mocha": {
17
17
  "diff": true,
@@ -50,21 +50,21 @@
50
50
  },
51
51
  "homepage": "https://github.com/stevenvelozo/fable",
52
52
  "devDependencies": {
53
- "quackage": "^1.0.56"
53
+ "quackage": "^1.0.58"
54
54
  },
55
55
  "dependencies": {
56
56
  "async.eachlimit": "^0.5.2",
57
57
  "async.waterfall": "^0.5.2",
58
58
  "big.js": "^7.0.1",
59
- "cachetrax": "^1.0.5",
59
+ "cachetrax": "^1.0.6",
60
60
  "cookie": "^1.1.1",
61
61
  "data-arithmatic": "^1.0.7",
62
62
  "dayjs": "^1.11.19",
63
- "fable-log": "^3.0.17",
64
- "fable-serviceproviderbase": "^3.0.18",
65
- "fable-settings": "^3.0.15",
66
- "fable-uuid": "^3.0.12",
67
- "manyfest": "^1.0.47",
63
+ "fable-log": "^3.0.18",
64
+ "fable-serviceproviderbase": "^3.0.19",
65
+ "fable-settings": "^3.0.16",
66
+ "fable-uuid": "^3.0.13",
67
+ "manyfest": "^1.0.48",
68
68
  "simple-get": "^4.0.1"
69
69
  }
70
70
  }
@@ -1864,24 +1864,95 @@ class FableServiceMath extends libFableServiceBase
1864
1864
  }, pRegressionCoefficients[0]);
1865
1865
  }
1866
1866
 
1867
+ /**
1868
+ * Resolves paired Y/X value arrays from flexible argument patterns.
1869
+ *
1870
+ * The expression parser's SetConcatArray spreads comma-separated arguments
1871
+ * as individual function parameters. This helper normalizes three calling
1872
+ * conventions into a clean paired-value result:
1873
+ *
1874
+ * 1. Two arrays: SLOPE(yArray, xArray)
1875
+ * 2. Spread scalars: SLOPE(y1,y2,...,yN, x1,x2,...,xN) — split in half
1876
+ * 3. One flat array: SLOPE([y1,...,yN,x1,...,xN]) — split in half
1877
+ *
1878
+ * Non-numeric values are filtered; only pairs where both Y and X parse
1879
+ * are kept.
1880
+ *
1881
+ * @param {...*} pArguments - Flexible: (yArr, xArr) | (y1,y2,...,x1,x2,...) | (flatArr)
1882
+ *
1883
+ * @return {Object} { cleanX, cleanY, n }
1884
+ */
1885
+ cleanPairedValues(...pArguments)
1886
+ {
1887
+ let tmpYValues;
1888
+ let tmpXValues;
1889
+
1890
+ if (pArguments.length === 2 && Array.isArray(pArguments[0]) && Array.isArray(pArguments[1]))
1891
+ {
1892
+ // Two arrays: SLOPE(yArray, xArray)
1893
+ tmpYValues = pArguments[0];
1894
+ tmpXValues = pArguments[1];
1895
+ }
1896
+ else if (pArguments.length === 1 && Array.isArray(pArguments[0]))
1897
+ {
1898
+ // Single flat array — split in half
1899
+ let tmpFlat = pArguments[0];
1900
+ let tmpHalf = Math.floor(tmpFlat.length / 2);
1901
+ tmpYValues = tmpFlat.slice(0, tmpHalf);
1902
+ tmpXValues = tmpFlat.slice(tmpHalf);
1903
+ }
1904
+ else if (pArguments.length > 2)
1905
+ {
1906
+ // Many spread scalars from expression parser — split in half
1907
+ let tmpHalf = Math.floor(pArguments.length / 2);
1908
+ tmpYValues = pArguments.slice(0, tmpHalf);
1909
+ tmpXValues = pArguments.slice(tmpHalf);
1910
+ }
1911
+ else
1912
+ {
1913
+ // Two scalars or other degenerate input
1914
+ tmpYValues = Array.isArray(pArguments[0]) ? pArguments[0] : [pArguments[0]];
1915
+ tmpXValues = Array.isArray(pArguments[1]) ? pArguments[1] : [pArguments[1]];
1916
+ }
1917
+
1918
+ let tmpPairCount = Math.min(tmpYValues.length, tmpXValues.length);
1919
+
1920
+ let tmpCleanX = [];
1921
+ let tmpCleanY = [];
1922
+
1923
+ for (let i = 0; i < tmpPairCount; i++)
1924
+ {
1925
+ let tmpX = this.parsePrecise(tmpXValues[i], NaN);
1926
+ let tmpY = this.parsePrecise(tmpYValues[i], NaN);
1927
+ if (!isNaN(tmpX) && !isNaN(tmpY))
1928
+ {
1929
+ tmpCleanX.push(tmpX);
1930
+ tmpCleanY.push(tmpY);
1931
+ }
1932
+ }
1933
+
1934
+ return { cleanX: tmpCleanX, cleanY: tmpCleanY, n: tmpCleanX.length };
1935
+ }
1936
+
1867
1937
  /**
1868
1938
  * Calculates the slope of a linear regression line through paired data points.
1869
1939
  * Equivalent to Excel's SLOPE function.
1870
1940
  *
1871
1941
  * Formula: slope = (n * Σ(xy) - Σx * Σy) / (n * Σ(x²) - (Σx)²)
1872
1942
  *
1873
- * @param {Array<number|string>} pYValues - The dependent data points (known y's).
1874
- * @param {Array<number|string>} pXValues - The independent data points (known x's).
1943
+ * Calling conventions (all work through the expression parser):
1944
+ * SLOPE(yArray, xArray) — two resolved arrays
1945
+ * SLOPE(y1,y2,...,yN, x1,x2,...,xN) — inline scalars, split in half
1946
+ *
1947
+ * @param {...*} pArguments - Y values followed by X values (see cleanPairedValues).
1875
1948
  *
1876
1949
  * @return {string} The slope of the regression line.
1877
1950
  */
1878
- slopePrecise(pYValues, pXValues)
1951
+ slopePrecise(...pArguments)
1879
1952
  {
1880
- let tmpYValues = Array.isArray(pYValues) ? pYValues : [pYValues];
1881
- let tmpXValues = Array.isArray(pXValues) ? pXValues : [pXValues];
1882
- let tmpN = Math.min(tmpYValues.length, tmpXValues.length);
1953
+ let tmpPaired = this.cleanPairedValues(...pArguments);
1883
1954
 
1884
- if (tmpN < 2)
1955
+ if (tmpPaired.n < 2)
1885
1956
  {
1886
1957
  return '0';
1887
1958
  }
@@ -1891,23 +1962,17 @@ class FableServiceMath extends libFableServiceBase
1891
1962
  let tmpSumXY = '0';
1892
1963
  let tmpSumX2 = '0';
1893
1964
 
1894
- for (let i = 0; i < tmpN; i++)
1965
+ for (let i = 0; i < tmpPaired.n; i++)
1895
1966
  {
1896
- let tmpX = this.parsePrecise(tmpXValues[i], NaN);
1897
- let tmpY = this.parsePrecise(tmpYValues[i], NaN);
1898
- if (isNaN(tmpX) || isNaN(tmpY))
1899
- {
1900
- continue;
1901
- }
1902
- tmpSumX = this.addPrecise(tmpSumX, tmpX);
1903
- tmpSumY = this.addPrecise(tmpSumY, tmpY);
1904
- tmpSumXY = this.addPrecise(tmpSumXY, this.multiplyPrecise(tmpX, tmpY));
1905
- tmpSumX2 = this.addPrecise(tmpSumX2, this.multiplyPrecise(tmpX, tmpX));
1967
+ tmpSumX = this.addPrecise(tmpSumX, tmpPaired.cleanX[i]);
1968
+ tmpSumY = this.addPrecise(tmpSumY, tmpPaired.cleanY[i]);
1969
+ tmpSumXY = this.addPrecise(tmpSumXY, this.multiplyPrecise(tmpPaired.cleanX[i], tmpPaired.cleanY[i]));
1970
+ tmpSumX2 = this.addPrecise(tmpSumX2, this.multiplyPrecise(tmpPaired.cleanX[i], tmpPaired.cleanX[i]));
1906
1971
  }
1907
1972
 
1908
1973
  // slope = (n * Σ(xy) - Σx * Σy) / (n * Σ(x²) - (Σx)²)
1909
- let tmpNumerator = this.subtractPrecise(this.multiplyPrecise(tmpN, tmpSumXY), this.multiplyPrecise(tmpSumX, tmpSumY));
1910
- let tmpDenominator = this.subtractPrecise(this.multiplyPrecise(tmpN, tmpSumX2), this.multiplyPrecise(tmpSumX, tmpSumX));
1974
+ let tmpNumerator = this.subtractPrecise(this.multiplyPrecise(tmpPaired.n, tmpSumXY), this.multiplyPrecise(tmpSumX, tmpSumY));
1975
+ let tmpDenominator = this.subtractPrecise(this.multiplyPrecise(tmpPaired.n, tmpSumX2), this.multiplyPrecise(tmpSumX, tmpSumX));
1911
1976
 
1912
1977
  if (this.comparePrecise(tmpDenominator, 0) == 0)
1913
1978
  {
@@ -1923,19 +1988,21 @@ class FableServiceMath extends libFableServiceBase
1923
1988
  *
1924
1989
  * Formula: intercept = ȳ - slope * x̄
1925
1990
  *
1926
- * @param {Array<number|string>} pYValues - The dependent data points (known y's).
1927
- * @param {Array<number|string>} pXValues - The independent data points (known x's).
1991
+ * Calling conventions (all work through the expression parser):
1992
+ * INTERCEPT(yArray, xArray) — two resolved arrays
1993
+ * INTERCEPT(y1,y2,...,yN, x1,x2,...,xN) — inline scalars, split in half
1994
+ *
1995
+ * @param {...*} pArguments - Y values followed by X values (see cleanPairedValues).
1928
1996
  *
1929
1997
  * @return {string} The y-intercept of the regression line.
1930
1998
  */
1931
- interceptPrecise(pYValues, pXValues)
1999
+ interceptPrecise(...pArguments)
1932
2000
  {
1933
- let tmpYValues = Array.isArray(pYValues) ? pYValues : [pYValues];
1934
- let tmpXValues = Array.isArray(pXValues) ? pXValues : [pXValues];
2001
+ let tmpPaired = this.cleanPairedValues(...pArguments);
1935
2002
 
1936
- let tmpSlope = this.slopePrecise(tmpYValues, tmpXValues);
1937
- let tmpMeanY = this.meanPrecise(tmpYValues);
1938
- let tmpMeanX = this.meanPrecise(tmpXValues);
2003
+ let tmpSlope = this.slopePrecise(tmpPaired.cleanY, tmpPaired.cleanX);
2004
+ let tmpMeanY = this.meanPrecise(tmpPaired.cleanY);
2005
+ let tmpMeanX = this.meanPrecise(tmpPaired.cleanX);
1939
2006
 
1940
2007
  // intercept = ȳ - slope * x̄
1941
2008
  return this.subtractPrecise(tmpMeanY, this.multiplyPrecise(tmpSlope, tmpMeanX));
@@ -1336,10 +1336,38 @@ suite
1336
1336
  _Parser.solve('InterceptResult = INTERCEPT(AppData.YData, AppData.XData)', testFable, tmpSolverResult, false, tmpSolverResult);
1337
1337
  Expect(tmpSolverResult.InterceptResult).to.equal('1');
1338
1338
 
1339
- // Solver with inline sets
1339
+ // Solver with inline scalars (split in half: first 5 = Y, last 5 = X)
1340
1340
  let tmpInlineResult = {};
1341
1341
  _Parser.solve('InlineSlope = SLOPE(3,5,7,9,11, 1,2,3,4,5)', testFable, tmpInlineResult, false, tmpInlineResult);
1342
- testFable.log.info('Inline slope result:', tmpInlineResult.InlineSlope);
1342
+ Expect(tmpInlineResult.InlineSlope).to.equal('2');
1343
+
1344
+ _Parser.solve('InlineIntercept = INTERCEPT(3,5,7,9,11, 1,2,3,4,5)', testFable, tmpInlineResult, false, tmpInlineResult);
1345
+ Expect(tmpInlineResult.InlineIntercept).to.equal('1');
1346
+
1347
+ // Solver chain: SERIES produces string arrays, then SLOPE/INTERCEPT consume them
1348
+ let tmpChainData = {};
1349
+ _Parser.solve('XValues = SERIES FROM 1 TO 5 STEP 1 : n + 0', tmpChainData, {}, false, tmpChainData);
1350
+ _Parser.solve('YValues = SERIES FROM 1 TO 5 STEP 1 : 2 * n + 1', tmpChainData, {}, false, tmpChainData);
1351
+ // Verify the arrays are string-typed (expression parser produces strings)
1352
+ Expect(tmpChainData.XValues[0]).to.equal('1');
1353
+ Expect(typeof tmpChainData.XValues[0]).to.equal('string');
1354
+ Expect(tmpChainData.YValues[0]).to.equal('3');
1355
+ Expect(typeof tmpChainData.YValues[0]).to.equal('string');
1356
+
1357
+ _Parser.solve('ChainSlope = SLOPE(YValues, XValues)', tmpChainData, {}, false, tmpChainData);
1358
+ Expect(tmpChainData.ChainSlope).to.equal('2');
1359
+
1360
+ _Parser.solve('ChainIntercept = INTERCEPT(YValues, XValues)', tmpChainData, {}, false, tmpChainData);
1361
+ Expect(tmpChainData.ChainIntercept).to.equal('1');
1362
+
1363
+ // String arrays at AppData addresses
1364
+ testFable.AppData.XStrings = ['1', '2', '3', '4', '5'];
1365
+ testFable.AppData.YStrings = ['3', '5', '7', '9', '11'];
1366
+ _Parser.solve('StringSlope = SLOPE(AppData.YStrings, AppData.XStrings)', testFable, tmpSolverResult, false, tmpSolverResult);
1367
+ Expect(tmpSolverResult.StringSlope).to.equal('2');
1368
+
1369
+ _Parser.solve('StringIntercept = INTERCEPT(AppData.YStrings, AppData.XStrings)', testFable, tmpSolverResult, false, tmpSolverResult);
1370
+ Expect(tmpSolverResult.StringIntercept).to.equal('1');
1343
1371
 
1344
1372
  // Solver with non-perfect data
1345
1373
  testFable.AppData.YData2 = [2, 4, 5, 4, 5];
@@ -1350,12 +1378,8 @@ suite
1350
1378
  Expect(Number(tmpSolverResult.Intercept2)).to.be.closeTo(2.2, 0.0001);
1351
1379
 
1352
1380
  // Verify slope and intercept reconstruct the regression line
1353
- // y_predicted = intercept + slope * x
1354
- for (let i = 0; i < tmpXValues2.length; i++)
1355
- {
1356
- let tmpPredicted = testFable.Math.addPrecise(tmpSolverResult.Intercept2, testFable.Math.multiplyPrecise(tmpSolverResult.Slope2, tmpXValues2[i]));
1357
- testFable.log.info(`x=${tmpXValues2[i]}, y=${tmpYValues2[i]}, predicted=${tmpPredicted}`);
1358
- }
1381
+ _Parser.solve('Predicted = ChainIntercept + ChainSlope * 6', tmpChainData, {}, false, tmpChainData);
1382
+ Expect(tmpChainData.Predicted).to.equal('13');
1359
1383
  }
1360
1384
  );
1361
1385
  }
package/.babelrc DELETED
@@ -1,3 +0,0 @@
1
- {
2
- "presets": ["@babel/preset-env"]
3
- }
package/.browserslistrc DELETED
@@ -1 +0,0 @@
1
- since 2020