spice-js 2.7.14 → 2.7.16

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.
@@ -1248,6 +1248,15 @@ class SpiceModel {
1248
1248
  return t.startsWith("`") ? t : q(t);
1249
1249
  });
1250
1250
  return q(this.type) + "." + safeParts.join(".");
1251
+ } // For protected aliases (joined resources), return the base table's column (IDs)
1252
+ // instead of the joined resource itself. The JOIN is only for WHERE filtering,
1253
+ // and the serializer will map the IDs later.
1254
+
1255
+
1256
+ var unquotedCol = col.replace(/`/g, "");
1257
+
1258
+ if (protectedSet.has(unquotedCol)) {
1259
+ return q(this.type) + "." + q(unquotedCol);
1251
1260
  }
1252
1261
 
1253
1262
  if (!col.includes(".") && !col.startsWith("`")) {
@@ -1290,34 +1299,42 @@ class SpiceModel {
1290
1299
  }
1291
1300
 
1292
1301
  extractNestings(string, localType) {
1293
- var returnVal = []; // First, extract loop variables and embedded arrays from ANY/EVERY expressions
1294
- // These should be EXCLUDED from join candidates
1302
+ var returnVal = []; // Extract loop variables from ANY/EVERY expressions
1303
+ // Loop variables (e.g., "vote" in "EVERY vote IN committee_votes") should be excluded
1304
+ // But the collection being iterated (e.g., "committee_votes") should be INCLUDED
1305
+ // if it's a joinable mapped property - buildJoinMetadata will filter non-joinables
1295
1306
 
1296
1307
  var anyEveryRegex = /(ANY|EVERY)\s+(\w+)\s+IN\s+`?(\w+)`?/gi;
1297
1308
  var anyEveryMatch;
1298
1309
  var loopVariables = new Set();
1299
- var embeddedArrayFields = new Set();
1310
+ var anyEveryCollections = new Set();
1300
1311
 
1301
1312
  while ((anyEveryMatch = anyEveryRegex.exec(string)) !== null) {
1302
- loopVariables.add(anyEveryMatch[2]); // e.g., "p"
1313
+ loopVariables.add(anyEveryMatch[2]); // e.g., "vote" - exclude from nestings
1303
1314
 
1304
- embeddedArrayFields.add(anyEveryMatch[3]); // e.g., "permissions"
1305
- } // Now extract dot notation patterns, but skip loop variables and embedded arrays
1315
+ anyEveryCollections.add(anyEveryMatch[3]); // e.g., "committee_votes" - include in nestings
1316
+ } // Now extract dot notation patterns, but skip loop variables
1306
1317
 
1307
1318
 
1308
1319
  var regex = /(`?\w+`?)\.(`?\w+`?)/g;
1309
1320
  var match;
1310
1321
 
1311
1322
  while ((match = regex.exec(string)) !== null) {
1312
- var first = match[1].replace(/`/g, ""); // Skip if it's the local type, a loop variable from ANY/EVERY, or an embedded array field
1323
+ var first = match[1].replace(/`/g, ""); // Skip if it's the local type or a loop variable from ANY/EVERY
1313
1324
 
1314
- if (first !== localType && !loopVariables.has(first) && !embeddedArrayFields.has(first)) {
1325
+ if (first !== localType && !loopVariables.has(first)) {
1315
1326
  returnVal.push(first);
1316
1327
  }
1317
- } // DO NOT add embedded array fields from ANY/EVERY expressions
1318
- // They iterate over document's embedded array, not join to another collection
1328
+ } // Add collections from ANY/EVERY expressions as potential nestings
1329
+ // buildJoinMetadata will filter out non-joinable ones (embedded arrays vs mapped collections)
1319
1330
 
1320
1331
 
1332
+ for (var collection of anyEveryCollections) {
1333
+ if (collection !== localType) {
1334
+ returnVal.push(collection);
1335
+ }
1336
+ }
1337
+
1321
1338
  return [...new Set(returnVal)];
1322
1339
  }
1323
1340
 
