@objectstack/driver-memory 3.2.1 → 3.2.2

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @objectstack/driver-memory@3.2.1 build /home/runner/work/spec/spec/packages/plugins/driver-memory
2
+ > @objectstack/driver-memory@3.2.2 build /home/runner/work/spec/spec/packages/plugins/driver-memory
3
3
  > tsup --config ../../../tsup.config.ts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -10,13 +10,13 @@
10
10
  CLI Cleaning output folder
11
11
  ESM Build start
12
12
  CJS Build start
13
- CJS dist/index.js 50.91 KB
14
- CJS dist/index.js.map 97.27 KB
15
- CJS ⚡️ Build success in 96ms
16
- ESM dist/index.mjs 49.23 KB
17
- ESM dist/index.mjs.map 97.21 KB
18
- ESM ⚡️ Build success in 101ms
13
+ ESM dist/index.mjs 52.62 KB
14
+ ESM dist/index.mjs.map 104.48 KB
15
+ ESM ⚡️ Build success in 129ms
16
+ CJS dist/index.js 54.30 KB
17
+ CJS dist/index.js.map 104.53 KB
18
+ CJS ⚡️ Build success in 143ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 16128ms
21
- DTS dist/index.d.mts 13.73 KB
22
- DTS dist/index.d.ts 13.73 KB
20
+ DTS ⚡️ Build success in 17127ms
21
+ DTS dist/index.d.mts 14.27 KB
22
+ DTS dist/index.d.ts 14.27 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @objectstack/driver-memory
2
2
 
3
+ ## 3.2.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 46defbb: Fix filter operators (contains, notContains, startsWith, endsWith, between, null) broken across spec and memory driver
8
+
9
+ - Add `$notContains` to `StringOperatorSchema`, `FieldOperatorsSchema`, `FILTER_OPERATORS`, and `Filter` type
10
+ - Add `notcontains` / `not_contains` to `VALID_AST_OPERATORS` and `AST_OPERATOR_MAP`
11
+ - Fix memory driver `convertToMongoQuery()` passthrough to normalize non-standard operators to Mingo-compatible format
12
+ - Add `$notContains` and `$null` operators to memory matcher
13
+ - Fix undefined value guard in memory matcher to exclude `$exists`, `$ne`, and `$null`
14
+
15
+ - Updated dependencies [46defbb]
16
+ - @objectstack/spec@3.2.2
17
+ - @objectstack/core@3.2.2
18
+
3
19
  ## 3.2.1
4
20
 
