@fileverse-dev/formulajs 4.4.15 → 4.4.17-fix-sort-fn-1

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/lib/cjs/index.cjs CHANGED
@@ -1271,47 +1271,73 @@ function ROWS(array) {
1271
1271
  * @returns
1272
1272
  */
1273
1273
  function SORT(array, sort_index = 1, sort_order = 1, by_col = false) {
1274
- if (!array || !Array.isArray(array)) {
1275
- return na
1276
- }
1274
+ if (!array || !Array.isArray(array)) return na;
1275
+ if (array.length === 0) return 0;
1277
1276
 
1278
- if (array.length === 0) {
1279
- return 0
1280
- }
1277
+ let idx = parseNumber(sort_index);
1278
+ if (!idx || idx < 1) return value;
1279
+ idx = idx - 1;
1281
1280
 
1282
- sort_index = parseNumber(sort_index);
1283
- if (!sort_index || sort_index < 1) {
1284
- return value
1285
- }
1281
+ let order = parseNumber(sort_order);
1282
+ if (order !== 1 && order !== -1) return value;
1286
1283
 
1287
- sort_order = parseNumber(sort_order);
1288
- if (sort_order !== 1 && sort_order !== -1) {
1289
- return value
1290
- }
1284
+ const byCol = parseBool(by_col);
1285
+ if (typeof byCol !== "boolean") return name;
1286
+
1287
+ // Rectangularize then orient
1288
+ const matrix = fillMatrix(array);
1289
+ const working = byCol ? transpose(matrix) : matrix;
1291
1290
 
1292
- by_col = parseBool(by_col);
1293
- if (typeof by_col !== 'boolean') {
1294
- return name
1291
+ if (!working.length || !working[0] || idx >= working[0].length) {
1292
+ return value; // out of bounds
1295
1293
  }
1296
1294
 
1297
- const sortArray = (arr) =>
1298
- arr.sort((a, b) => {
1299
- a = parseString(a[sort_index - 1]);
1300
- b = parseString(b[sort_index - 1]);
1295
+ // Helpers (aiming for spreadsheet-ish behavior)
1296
+ const isBlank = (v) => v === "" || v === null || v === undefined;
1301
1297
 
1302
- return sort_order === 1 ? (a < b ? sort_order * -1 : sort_order) : a > b ? sort_order : sort_order * -1
1303
- });
1298
+ const cmp = (a, b) => {
1299
+ // Blanks last (ascending); we'll multiply by order later for descending.
1300
+ const aBlank = isBlank(a);
1301
+ const bBlank = isBlank(b);
1302
+ if (aBlank && bBlank) return 0;
1303
+ if (aBlank) return 1;
1304
+ if (bBlank) return -1;
1304
1305
 
1305
- const matrix = fillMatrix(array);
1306
- const result = by_col ? transpose(matrix) : matrix;
1306
+ // If both parse as finite numbers, compare numerically
1307
+ const na = parseNumber(a);
1308
+ const nb = parseNumber(b);
1309
+ const aNum = Number.isFinite(na);
1310
+ const bNum = Number.isFinite(nb);
1311
+
1312
+ if (aNum && bNum) {
1313
+ if (na < nb) return -1;
1314
+ if (na > nb) return 1;
1315
+ return 0;
1316
+ }
1307
1317
 
1308
- return sort_index >= 1 && sort_index <= result[0].length
1309
- ? by_col
1310
- ? transpose(sortArray(result))
1311
- : sortArray(result)
1312
- : value
1318
+ // Fallback: case-insensitive string compare
1319
+ const sa = (parseString(a) ?? "").toString().toLowerCase();
1320
+ const sb = (parseString(b) ?? "").toString().toLowerCase();
1321
+ if (sa < sb) return -1;
1322
+ if (sa > sb) return 1;
1323
+ return 0;
1324
+ };
1325
+
1326
+ // Stable sort: decorate with original index
1327
+ const decorated = working.map((row, i) => ({ row, i }));
1328
+
1329
+ decorated.sort((A, B) => {
1330
+ const base = cmp(A.row[idx], B.row[idx]);
1331
+ if (base !== 0) return base * order;
1332
+ // stable tiebreaker: original order
1333
+ return A.i - B.i;
1334
+ });
1335
+
1336
+ const sorted = decorated.map((d) => d.row);
1337
+ return byCol ? transpose(sorted) : sorted;
1313
1338
  }
1314
1339
 
1340
+
1315
1341
  /**
1316
1342
  * Returns the transpose of an array.
1317
1343
  *
@@ -17870,7 +17896,7 @@ async function COINGECKO() {
17870
17896
  const out = {};
17871
17897
  for (const [token, prices] of Object.entries(json))
17872
17898
  for (const [cur, val] of Object.entries(prices))
17873
- out[`${token.charAt(0).toUpperCase() + token.slice(1)}_${cur.toUpperCase()}`] = val;
17899
+ out[`${token.toUpperCase()}_${cur.toUpperCase()}`] = val;
17874
17900
  return [out]
17875
17901
  }
17876
17902
 
@@ -18586,17 +18612,22 @@ const SUPPORTED_TOKEN_NAMES = {
18586
18612
 
18587
18613
 
18588
18614
  function formatNumber(raw, decimals) {
18589
- if(!decimals){
18615
+ try {
18616
+ if(!decimals){
18617
+ return raw
18618
+ }
18619
+ const quorum = BigInt(raw);
18620
+ const divisor = 10 ** decimals;
18621
+ const normalized = Number(quorum) / divisor;
18622
+
18623
+ return new Intl.NumberFormat("en-US", {
18624
+ notation: "compact",
18625
+ maximumFractionDigits: 2,
18626
+ }).format(normalized);
18627
+ } catch (error) {
18628
+ console.log({error});
18590
18629
  return raw
18591
18630
  }
18592
- const quorum = BigInt(raw);
18593
- const divisor = 10 ** decimals;
18594
- const normalized = Number(quorum) / divisor;
18595
-
18596
- return new Intl.NumberFormat("en-US", {
18597
- notation: "compact",
18598
- maximumFractionDigits: 2,
18599
- }).format(normalized);
18600
18631
  }
18601
18632
 
18602
18633
  let cachedChains = null;
@@ -18667,7 +18698,13 @@ async function DUNE() {
18667
18698
  });
18668
18699
 
18669
18700
  const res = await fetch(finalUrl, { method: "GET", headers: HEADERS });
18670
- if (!res.ok) throw new NetworkError(SERVICES_API_KEY.DuneSim, res.status);
18701
+ if (!res.ok) {
18702
+ if(res.status === 400){
18703
+ const data = await res.json();
18704
+ throw new ValidationError(data.message)
18705
+ }
18706
+ throw new NetworkError(SERVICES_API_KEY.DuneSim, res.status);
18707
+ }
18671
18708
 
18672
18709
  const json = await res.json();
18673
18710
  const data =
package/lib/esm/index.mjs CHANGED
@@ -1269,47 +1269,73 @@ function ROWS(array) {
1269
1269
  * @returns
1270
1270
  */
1271
1271
  function SORT(array, sort_index = 1, sort_order = 1, by_col = false) {
1272
- if (!array || !Array.isArray(array)) {
1273
- return na
1274
- }
1272
+ if (!array || !Array.isArray(array)) return na;
1273
+ if (array.length === 0) return 0;
1275
1274
 
1276
- if (array.length === 0) {
1277
- return 0
1278
- }
1275
+ let idx = parseNumber(sort_index);
1276
+ if (!idx || idx < 1) return value;
1277
+ idx = idx - 1;
1279
1278
 
1280
- sort_index = parseNumber(sort_index);
1281
- if (!sort_index || sort_index < 1) {
1282
- return value
1283
- }
1279
+ let order = parseNumber(sort_order);
1280
+ if (order !== 1 && order !== -1) return value;
1284
1281
 
1285
- sort_order = parseNumber(sort_order);
1286
- if (sort_order !== 1 && sort_order !== -1) {
1287
- return value
1288
- }
1282
+ const byCol = parseBool(by_col);
1283
+ if (typeof byCol !== "boolean") return name;
1284
+
1285
+ // Rectangularize then orient
1286
+ const matrix = fillMatrix(array);
1287
+ const working = byCol ? transpose(matrix) : matrix;
1289
1288
 
1290
- by_col = parseBool(by_col);
1291
- if (typeof by_col !== 'boolean') {
1292
- return name
1289
+ if (!working.length || !working[0] || idx >= working[0].length) {
1290
+ return value; // out of bounds
1293
1291
  }
1294
1292
 
1295
- const sortArray = (arr) =>
1296
- arr.sort((a, b) => {
1297
- a = parseString(a[sort_index - 1]);
1298
- b = parseString(b[sort_index - 1]);
1293
+ // Helpers (aiming for spreadsheet-ish behavior)
1294
+ const isBlank = (v) => v === "" || v === null || v === undefined;
1299
1295
 
1300
- return sort_order === 1 ? (a < b ? sort_order * -1 : sort_order) : a > b ? sort_order : sort_order * -1
1301
- });
1296
+ const cmp = (a, b) => {
1297
+ // Blanks last (ascending); we'll multiply by order later for descending.
1298
+ const aBlank = isBlank(a);
1299
+ const bBlank = isBlank(b);
1300
+ if (aBlank && bBlank) return 0;
1301
+ if (aBlank) return 1;
1302
+ if (bBlank) return -1;
1302
1303
 
1303
- const matrix = fillMatrix(array);
1304
- const result = by_col ? transpose(matrix) : matrix;
1304
+ // If both parse as finite numbers, compare numerically
1305
+ const na = parseNumber(a);
1306
+ const nb = parseNumber(b);
1307
+ const aNum = Number.isFinite(na);
1308
+ const bNum = Number.isFinite(nb);
1309
+
1310
+ if (aNum && bNum) {
1311
+ if (na < nb) return -1;
1312
+ if (na > nb) return 1;
1313
+ return 0;
1314
+ }
1305
1315
 
1306
- return sort_index >= 1 && sort_index <= result[0].length
1307
- ? by_col
1308
- ? transpose(sortArray(result))
1309
- : sortArray(result)
1310
- : value
1316
+ // Fallback: case-insensitive string compare
1317
+ const sa = (parseString(a) ?? "").toString().toLowerCase();
1318
+ const sb = (parseString(b) ?? "").toString().toLowerCase();
1319
+ if (sa < sb) return -1;
1320
+ if (sa > sb) return 1;
1321
+ return 0;
1322
+ };
1323
+
1324
+ // Stable sort: decorate with original index
1325
+ const decorated = working.map((row, i) => ({ row, i }));
1326
+
1327
+ decorated.sort((A, B) => {
1328
+ const base = cmp(A.row[idx], B.row[idx]);
1329
+ if (base !== 0) return base * order;
1330
+ // stable tiebreaker: original order
1331
+ return A.i - B.i;
1332
+ });
1333
+
1334
+ const sorted = decorated.map((d) => d.row);
1335
+ return byCol ? transpose(sorted) : sorted;
1311
1336
  }
1312
1337
 
1338
+
1313
1339
  /**
1314
1340
  * Returns the transpose of an array.
1315
1341
  *
@@ -17868,7 +17894,7 @@ async function COINGECKO() {
17868
17894
  const out = {};
17869
17895
  for (const [token, prices] of Object.entries(json))
17870
17896
  for (const [cur, val] of Object.entries(prices))
17871
- out[`${token.charAt(0).toUpperCase() + token.slice(1)}_${cur.toUpperCase()}`] = val;
17897
+ out[`${token.toUpperCase()}_${cur.toUpperCase()}`] = val;
17872
17898
  return [out]
17873
17899
  }
17874
17900
 
@@ -18584,17 +18610,22 @@ const SUPPORTED_TOKEN_NAMES = {
18584
18610
 
18585
18611
 
18586
18612
  function formatNumber(raw, decimals) {
18587
- if(!decimals){
18613
+ try {
18614
+ if(!decimals){
18615
+ return raw
18616
+ }
18617
+ const quorum = BigInt(raw);
18618
+ const divisor = 10 ** decimals;
18619
+ const normalized = Number(quorum) / divisor;
18620
+
18621
+ return new Intl.NumberFormat("en-US", {
18622
+ notation: "compact",
18623
+ maximumFractionDigits: 2,
18624
+ }).format(normalized);
18625
+ } catch (error) {
18626
+ console.log({error});
18588
18627
  return raw
18589
18628
  }
18590
- const quorum = BigInt(raw);
18591
- const divisor = 10 ** decimals;
18592
- const normalized = Number(quorum) / divisor;
18593
-
18594
- return new Intl.NumberFormat("en-US", {
18595
- notation: "compact",
18596
- maximumFractionDigits: 2,
18597
- }).format(normalized);
18598
18629
  }
18599
18630
 
18600
18631
  let cachedChains = null;
@@ -18665,7 +18696,13 @@ async function DUNE() {
18665
18696
  });
18666
18697
 
18667
18698
  const res = await fetch(finalUrl, { method: "GET", headers: HEADERS });
18668
- if (!res.ok) throw new NetworkError(SERVICES_API_KEY.DuneSim, res.status);
18699
+ if (!res.ok) {
18700
+ if(res.status === 400){
18701
+ const data = await res.json();
18702
+ throw new ValidationError(data.message)
18703
+ }
18704
+ throw new NetworkError(SERVICES_API_KEY.DuneSim, res.status);
18705
+ }
18669
18706
 
18670
18707
  const json = await res.json();
18671
18708
  const data =
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fileverse-dev/formulajs",
3
- "version": "4.4.15",
3
+ "version": "4.4.17-fix-sort-fn-1",
4
4
  "description": "JavaScript implementation of most Microsoft Excel formula functions",
5
5
  "author": "Formulajs",
6
6
  "publishConfig": {