spice-js 2.7.23 → 2.7.26

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.
@@ -696,6 +696,9 @@ class SpiceModel {
696
696
  try {
697
697
  if (_.isString(data)) {
698
698
  var result = yield _this7.database.get(data);
699
+ if (!result) {
700
+ return false;
701
+ }
699
702
  if (result.type) if (result.type != _this7.type) {
700
703
  return false;
701
704
  }
@@ -703,6 +706,9 @@ class SpiceModel {
703
706
  return false;
704
707
  }
705
708
  } else {
709
+ if (!data) {
710
+ return false;
711
+ }
706
712
  if (data.type) if (data.type != _this7.type) {
707
713
  return false;
708
714
  }
@@ -1036,65 +1042,33 @@ class SpiceModel {
1036
1042
  }
1037
1043
  filterResultsByColumns(data, columns) {
1038
1044
  if (columns && columns !== "") {
1045
+ // Remove backticks and replace meta().id with id
1039
1046
  var cleanedColumns = columns.replace(/`/g, "").replace(/meta\(\)\.id/g, "id");
1040
- var columnPaths = cleanedColumns.split(",").map(col => col.trim()).map(col => {
1047
+
1048
+ // Process each column by splitting on comma and trimming
1049
+ var columnList = cleanedColumns.split(",").map(col => col.trim()).map(col => {
1050
+ // Check for alias with AS (case-insensitive)
1041
1051
  var aliasMatch = col.match(/\s+AS\s+([\w]+)/i);
1042
1052
  if (aliasMatch) {
1043
- return {
1044
- original: col,
1045
- path: col.replace(/\s+AS\s+[\w]+/i, "").trim(),
1046
- alias: aliasMatch[1].trim()
1047
- };
1053
+ return aliasMatch[1].trim();
1048
1054
  }
1049
- return {
1050
- original: col,
1051
- path: col,
1052
- alias: null
1053
- };
1055
+ // Otherwise, for dot-notation, keep the first segment after removing
1056
+ // the base model prefix (e.g. user.group.name -> group).
1057
+ if (col.includes(".")) {
1058
+ var pathParts = col.split(".").map(part => part.trim()).filter(part => part !== "");
1059
+ if (pathParts[0] === this.type && pathParts.length > 1) {
1060
+ pathParts = pathParts.slice(1);
1061
+ }
1062
+ return pathParts[0];
1063
+ }
1064
+ return col;
1054
1065
  });
1055
- var requiredKeys = ["_permissions", "_permissions_", "id"];
1056
- return data.map(entry => {
1057
- var result = {};
1058
-
1059
- // Add required keys
1060
- requiredKeys.forEach(key => {
1061
- if (entry.hasOwnProperty(key)) {
1062
- result[key] = entry[key];
1063
- }
1064
- });
1065
1066
 
1066
- // Process each column path
1067
- columnPaths.forEach(_ref10 => {
1068
- var {
1069
- path,
1070
- alias
1071
- } = _ref10;
1072
- var actualPath = path;
1073
-
1074
- // Remove table prefix if it matches this.type
1075
- if (path.includes(".")) {
1076
- var parts = path.split(".");
1077
- if (parts[0] === this.type) {
1078
- actualPath = parts.slice(1).join(".");
1079
- }
1080
- }
1081
- var value = _.get(entry, actualPath);
1082
- if (value !== undefined) {
1083
- if (alias) {
1084
- result[alias] = value;
1085
- } else if (actualPath.includes(".")) {
1086
- // For nested paths, use the last part as the key
1087
- var key = actualPath.split(".").pop();
1088
- result[key] = value;
1089
- } else {
1090
- result[actualPath] = value;
1091
- }
1092
- }
1093
- });
1094
- return result;
1095
- });
1067
+ // Ensure that essential keys are always picked
1068
+ var requiredKeys = ["_permissions", "_permissions_", "id"];
1069
+ var finalKeys = [...new Set([...columnList, ...requiredKeys])];
1070
+ return data.map(entry => _.pick(entry, finalKeys));
1096
1071
  }
1097
- console.log("filterResultsByColumns", data);
1098
1072
  return data;
1099
1073
  }
1100
1074
  extractNestings(string, localType) {
@@ -1135,12 +1109,12 @@ class SpiceModel {
1135
1109
  }
1136
1110
  createJoinSection(mappedNestings) {
1137
1111
  if (!mappedNestings || mappedNestings.length === 0) return "";
1138
- return mappedNestings.map(_ref11 => {
1112
+ return mappedNestings.map(_ref10 => {
1139
1113
  var {
1140
1114
  alias,
1141
1115
  reference,
1142
1116
  is_array
1143
- } = _ref11;
1117
+ } = _ref10;
1144
1118
  var keyspace = (0, _fix.fixCollection)(reference);
1145
1119
  if (is_array === true) {
1146
1120
  return "LEFT NEST `" + keyspace + "` AS `" + alias + "` ON KEYS `" + this.type + "`.`" + alias + "`";
@@ -1285,8 +1259,8 @@ class SpiceModel {
1285
1259
  // Profiling: use track() for proper async context forking
1286
1260
  var p = (_this10$_ctx = _this10[_ctx]) == null ? void 0 : _this10$_ctx.profiler;
1287
1261
  var doList = /*#__PURE__*/function () {
1288
- var _ref12 = _asyncToGenerator(function* () {
1289
- var _args10, _args11, _args12;
1262
+ var _ref11 = _asyncToGenerator(function* () {
1263
+ var _args10, _args11;
1290
1264
  if (args.mapping_dept) _this10[_mapping_dept] = args.mapping_dept;
1291
1265
  if (args.mapping_dept_exempt) _this10[_mapping_dept_exempt] = args.mapping_dept_exempt;
1292
1266
 
@@ -1302,12 +1276,15 @@ class SpiceModel {
1302
1276
  var originalColumns = args.columns;
1303
1277
 
1304
1278
  // Find alias tokens from query/columns/sort
1305
- var nestings = [..._this10.extractNestings(((_args10 = args) == null ? void 0 : _args10.query) || "", _this10.type), ..._this10.extractNestings(((_args11 = args) == null ? void 0 : _args11.columns) || "", _this10.type), ..._this10.extractNestings(((_args12 = args) == null ? void 0 : _args12.sort) || "", _this10.type)];
1279
+ var nestings = [..._this10.extractNestings(((_args10 = args) == null ? void 0 : _args10.query) || "", _this10.type),
1280
+ //...this.extractNestings(args?.columns || "", this.type),
1281
+ ..._this10.extractNestings(((_args11 = args) == null ? void 0 : _args11.sort) || "", _this10.type)];
1306
1282
 
1307
1283
  // Build join metadata and prepare columns
1308
1284
  var {
1309
1285
  mappedNestings
1310
1286
  } = _this10.buildJoinMetadata(nestings, args);
1287
+
1311
1288
  // Build JOIN/NEST from the mapped keyspaces
1312
1289
  args._join = _this10.createJoinSection(mappedNestings);
1313
1290
 
@@ -1357,20 +1334,20 @@ class SpiceModel {
1357
1334
  if (args.skip_hooks !== true) {
1358
1335
  yield _this10.run_hook(results.data, "list", "after");
1359
1336
  }
1360
- results.data = _this10.filterResultsByColumns(results.data, originalColumns);
1337
+ results.data = _this10.filterResultsByColumns(results.data, args.columns);
1361
1338
  //console.log("results.data", results.data);
1362
1339
  return results;
1363
1340
  });
1364
1341
  return function doList() {
1365
- return _ref12.apply(this, arguments);
1342
+ return _ref11.apply(this, arguments);
1366
1343
  };
1367
1344
  }();
1368
1345
  try {
1369
1346
  if (p) {
1370
- var _args13, _args14;
1347
+ var _args12, _args13;
1371
1348
  return yield p.track(_this10.type + ".list", doList, {
1372
- limit: (_args13 = args) == null ? void 0 : _args13.limit,
1373
- offset: (_args14 = args) == null ? void 0 : _args14.offset
1349
+ limit: (_args12 = args) == null ? void 0 : _args12.limit,
1350
+ offset: (_args13 = args) == null ? void 0 : _args13.offset
1374
1351
  });
1375
1352
  }
1376
1353
  return yield doList();
@@ -1416,7 +1393,7 @@ class SpiceModel {
1416
1393
  // Profiling: use track() for proper async context forking
1417
1394
  var p = (_this11$_ctx = _this11[_ctx]) == null ? void 0 : _this11$_ctx.profiler;
1418
1395
  var doFetch = /*#__PURE__*/function () {
1419
- var _ref13 = _asyncToGenerator(function* () {
1396
+ var _ref12 = _asyncToGenerator(function* () {
1420
1397
  if (args.is_custom_query === "true" && args.ids.length > 0) {
1421
1398
  return yield _this11.database.query(query);
1422
1399
  } else if (args.is_full_text === "true") {
@@ -1428,7 +1405,7 @@ class SpiceModel {
1428
1405
  }
1429
1406
  });
1430
1407
  return function doFetch() {
1431
- return _ref13.apply(this, arguments);
1408
+ return _ref12.apply(this, arguments);
1432
1409
  };
1433
1410
  }();
1434
1411
  if (p) {
@@ -1437,12 +1414,12 @@ class SpiceModel {
1437
1414
  return yield doFetch();
1438
1415
  })();
1439
1416
  }
1440
- addHook(_ref14) {
1417
+ addHook(_ref13) {
1441
1418
  var {
1442
1419
  operation,
1443
1420
  when,
1444
1421
  execute
1445
- } = _ref14;
1422
+ } = _ref13;
1446
1423
  this[_hooks][operation][when].push(execute);
1447
1424
  }
1448
1425
  run_hook(data, op, when, old_data) {
@@ -1564,7 +1541,7 @@ class SpiceModel {
1564
1541
 
1565
1542
  // ⚡ Wrap in profiler track() to ensure proper async context for child operations
1566
1543
  var fetchRelated = /*#__PURE__*/function () {
1567
- var _ref15 = _asyncToGenerator(function* () {
1544
+ var _ref14 = _asyncToGenerator(function* () {
1568
1545
  return yield Promise.allSettled(_.map(classes, obj => {
1569
1546
  var objInstance = new obj(_extends({}, _this13[_args], {
1570
1547
  skip_cache: _this13[_skip_cache],
@@ -1582,7 +1559,7 @@ class SpiceModel {
1582
1559
  }));
1583
1560
  });
1584
1561
  return function fetchRelated() {
1585
- return _ref15.apply(this, arguments);
1562
+ return _ref14.apply(this, arguments);
1586
1563
  };
1587
1564
  }();
1588
1565
  var returned_all;
@@ -1682,7 +1659,7 @@ class SpiceModel {
1682
1659
 
1683
1660
  // ⚡ Wrap in profiler track() to ensure proper async context for child operations
1684
1661
  var fetchRelated = /*#__PURE__*/function () {
1685
- var _ref16 = _asyncToGenerator(function* () {
1662
+ var _ref15 = _asyncToGenerator(function* () {
1686
1663
  return yield Promise.allSettled(_.map(classes, obj => {
1687
1664
  return new obj(_extends({}, _this14[_args], {
1688
1665
  skip_cache: _this14[_skip_cache],
@@ -1699,7 +1676,7 @@ class SpiceModel {
1699
1676
  }));
1700
1677
  });
1701
1678
  return function fetchRelated() {
1702
- return _ref16.apply(this, arguments);
1679
+ return _ref15.apply(this, arguments);
1703
1680
  };
1704
1681
  }();
1705
1682
  var returned_all;
@@ -1747,11 +1724,11 @@ class SpiceModel {
1747
1724
  return original_is_array ? data : data[0];
1748
1725
  })();
1749
1726
  }
1750
- addModifier(_ref17) {
1727
+ addModifier(_ref16) {
1751
1728
  var {
1752
1729
  when,
1753
1730
  execute
1754
- } = _ref17;
1731
+ } = _ref16;
1755
1732
  if (this[_serializers][when]) {
1756
1733
  this[_serializers][when]["modifiers"].push(execute);
1757
1734
  }
@@ -1881,7 +1858,7 @@ class SpiceModel {
1881
1858
  // Profiling: use track() for proper async context forking
1882
1859
  var p = (_this16$_ctx = _this16[_ctx]) == null ? void 0 : _this16$_ctx.profiler;
1883
1860
  var doSerialize = /*#__PURE__*/function () {
1884
- var _ref18 = _asyncToGenerator(function* () {
1861
+ var _ref17 = _asyncToGenerator(function* () {
1885
1862
  var _this16$_serializers, _this16$_serializers$;
1886
1863
  // Early exit if serialization should not run.
1887
1864
  if (!_this16.shouldSerializerRun(args, type)) {
@@ -1949,7 +1926,7 @@ class SpiceModel {
1949
1926
  return originalIsArray ? data : data[0];
1950
1927
  });
1951
1928
  return function doSerialize() {
1952
- return _ref18.apply(this, arguments);
1929
+ return _ref17.apply(this, arguments);
1953
1930
  };
1954
1931
  }();
1955
1932
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spice-js",
3
- "version": "2.7.23",
3
+ "version": "2.7.26",
4
4
  "description": "spice",
5
5
  "main": "build/index.js",
6
6
  "repository": {
@@ -762,6 +762,9 @@ export default class SpiceModel {
762
762
  try {
763
763
  if (_.isString(data)) {
764
764
  let result = await this.database.get(data);
765
+ if (!result) {
766
+ return false;
767
+ }
765
768
  if (result.type)
766
769
  if (result.type != this.type) {
767
770
  return false;
@@ -771,6 +774,9 @@ export default class SpiceModel {
771
774
  return false;
772
775
  }
773
776
  } else {
777
+ if (!data) {
778
+ return false;
779
+ }
774
780
  if (data.type)
775
781
  if (data.type != this.type) {
776
782
  return false;
@@ -32,6 +32,18 @@ class MockDatabase {
32
32
  return this.mockData.get(id);
33
33
  }
34
34
 
35
+ /**
36
+ * Mock update operation
37
+ * @param {string} id - Document ID to update
38
+ * @param {Object} data - Updated document data
39
+ * @returns {Promise<Object>} Updated document
40
+ */
41
+ async update(id, data, ttl) {
42
+ const updated = { ...data, id };
43
+ this.mockData.set(id, updated);
44
+ return updated;
45
+ }
46
+
35
47
  /**
36
48
  * Seed mock database with test data
37
49
  * @param {string} id - Document ID
@@ -578,6 +578,12 @@ describe('SpiceModel - Critical Fixes for Empty/Null Values', () => {
578
578
  model = createTestModel({ type: 'user' });
579
579
  });
580
580
 
581
+ test('should fail update with a controlled not-found error when database returns undefined', async () => {
582
+ await expect(
583
+ model.update({ id: 'missing-user' })
584
+ ).rejects.toThrow('user does not exist. in update');
585
+ });
586
+
581
587
  test('should handle undefined columns parameter', async () => {
582
588
  seedDatabase(model, sampleDbResults.users);
583
589