5
21
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -181,6 +181,18 @@ declare class InMemoryDriver implements DriverInterface {
181
181
  * Convert a single ObjectQL condition to MongoDB operator format.
182
182
  */
183
183
  private convertConditionToMongo;
184
+ /**
185
+ * Normalize a FilterCondition object by converting non-standard $-prefixed
186
+ * operators ($contains, $notContains, $startsWith, $endsWith, $between, $null)
187
+ * to Mingo-compatible equivalents ($regex, $gte/$lte, null checks).
188
+ */
189
+ private normalizeFilterCondition;
190
+ /**
191
+ * Convert non-standard field operators to Mingo-compatible format.
192
+ * When multiple regex-producing operators appear on the same field
193
+ * (e.g. $startsWith + $endsWith), they are combined via $and.
194
+ */
195
+ private normalizeFieldOperators;
184
196
  /**
185
197
  * Escape special regex characters for safe literal matching.
186
198
  */
package/dist/index.d.ts CHANGED
@@ -181,6 +181,18 @@ declare class InMemoryDriver implements DriverInterface {
181
181
  * Convert a single ObjectQL condition to MongoDB operator format.
182
182
  */
183
183
  private convertConditionToMongo;
184
+ /**
185
+ * Normalize a FilterCondition object by converting non-standard $-prefixed
186
+ * operators ($contains, $notContains, $startsWith, $endsWith, $between, $null)
187
+ * to Mingo-compatible equivalents ($regex, $gte/$lte, null checks).
188
+ */
189
+ private normalizeFilterCondition;
190
+ /**
191
+ * Convert non-standard field operators to Mingo-compatible format.
192
+ * When multiple regex-producing operators appear on the same field
193
+ * (e.g. $startsWith + $endsWith), they are combined via $and.
194
+ */
195
+ private normalizeFieldOperators;
184
196
  /**
185
197
  * Escape special regex characters for safe literal matching.
186
198
  */
package/dist/index.js CHANGED
@@ -194,9 +194,6 @@ var import_mingo = require("mingo");
194
194
 
195
195
  // src/memory-matcher.ts
196
196
  function getValueByPath(obj, path2) {
197
- if (path2 === "_id" && obj._id === void 0 && obj.id !== void 0) {
198
- return obj.id;
199
- }
200
197
  if (!path2.includes(".")) return obj[path2];
201
198
  return path2.split(".").reduce((o, i) => o ? o[i] : void 0, obj);
202
199
  }
@@ -666,7 +663,7 @@ var _InMemoryDriver = class _InMemoryDriver {
666
663
  const op = filters.operator === "or" ? "$or" : "$and";
667
664
  return { [op]: conditions };
668
665
  }
669
- return filters;
666
+ return this.normalizeFilterCondition(filters);
670
667
  }
671
668
  if (!Array.isArray(filters) || filters.length === 0) return {};
672
669
  const logicGroups = [
@@ -727,6 +724,9 @@ var _InMemoryDriver = class _InMemoryDriver {
727
724
  case "contains":
728
725
  case "like":
729
726
  return { [field]: { $regex: new RegExp(this.escapeRegex(value), "i") } };
727
+ case "notcontains":
728
+ case "not_contains":
729
+ return { [field]: { $not: { $regex: new RegExp(this.escapeRegex(value), "i") } } };
730
730
  case "startswith":
731
731
  case "starts_with":
732
732
  return { [field]: { $regex: new RegExp(`^${this.escapeRegex(value)}`, "i") } };
@@ -742,6 +742,104 @@ var _InMemoryDriver = class _InMemoryDriver {
742
742
  return null;
743
743
  }
744
744
  }
745
+ /**
746
+ * Normalize a FilterCondition object by converting non-standard $-prefixed
747
+ * operators ($contains, $notContains, $startsWith, $endsWith, $between, $null)
748
+ * to Mingo-compatible equivalents ($regex, $gte/$lte, null checks).
749
+ */
750
+ normalizeFilterCondition(filter) {
751
+ const result = {};
752
+ const extraAndConditions = [];
753
+ for (const key of Object.keys(filter)) {
754
+ const value = filter[key];
755
+ if (key === "$and" || key === "$or") {
756
+ result[key] = Array.isArray(value) ? value.map((child) => this.normalizeFilterCondition(child)) : value;
757
+ continue;
758
+ }
759
+ if (key === "$not") {
760
+ result[key] = value && typeof value === "object" ? this.normalizeFilterCondition(value) : value;
761
+ continue;
762
+ }
763
+ if (key.startsWith("$")) {
764
+ result[key] = value;
765
+ continue;
766
+ }
767
+ if (value && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof RegExp)) {
768
+ const normalized = this.normalizeFieldOperators(value);
769
+ if (normalized._multiRegex) {
770
+ const regexConditions = normalized._multiRegex;
771
+ delete normalized._multiRegex;
772
+ for (const rc of regexConditions) {
773
+ extraAndConditions.push({ [key]: { ...normalized, ...rc } });
774
+ }
775
+ } else {
776
+ result[key] = normalized;
777
+ }
778
+ } else {
779
+ result[key] = value;
780
+ }
781
+ }
782
+ if (extraAndConditions.length > 0) {
783
+ const existing = result.$and;
784
+ const andArray = Array.isArray(existing) ? existing : [];
785
+ if (Object.keys(result).filter((k) => k !== "$and").length > 0) {
786
+ const rest = { ...result };
787
+ delete rest.$and;
788
+ andArray.push(rest);
789
+ }
790
+ andArray.push(...extraAndConditions);
791
+ return { $and: andArray };
792
+ }
793
+ return result;
794
+ }
795
+ /**
796
+ * Convert non-standard field operators to Mingo-compatible format.
797
+ * When multiple regex-producing operators appear on the same field
798
+ * (e.g. $startsWith + $endsWith), they are combined via $and.
799
+ */
800
+ normalizeFieldOperators(ops) {
801
+ const result = {};
802
+ const regexConditions = [];
803
+ for (const op of Object.keys(ops)) {
804
+ const val = ops[op];
805
+ switch (op) {
806
+ case "$contains":
807
+ regexConditions.push({ $regex: new RegExp(this.escapeRegex(val), "i") });
808
+ break;
809
+ case "$notContains":
810
+ result.$not = { $regex: new RegExp(this.escapeRegex(val), "i") };
811
+ break;
812
+ case "$startsWith":
813
+ regexConditions.push({ $regex: new RegExp(`^${this.escapeRegex(val)}`, "i") });
814
+ break;
815
+ case "$endsWith":
816
+ regexConditions.push({ $regex: new RegExp(`${this.escapeRegex(val)}$`, "i") });
817
+ break;
818
+ case "$between":
819
+ if (Array.isArray(val) && val.length === 2) {
820
+ result.$gte = val[0];
821
+ result.$lte = val[1];
822
+ }
823
+ break;
824
+ case "$null":
825
+ if (val === true) {
826
+ result.$eq = null;
827
+ } else {
828
+ result.$ne = null;
829
+ }
830
+ break;
831
+ default:
832
+ result[op] = val;
833
+ break;
834
+ }
835
+ }
836
+ if (regexConditions.length === 1) {
837
+ Object.assign(result, regexConditions[0]);
838
+ } else if (regexConditions.length > 1) {
839
+ result._multiRegex = regexConditions;
840
+ }
841
+ return result;
842
+ }
745
843
  /**
746
844
  * Escape special regex characters for safe literal matching.
747
845
  */