@statezero/core 0.2.14 → 0.2.15

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.
@@ -83,6 +83,7 @@ function processFieldPath(fieldPath, value, ModelClass, options = {}) {
83
83
  let currentModel = ModelClass;
84
84
  let processedPath = [];
85
85
  let isRelationship = false;
86
+ let isM2M = false; // Track if this is a many-to-many relationship
86
87
  let finalFieldName = null; // Track the actual field name for schema lookup
87
88
  for (let i = 0; i < fieldParts.length; i++) {
88
89
  let part = fieldParts[i];
@@ -109,6 +110,7 @@ function processFieldPath(fieldPath, value, ModelClass, options = {}) {
109
110
  // For many-to-many relationships, don't append the primary key field
110
111
  // M2M fields store an array of PKs directly, not objects
111
112
  if (relationshipType === 'many-to-many') {
113
+ isM2M = true;
112
114
  // Keep path as-is (e.g., 'comprehensive_models')
113
115
  // Sift will handle array membership check
114
116
  finalFieldName = part;
@@ -154,7 +156,7 @@ function processFieldPath(fieldPath, value, ModelClass, options = {}) {
154
156
  }
155
157
  // Handle the single lookup operation if present
156
158
  if (lookup) {
157
- return createOperatorFromLookup(finalPath, lookup, normalizedValue, isRelationship, currentModel, finalFieldName);
159
+ return createOperatorFromLookup(finalPath, lookup, normalizedValue, isRelationship, currentModel, finalFieldName, isM2M);
158
160
  }
159
161
  // If there's no explicit lookup and this is a relationship field,
160
162
  // we've already appended the PK field name to the path
@@ -201,9 +203,10 @@ function createDatePartComparisonOperator(field, datePart, comparisonOperator, v
201
203
  * @param {boolean} isRelationship - Whether the field is a relationship
202
204
  * @param {Class} ModelClass - The model class (unused, for future extensibility)
203
205
  * @param {string} finalFieldName - The final field name (unused, for future extensibility)
206
+ * @param {boolean} isM2M - Whether the field is a many-to-many relationship
204
207
  * @returns {Object} Object with field name and sift operator
205
208
  */
206
- function createOperatorFromLookup(field, lookup, value, isRelationship, ModelClass, finalFieldName) {
209
+ function createOperatorFromLookup(field, lookup, value, isRelationship, ModelClass, finalFieldName, isM2M = false) {
207
210
  // Helper function to escape special characters in regex
208
211
  function escapeRegExp(string) {
209
212
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
@@ -212,7 +215,23 @@ function createOperatorFromLookup(field, lookup, value, isRelationship, ModelCla
212
215
  if (isRelationship) {
213
216
  // For relationship fields with lookups, we need special handling
214
217
  if (lookup === 'isnull') {
215
- // Check for both undefined and null values
218
+ if (isM2M) {
219
+ // For M2M fields, isnull=True means null OR empty array, but not undefined because
220
+ // that can mean the field was not fetched so we can't be sure
221
+ // isnull=False means has at least one item
222
+ // Use a custom function since sift doesn't support $or inside field operators
223
+ return {
224
+ field,
225
+ operator: {
226
+ $where: function (fieldValue) {
227
+ const isEmpty = fieldValue === null ||
228
+ (Array.isArray(fieldValue) && fieldValue.length === 0);
229
+ return value ? isEmpty : !isEmpty;
230
+ }
231
+ }
232
+ };
233
+ }
234
+ // For FK/O2O, check for both undefined and null values
216
235
  return {
217
236
  field,
218
237
  operator: value ? { $in: [null, undefined] } : { $nin: [null, undefined] }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statezero/core",
3
- "version": "0.2.14",
3
+ "version": "0.2.15",
4
4
  "type": "module",
5
5
  "module": "ESNext",
6
6
  "description": "The type-safe frontend client for StateZero - connect directly to your backend models with zero boilerplate",