@@ -1460,6 +1477,11 @@ class SpiceModel {
1460
1477
 
1461
1478
  if (tableName === this.type && match) {
1462
1479
  return col;
1480
+ } // If column is already backtick-quoted, return as-is
1481
+
1482
+
1483
+ if (col.startsWith("`") && col.endsWith("`")) {
1484
+ return col;
1463
1485
  }
1464
1486
 
1465
1487
  return "`" + col + "`";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spice-js",
3
- "version": "2.7.14",
3
+ "version": "2.7.16",
4
4
  "description": "spice",
5
5
  "main": "build/index.js",
6
6
  "repository": {
@@ -1064,6 +1064,14 @@ export default class SpiceModel {
1064
1064
  return `${q(this.type)}.${safeParts.join(".")}`;
1065
1065
  }
1066
1066
 
1067
+ // For protected aliases (joined resources), return the base table's column (IDs)
1068
+ // instead of the joined resource itself. The JOIN is only for WHERE filtering,
1069
+ // and the serializer will map the IDs later.
1070
+ const unquotedCol = col.replace(/`/g, "");
1071
+ if (protectedSet.has(unquotedCol)) {
1072
+ return `${q(this.type)}.${q(unquotedCol)}`;
1073
+ }
1074
+
1067
1075
  if (!col.includes(".") && !col.startsWith("`")) {
1068
1076
  return q(col);
1069
1077
  }
@@ -1110,32 +1118,39 @@ export default class SpiceModel {
1110
1118
  extractNestings(string, localType) {
1111
1119
  let returnVal = [];
1112
1120
 
1113
- // First, extract loop variables and embedded arrays from ANY/EVERY expressions
1114
- // These should be EXCLUDED from join candidates
1121
+ // Extract loop variables from ANY/EVERY expressions
1122
+ // Loop variables (e.g., "vote" in "EVERY vote IN committee_votes") should be excluded
1123
+ // But the collection being iterated (e.g., "committee_votes") should be INCLUDED
1124
+ // if it's a joinable mapped property - buildJoinMetadata will filter non-joinables
1115
1125
  let anyEveryRegex = /(ANY|EVERY)\s+(\w+)\s+IN\s+`?(\w+)`?/gi;
1116
1126
  let anyEveryMatch;
1117
1127
  const loopVariables = new Set();
1118
- const embeddedArrayFields = new Set();
1128
+ const anyEveryCollections = new Set();
1119
1129
 
1120
1130
  while ((anyEveryMatch = anyEveryRegex.exec(string)) !== null) {
1121
- loopVariables.add(anyEveryMatch[2]); // e.g., "p"
1122
- embeddedArrayFields.add(anyEveryMatch[3]); // e.g., "permissions"
1131
+ loopVariables.add(anyEveryMatch[2]); // e.g., "vote" - exclude from nestings
1132
+ anyEveryCollections.add(anyEveryMatch[3]); // e.g., "committee_votes" - include in nestings
1123
1133
  }
1124
1134
 
1125
- // Now extract dot notation patterns, but skip loop variables and embedded arrays
1135
+ // Now extract dot notation patterns, but skip loop variables
1126
1136
  let regex = /(`?\w+`?)\.(`?\w+`?)/g;
1127
1137
  let match;
1128
1138
 
1129
1139
  while ((match = regex.exec(string)) !== null) {
1130
1140
  let first = match[1].replace(/`/g, "");
1131
- // Skip if it's the local type, a loop variable from ANY/EVERY, or an embedded array field
1132
- if (first !== localType && !loopVariables.has(first) && !embeddedArrayFields.has(first)) {
1141
+ // Skip if it's the local type or a loop variable from ANY/EVERY
1142
+ if (first !== localType && !loopVariables.has(first)) {
1133
1143
  returnVal.push(first);
1134
1144
  }
1135
1145
  }
1136
1146
 
1137
- // DO NOT add embedded array fields from ANY/EVERY expressions
1138
- // They iterate over document's embedded array, not join to another collection
1147
+ // Add collections from ANY/EVERY expressions as potential nestings
1148
+ // buildJoinMetadata will filter out non-joinable ones (embedded arrays vs mapped collections)
1149
+ for (const collection of anyEveryCollections) {
1150
+ if (collection !== localType) {
1151
+ returnVal.push(collection);
1152
+ }
1153
+ }
1139
1154
 
1140
1155
  return [...new Set(returnVal)];
1141
1156
  }
@@ -1292,6 +1307,10 @@ export default class SpiceModel {
1292
1307
  return col;
1293
1308
  }
1294
1309
 
1310
+ // If column is already backtick-quoted, return as-is
1311
+ if (col.startsWith("`") && col.endsWith("`")) {
1312
+ return col;
1313
+ }
1295
1314
  return `\`${col}\``;
1296
1315
  });
1297
1316