@webda/ql 4.0.0-beta.2 → 4.0.0-beta.3

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.
Files changed (48) hide show
  1. package/README.md +135 -2
  2. package/lib/WebdaQLLexer.d.ts +19 -13
  3. package/lib/WebdaQLLexer.d.ts.map +1 -1
  4. package/lib/WebdaQLLexer.js +305 -252
  5. package/lib/WebdaQLLexer.js.map +1 -1
  6. package/lib/WebdaQLParserLexer.d.ts +19 -13
  7. package/lib/WebdaQLParserLexer.d.ts.map +1 -1
  8. package/lib/WebdaQLParserLexer.js +305 -252
  9. package/lib/WebdaQLParserLexer.js.map +1 -1
  10. package/lib/WebdaQLParserListener.d.ts +89 -3
  11. package/lib/WebdaQLParserListener.d.ts.map +1 -1
  12. package/lib/WebdaQLParserListener.js +1 -1
  13. package/lib/WebdaQLParserListener.js.map +1 -1
  14. package/lib/WebdaQLParserParser.d.ts +83 -22
  15. package/lib/WebdaQLParserParser.d.ts.map +1 -1
  16. package/lib/WebdaQLParserParser.js +790 -290
  17. package/lib/WebdaQLParserParser.js.map +1 -1
  18. package/lib/WebdaQLParserVisitor.d.ts +65 -2
  19. package/lib/WebdaQLParserVisitor.d.ts.map +1 -1
  20. package/lib/WebdaQLParserVisitor.js +1 -1
  21. package/lib/WebdaQLParserVisitor.js.map +1 -1
  22. package/lib/bind.d.ts +29 -0
  23. package/lib/bind.d.ts.map +1 -0
  24. package/lib/bind.js +139 -0
  25. package/lib/bind.js.map +1 -0
  26. package/lib/bind.spec.d.ts +2 -0
  27. package/lib/bind.spec.d.ts.map +1 -0
  28. package/lib/bind.spec.js +206 -0
  29. package/lib/bind.spec.js.map +1 -0
  30. package/lib/index.d.ts +4 -0
  31. package/lib/index.d.ts.map +1 -0
  32. package/lib/index.js +4 -0
  33. package/lib/index.js.map +1 -0
  34. package/lib/query.d.ts +395 -76
  35. package/lib/query.d.ts.map +1 -1
  36. package/lib/query.js +562 -91
  37. package/lib/query.js.map +1 -1
  38. package/lib/query.spec.js +593 -195
  39. package/lib/query.spec.js.map +1 -1
  40. package/lib/webdaql-string.d.ts +59 -0
  41. package/lib/webdaql-string.d.ts.map +1 -0
  42. package/lib/webdaql-string.js +112 -0
  43. package/lib/webdaql-string.js.map +1 -0
  44. package/lib/webdaql-string.spec.d.ts +2 -0
  45. package/lib/webdaql-string.spec.d.ts.map +1 -0
  46. package/lib/webdaql-string.spec.js +282 -0
  47. package/lib/webdaql-string.spec.js.map +1 -0
  48. package/package.json +30 -42
package/lib/query.spec.js CHANGED
@@ -1,12 +1,44 @@
1
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
1
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
2
+ var useValue = arguments.length > 2;
3
+ for (var i = 0; i < initializers.length; i++) {
4
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
5
+ }
6
+ return useValue ? value : void 0;
7
+ };
8
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
9
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
10
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
11
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
12
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
13
+ var _, done = false;
14
+ for (var i = decorators.length - 1; i >= 0; i--) {
15
+ var context = {};
16
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
17
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
18
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
19
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
20
+ if (kind === "accessor") {
21
+ if (result === void 0) continue;
22
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
23
+ if (_ = accept(result.get)) descriptor.get = _;
24
+ if (_ = accept(result.set)) descriptor.set = _;
25
+ if (_ = accept(result.init)) initializers.unshift(_);
26
+ }
27
+ else if (_ = accept(result)) {
28
+ if (kind === "field") initializers.unshift(_);
29
+ else descriptor[key] = _;
30
+ }
31
+ }
32
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
33
+ done = true;
6
34
  };
7
- import { suite, test } from "@testdeck/mocha";
35
+ import { suite, test } from "@webda/test";
8
36
  import * as assert from "assert";
9
37
  import * as WebdaQL from "./query.js";
38
+ import { CharStreams, CommonTokenStream } from "antlr4ts";
39
+ import { ParseTreeWalker } from "antlr4ts/tree/index.js";
40
+ import { WebdaQLLexer } from "./WebdaQLLexer.js";
41
+ import { WebdaQLParserParser } from "./WebdaQLParserParser.js";
10
42
  const targets = [
11
43
  {
12
44
  test: {
@@ -18,197 +50,563 @@ const targets = [
18
50
  attr5: ["test", "plip"]
19
51
  }
20
52
  ];
21
- let QueryTest = class QueryTest {
22
- dev() {
23
- const queryMap = {
24
- "a = 1 AND b = 2 AND c = 3 AND d = 4": "a = 1 AND b = 2 AND c = 3 AND d = 4",
25
- "a = 1 AND (b = 2 AND c = 3) AND d = 4": "a = 1 AND b = 2 AND c = 3 AND d = 4",
26
- "a = 1 AND ((b = 2 AND c = 3) AND d = 4)": "a = 1 AND b = 2 AND c = 3 AND d = 4",
27
- "a = 1 AND ((b = 2 OR c = 3) AND d = 4)": "a = 1 AND ( b = 2 OR c = 3 ) AND d = 4",
28
- "a = 1 AND (b = 2) AND c = 3 AND d = 4": "a = 1 AND b = 2 AND c = 3 AND d = 4",
29
- "a = 1 OR b = 2 OR c = 3 OR d = 4": "a = 1 OR b = 2 OR c = 3 OR d = 4",
30
- 'a = 1 AND (b = 2 OR c = 3) AND d = "plop"': 'a = 1 AND ( b = 2 OR c = 3 ) AND d = "plop"',
31
- "a = 1 OR b = TRUE AND c = FALSE OR d = 'plop'": 'a = 1 OR ( b = TRUE AND c = FALSE ) OR d = "plop"',
32
- "test.attr1 = 'plop'": true,
33
- "test.attr1 = 'plop2'": false,
34
- "test.attr1 = 'plop' AND attr2 IN ['TEST', TRUE, 3]": 'test.attr1 = "plop" AND attr2 IN ["TEST", TRUE, 3]',
35
- "(test.attr1 = 'plop' AND attr2 IN ['TEST', 'OK']) OR attr3 <= 12": null,
36
- "test.attr1 = 'plop' AND attr2 IN ['TEST', 'OK'] OR attr3 <= 12 AND attr4 = 'ok'": null,
37
- "test.attr1 = 'plop' AND (attr2 IN ['TEST', 'OK'] OR attr3 <= 12) AND attr4 = 'ok'": null,
38
- "test.attr1 = 'plop' AND attr2 IN ['TEST', 'OK'] AND attr3 <= 12 AND attr4 = 'ok'": null,
39
- "a = 1 OR b = 2": null,
40
- "a = 1 AND b = 2 OR c = 3 AND d = 4": null,
41
- "a = 1 AND b = 2 OR c = 3 AND d = 4 OR e = 5 AND f = 6": null,
42
- "a = 1 AND (b = 2 OR (c = 3 AND (d = 4 OR e = 5))) AND f = 6": null,
43
- "": true,
44
- 'test.attr1 LIKE "pl_p"': true,
45
- 'test.attr1 LIKE "pl__p"': false,
46
- 'attr3 LIKE "1_"': true,
47
- "attr3 >= 12": true,
48
- "attr3 <= 12": false,
49
- "attr3 != 12": true,
50
- "attr3 > 12": true,
51
- "attr3 < 13": false,
52
- "attr5 CONTAINS 'test'": true,
53
- "attr5 CONTAINS 'test2'": false,
54
- 'test.attr1 LIKE "pl%"': true,
55
- "a = 1 AND b=2 OR a=1 AND b=3": "( a = 1 AND b = 2 ) OR ( a = 1 AND b = 3 )", // TODO Might want to auto-simplify to a = 1 AND b IN [2,3]
56
- "a = 1 ORDER BY a": null,
57
- "a = 1 ORDER BY a DESC, b ASC": null,
58
- "(attr3 >= 12)": true
59
- };
60
- for (const query in queryMap) {
61
- const validator = new WebdaQL.QueryValidator(query);
62
- assert.strictEqual(validator.displayTree().replace(/\s/g, ""), query.replace(/\s/g, ""));
63
- if (typeof queryMap[query] === "string") {
64
- assert.strictEqual(validator.getExpression().toString(), queryMap[query], `Failed optimization of query: ${query} => ${validator.getExpression().toString()}`);
53
+ let QueryTest = (() => {
54
+ let _classDecorators = [suite];
55
+ let _classDescriptor;
56
+ let _classExtraInitializers = [];
57
+ let _classThis;
58
+ let _instanceExtraInitializers = [];
59
+ let _dev_decorators;
60
+ let _prependQuery_decorators;
61
+ let _likeRegexp_decorators;
62
+ let _simple_decorators;
63
+ let _contains_decorators;
64
+ let _andQuery_decorators;
65
+ let _orQuery_decorators;
66
+ let _multipleQuery_decorators;
67
+ let _unsanitize_decorators;
68
+ let _limitOffset_decorators;
69
+ let _badQuery_decorators;
70
+ let _setters_decorators;
71
+ let _parse_decorators;
72
+ let _partialValidator_decorators;
73
+ let _mergeTypeMismatch_decorators;
74
+ let _getOffsetFallback_decorators;
75
+ let _expressionToString_decorators;
76
+ let _hasCondition_decorators;
77
+ let _queryValidatorToString_decorators;
78
+ let _complexQueries_decorators;
79
+ let _containsNonArray_decorators;
80
+ let _likeOnNonString_decorators;
81
+ let _likeOnMissingAttribute_decorators;
82
+ let _quotedLiterals_decorators;
83
+ let _lexerIntrospection_decorators;
84
+ let _parserIntrospection_decorators;
85
+ let _listenerWalk_decorators;
86
+ let _contextAccessors_decorators;
87
+ let _setterAssignment_decorators;
88
+ let _mergePreservesExistingMeta_decorators;
89
+ let _booleanLiterals_decorators;
90
+ let _booleanLiteralsFold_decorators;
91
+ let _bareAtomsAreSyntaxErrors_decorators;
92
+ let _partialValidatorWithBooleans_decorators;
93
+ let _isNull_decorators;
94
+ let _isNullToString_decorators;
95
+ let _isNullPartial_decorators;
96
+ var QueryTest = class {
97
+ static { _classThis = this; }
98
+ static {
99
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
100
+ _dev_decorators = [test];
101
+ _prependQuery_decorators = [test];
102
+ _likeRegexp_decorators = [test];
103
+ _simple_decorators = [test];
104
+ _contains_decorators = [test];
105
+ _andQuery_decorators = [test];
106
+ _orQuery_decorators = [test];
107
+ _multipleQuery_decorators = [test];
108
+ _unsanitize_decorators = [test];
109
+ _limitOffset_decorators = [test];
110
+ _badQuery_decorators = [test];
111
+ _setters_decorators = [test];
112
+ _parse_decorators = [test];
113
+ _partialValidator_decorators = [test];
114
+ _mergeTypeMismatch_decorators = [test];
115
+ _getOffsetFallback_decorators = [test];
116
+ _expressionToString_decorators = [test];
117
+ _hasCondition_decorators = [test];
118
+ _queryValidatorToString_decorators = [test];
119
+ _complexQueries_decorators = [test];
120
+ _containsNonArray_decorators = [test];
121
+ _likeOnNonString_decorators = [test];
122
+ _likeOnMissingAttribute_decorators = [test];
123
+ _quotedLiterals_decorators = [test];
124
+ _lexerIntrospection_decorators = [test];
125
+ _parserIntrospection_decorators = [test];
126
+ _listenerWalk_decorators = [test];
127
+ _contextAccessors_decorators = [test];
128
+ _setterAssignment_decorators = [test];
129
+ _mergePreservesExistingMeta_decorators = [test];
130
+ _booleanLiterals_decorators = [test];
131
+ _booleanLiteralsFold_decorators = [test];
132
+ _bareAtomsAreSyntaxErrors_decorators = [test];
133
+ _partialValidatorWithBooleans_decorators = [test];
134
+ _isNull_decorators = [test];
135
+ _isNullToString_decorators = [test];
136
+ _isNullPartial_decorators = [test];
137
+ __esDecorate(this, null, _dev_decorators, { kind: "method", name: "dev", static: false, private: false, access: { has: obj => "dev" in obj, get: obj => obj.dev }, metadata: _metadata }, null, _instanceExtraInitializers);
138
+ __esDecorate(this, null, _prependQuery_decorators, { kind: "method", name: "prependQuery", static: false, private: false, access: { has: obj => "prependQuery" in obj, get: obj => obj.prependQuery }, metadata: _metadata }, null, _instanceExtraInitializers);
139
+ __esDecorate(this, null, _likeRegexp_decorators, { kind: "method", name: "likeRegexp", static: false, private: false, access: { has: obj => "likeRegexp" in obj, get: obj => obj.likeRegexp }, metadata: _metadata }, null, _instanceExtraInitializers);
140
+ __esDecorate(this, null, _simple_decorators, { kind: "method", name: "simple", static: false, private: false, access: { has: obj => "simple" in obj, get: obj => obj.simple }, metadata: _metadata }, null, _instanceExtraInitializers);
141
+ __esDecorate(this, null, _contains_decorators, { kind: "method", name: "contains", static: false, private: false, access: { has: obj => "contains" in obj, get: obj => obj.contains }, metadata: _metadata }, null, _instanceExtraInitializers);
142
+ __esDecorate(this, null, _andQuery_decorators, { kind: "method", name: "andQuery", static: false, private: false, access: { has: obj => "andQuery" in obj, get: obj => obj.andQuery }, metadata: _metadata }, null, _instanceExtraInitializers);
143
+ __esDecorate(this, null, _orQuery_decorators, { kind: "method", name: "orQuery", static: false, private: false, access: { has: obj => "orQuery" in obj, get: obj => obj.orQuery }, metadata: _metadata }, null, _instanceExtraInitializers);
144
+ __esDecorate(this, null, _multipleQuery_decorators, { kind: "method", name: "multipleQuery", static: false, private: false, access: { has: obj => "multipleQuery" in obj, get: obj => obj.multipleQuery }, metadata: _metadata }, null, _instanceExtraInitializers);
145
+ __esDecorate(this, null, _unsanitize_decorators, { kind: "method", name: "unsanitize", static: false, private: false, access: { has: obj => "unsanitize" in obj, get: obj => obj.unsanitize }, metadata: _metadata }, null, _instanceExtraInitializers);
146
+ __esDecorate(this, null, _limitOffset_decorators, { kind: "method", name: "limitOffset", static: false, private: false, access: { has: obj => "limitOffset" in obj, get: obj => obj.limitOffset }, metadata: _metadata }, null, _instanceExtraInitializers);
147
+ __esDecorate(this, null, _badQuery_decorators, { kind: "method", name: "badQuery", static: false, private: false, access: { has: obj => "badQuery" in obj, get: obj => obj.badQuery }, metadata: _metadata }, null, _instanceExtraInitializers);
148
+ __esDecorate(this, null, _setters_decorators, { kind: "method", name: "setters", static: false, private: false, access: { has: obj => "setters" in obj, get: obj => obj.setters }, metadata: _metadata }, null, _instanceExtraInitializers);
149
+ __esDecorate(this, null, _parse_decorators, { kind: "method", name: "parse", static: false, private: false, access: { has: obj => "parse" in obj, get: obj => obj.parse }, metadata: _metadata }, null, _instanceExtraInitializers);
150
+ __esDecorate(this, null, _partialValidator_decorators, { kind: "method", name: "partialValidator", static: false, private: false, access: { has: obj => "partialValidator" in obj, get: obj => obj.partialValidator }, metadata: _metadata }, null, _instanceExtraInitializers);
151
+ __esDecorate(this, null, _mergeTypeMismatch_decorators, { kind: "method", name: "mergeTypeMismatch", static: false, private: false, access: { has: obj => "mergeTypeMismatch" in obj, get: obj => obj.mergeTypeMismatch }, metadata: _metadata }, null, _instanceExtraInitializers);
152
+ __esDecorate(this, null, _getOffsetFallback_decorators, { kind: "method", name: "getOffsetFallback", static: false, private: false, access: { has: obj => "getOffsetFallback" in obj, get: obj => obj.getOffsetFallback }, metadata: _metadata }, null, _instanceExtraInitializers);
153
+ __esDecorate(this, null, _expressionToString_decorators, { kind: "method", name: "expressionToString", static: false, private: false, access: { has: obj => "expressionToString" in obj, get: obj => obj.expressionToString }, metadata: _metadata }, null, _instanceExtraInitializers);
154
+ __esDecorate(this, null, _hasCondition_decorators, { kind: "method", name: "hasCondition", static: false, private: false, access: { has: obj => "hasCondition" in obj, get: obj => obj.hasCondition }, metadata: _metadata }, null, _instanceExtraInitializers);
155
+ __esDecorate(this, null, _queryValidatorToString_decorators, { kind: "method", name: "queryValidatorToString", static: false, private: false, access: { has: obj => "queryValidatorToString" in obj, get: obj => obj.queryValidatorToString }, metadata: _metadata }, null, _instanceExtraInitializers);
156
+ __esDecorate(this, null, _complexQueries_decorators, { kind: "method", name: "complexQueries", static: false, private: false, access: { has: obj => "complexQueries" in obj, get: obj => obj.complexQueries }, metadata: _metadata }, null, _instanceExtraInitializers);
157
+ __esDecorate(this, null, _containsNonArray_decorators, { kind: "method", name: "containsNonArray", static: false, private: false, access: { has: obj => "containsNonArray" in obj, get: obj => obj.containsNonArray }, metadata: _metadata }, null, _instanceExtraInitializers);
158
+ __esDecorate(this, null, _likeOnNonString_decorators, { kind: "method", name: "likeOnNonString", static: false, private: false, access: { has: obj => "likeOnNonString" in obj, get: obj => obj.likeOnNonString }, metadata: _metadata }, null, _instanceExtraInitializers);
159
+ __esDecorate(this, null, _likeOnMissingAttribute_decorators, { kind: "method", name: "likeOnMissingAttribute", static: false, private: false, access: { has: obj => "likeOnMissingAttribute" in obj, get: obj => obj.likeOnMissingAttribute }, metadata: _metadata }, null, _instanceExtraInitializers);
160
+ __esDecorate(this, null, _quotedLiterals_decorators, { kind: "method", name: "quotedLiterals", static: false, private: false, access: { has: obj => "quotedLiterals" in obj, get: obj => obj.quotedLiterals }, metadata: _metadata }, null, _instanceExtraInitializers);
161
+ __esDecorate(this, null, _lexerIntrospection_decorators, { kind: "method", name: "lexerIntrospection", static: false, private: false, access: { has: obj => "lexerIntrospection" in obj, get: obj => obj.lexerIntrospection }, metadata: _metadata }, null, _instanceExtraInitializers);
162
+ __esDecorate(this, null, _parserIntrospection_decorators, { kind: "method", name: "parserIntrospection", static: false, private: false, access: { has: obj => "parserIntrospection" in obj, get: obj => obj.parserIntrospection }, metadata: _metadata }, null, _instanceExtraInitializers);
163
+ __esDecorate(this, null, _listenerWalk_decorators, { kind: "method", name: "listenerWalk", static: false, private: false, access: { has: obj => "listenerWalk" in obj, get: obj => obj.listenerWalk }, metadata: _metadata }, null, _instanceExtraInitializers);
164
+ __esDecorate(this, null, _contextAccessors_decorators, { kind: "method", name: "contextAccessors", static: false, private: false, access: { has: obj => "contextAccessors" in obj, get: obj => obj.contextAccessors }, metadata: _metadata }, null, _instanceExtraInitializers);
165
+ __esDecorate(this, null, _setterAssignment_decorators, { kind: "method", name: "setterAssignment", static: false, private: false, access: { has: obj => "setterAssignment" in obj, get: obj => obj.setterAssignment }, metadata: _metadata }, null, _instanceExtraInitializers);
166
+ __esDecorate(this, null, _mergePreservesExistingMeta_decorators, { kind: "method", name: "mergePreservesExistingMeta", static: false, private: false, access: { has: obj => "mergePreservesExistingMeta" in obj, get: obj => obj.mergePreservesExistingMeta }, metadata: _metadata }, null, _instanceExtraInitializers);
167
+ __esDecorate(this, null, _booleanLiterals_decorators, { kind: "method", name: "booleanLiterals", static: false, private: false, access: { has: obj => "booleanLiterals" in obj, get: obj => obj.booleanLiterals }, metadata: _metadata }, null, _instanceExtraInitializers);
168
+ __esDecorate(this, null, _booleanLiteralsFold_decorators, { kind: "method", name: "booleanLiteralsFold", static: false, private: false, access: { has: obj => "booleanLiteralsFold" in obj, get: obj => obj.booleanLiteralsFold }, metadata: _metadata }, null, _instanceExtraInitializers);
169
+ __esDecorate(this, null, _bareAtomsAreSyntaxErrors_decorators, { kind: "method", name: "bareAtomsAreSyntaxErrors", static: false, private: false, access: { has: obj => "bareAtomsAreSyntaxErrors" in obj, get: obj => obj.bareAtomsAreSyntaxErrors }, metadata: _metadata }, null, _instanceExtraInitializers);
170
+ __esDecorate(this, null, _partialValidatorWithBooleans_decorators, { kind: "method", name: "partialValidatorWithBooleans", static: false, private: false, access: { has: obj => "partialValidatorWithBooleans" in obj, get: obj => obj.partialValidatorWithBooleans }, metadata: _metadata }, null, _instanceExtraInitializers);
171
+ __esDecorate(this, null, _isNull_decorators, { kind: "method", name: "isNull", static: false, private: false, access: { has: obj => "isNull" in obj, get: obj => obj.isNull }, metadata: _metadata }, null, _instanceExtraInitializers);
172
+ __esDecorate(this, null, _isNullToString_decorators, { kind: "method", name: "isNullToString", static: false, private: false, access: { has: obj => "isNullToString" in obj, get: obj => obj.isNullToString }, metadata: _metadata }, null, _instanceExtraInitializers);
173
+ __esDecorate(this, null, _isNullPartial_decorators, { kind: "method", name: "isNullPartial", static: false, private: false, access: { has: obj => "isNullPartial" in obj, get: obj => obj.isNullPartial }, metadata: _metadata }, null, _instanceExtraInitializers);
174
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
175
+ QueryTest = _classThis = _classDescriptor.value;
176
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
177
+ __runInitializers(_classThis, _classExtraInitializers);
178
+ }
179
+ dev() {
180
+ const queryMap = {
181
+ "a = 1 AND b = 2 AND c = 3 AND d = 4": "a = 1 AND b = 2 AND c = 3 AND d = 4",
182
+ "a = 1 AND (b = 2 AND c = 3) AND d = 4": "a = 1 AND b = 2 AND c = 3 AND d = 4",
183
+ "a = 1 AND ((b = 2 AND c = 3) AND d = 4)": "a = 1 AND b = 2 AND c = 3 AND d = 4",
184
+ "a = 1 AND ((b = 2 OR c = 3) AND d = 4)": "a = 1 AND ( b = 2 OR c = 3 ) AND d = 4",
185
+ "a = 1 AND (b = 2) AND c = 3 AND d = 4": "a = 1 AND b = 2 AND c = 3 AND d = 4",
186
+ "a = 1 OR b = 2 OR c = 3 OR d = 4": "a = 1 OR b = 2 OR c = 3 OR d = 4",
187
+ 'a = 1 AND (b = 2 OR c = 3) AND d = "plop"': 'a = 1 AND ( b = 2 OR c = 3 ) AND d = "plop"',
188
+ "a = 1 OR b = TRUE AND c = FALSE OR d = 'plop'": 'a = 1 OR ( b = TRUE AND c = FALSE ) OR d = "plop"',
189
+ "test.attr1 = 'plop'": true,
190
+ "test.attr1 = 'plop2'": false,
191
+ "test.attr1 = 'plop' AND attr2 IN ['TEST', TRUE, 3]": 'test.attr1 = "plop" AND attr2 IN ["TEST", TRUE, 3]',
192
+ "(test.attr1 = 'plop' AND attr2 IN ['TEST', 'OK']) OR attr3 <= 12": null,
193
+ "test.attr1 = 'plop' AND attr2 IN ['TEST', 'OK'] OR attr3 <= 12 AND attr4 = 'ok'": null,
194
+ "test.attr1 = 'plop' AND (attr2 IN ['TEST', 'OK'] OR attr3 <= 12) AND attr4 = 'ok'": null,
195
+ "test.attr1 = 'plop' AND attr2 IN ['TEST', 'OK'] AND attr3 <= 12 AND attr4 = 'ok'": null,
196
+ "a = 1 OR b = 2": null,
197
+ "a = 1 AND b = 2 OR c = 3 AND d = 4": null,
198
+ "a = 1 AND b = 2 OR c = 3 AND d = 4 OR e = 5 AND f = 6": null,
199
+ "a = 1 AND (b = 2 OR (c = 3 AND (d = 4 OR e = 5))) AND f = 6": null,
200
+ "": true,
201
+ 'test.attr1 LIKE "pl_p"': true,
202
+ 'test.attr1 LIKE "pl__p"': false,
203
+ 'attr3 LIKE "1_"': true,
204
+ "attr3 >= 12": true,
205
+ "attr3 <= 12": false,
206
+ "attr3 != 12": true,
207
+ "attr3 > 12": true,
208
+ "attr3 < 13": false,
209
+ "attr5 CONTAINS 'test'": true,
210
+ "attr5 CONTAINS 'test2'": false,
211
+ 'test.attr1 LIKE "pl%"': true,
212
+ "a = 1 AND b=2 OR a=1 AND b=3": "( a = 1 AND b = 2 ) OR ( a = 1 AND b = 3 )",
213
+ "a = 1 ORDER BY a": null,
214
+ "a = 1 ORDER BY a DESC, b ASC": null,
215
+ "(attr3 >= 12)": true
216
+ };
217
+ for (const query in queryMap) {
218
+ const validator = new WebdaQL.QueryValidator(query);
219
+ assert.strictEqual(validator.displayTree().replace(/\s/g, ""), query.replace(/\s/g, ""));
220
+ if (typeof queryMap[query] === "string") {
221
+ assert.strictEqual(validator.getExpression().toString(), queryMap[query], `Failed optimization of query: ${query} => ${validator.getExpression().toString()}`);
222
+ }
223
+ else if (typeof queryMap[query] === "boolean") {
224
+ assert.strictEqual(validator.eval(targets[0]), queryMap[query], `Failed query: ${query}`);
225
+ }
226
+ else {
227
+ console.log("QUERY", query);
228
+ console.log("OPTIMIZED QUERY", validator.getExpression().toString(), "=>", validator.eval(targets[0]));
229
+ }
65
230
  }
66
- else if (typeof queryMap[query] === "boolean") {
67
- assert.strictEqual(validator.eval(targets[0]), queryMap[query], `Failed query: ${query}`);
231
+ }
232
+ prependQuery() {
233
+ // merge method is tested through the prependQuery helper
234
+ const strictEqual = (arg1, arg2) => {
235
+ // Ensure it is parsable
236
+ new WebdaQL.QueryValidator(arg1);
237
+ assert.deepStrictEqual(arg1, arg2);
238
+ };
239
+ strictEqual(WebdaQL.PrependCondition("", "test='plop'"), 'test = "plop"');
240
+ strictEqual(WebdaQL.PrependCondition("test='plop'", ""), 'test = "plop"');
241
+ strictEqual(WebdaQL.PrependCondition("test='plip'", "test='plop'"), 'test = "plip" AND test = "plop"');
242
+ strictEqual(WebdaQL.PrependCondition("ORDER BY test", "test='plop'"), 'test = "plop" ORDER BY test ASC');
243
+ strictEqual(WebdaQL.PrependCondition("ORDER BY test LIMIT 100", "test='plop'"), 'test = "plop" ORDER BY test ASC LIMIT 100');
244
+ strictEqual(WebdaQL.PrependCondition("ORDER BY test DESC LIMIT 100", "test='plop'"), 'test = "plop" ORDER BY test DESC LIMIT 100');
245
+ strictEqual(WebdaQL.PrependCondition("test='plip' ORDER BY test ASC LIMIT 100", "test='plop'"), 'test = "plip" AND test = "plop" ORDER BY test ASC LIMIT 100');
246
+ strictEqual(WebdaQL.PrependCondition("test='plip' LIMIT 100", "test='plop'"), 'test = "plip" AND test = "plop" LIMIT 100');
247
+ strictEqual(WebdaQL.PrependCondition("test='plip' OFFSET 'test'", "test='plop'"), 'test = "plip" AND test = "plop" OFFSET "test"');
248
+ strictEqual(WebdaQL.PrependCondition("test='ORDER BY plop' OFFSET 'test'", "test='plop'"), 'test = "ORDER BY plop" AND test = "plop" OFFSET "test"');
249
+ strictEqual(WebdaQL.PrependCondition('test="ORDER BY plop" LIMIT 100', "test='plop'"), 'test = "ORDER BY plop" AND test = "plop" LIMIT 100');
250
+ // Composed ORDER BY
251
+ strictEqual(WebdaQL.PrependCondition('test="plop" ORDER BY ab ASC LIMIT 100', "ORDER BY forced DESC"), 'test = "plop" ORDER BY forced DESC, ab ASC LIMIT 100');
252
+ // Opposite ORDER BY
253
+ strictEqual(WebdaQL.PrependCondition('test="plop" ORDER BY forced ASC LIMIT 100', "ORDER BY forced DESC"), 'test = "plop" ORDER BY forced DESC LIMIT 100');
254
+ // Overwrite LIMIT
255
+ strictEqual(WebdaQL.PrependCondition('test="plop" ORDER BY forced ASC LIMIT 100', "LIMIT 5"), 'test = "plop" ORDER BY forced ASC LIMIT 5');
256
+ // Overwrite OFFSET
257
+ strictEqual(WebdaQL.PrependCondition('OFFSET "test"', "OFFSET 'plop'"), 'OFFSET "plop"');
258
+ strictEqual(WebdaQL.PrependCondition('plop = "ok"', "OFFSET 'plop'"), 'plop = "ok" OFFSET "plop"');
259
+ }
260
+ likeRegexp() {
261
+ assert.deepStrictEqual(WebdaQL.ComparisonExpression.likeToRegex("test"), /test/);
262
+ assert.deepStrictEqual(WebdaQL.ComparisonExpression.likeToRegex("t_est"), /t.{1}est/);
263
+ assert.deepStrictEqual(WebdaQL.ComparisonExpression.likeToRegex("_test"), /.{1}test/);
264
+ assert.deepStrictEqual(WebdaQL.ComparisonExpression.likeToRegex("t\\_est"), /t_est/);
265
+ assert.deepStrictEqual(WebdaQL.ComparisonExpression.likeToRegex("t%est"), /t.*est/);
266
+ assert.deepStrictEqual(WebdaQL.ComparisonExpression.likeToRegex("%te?st"), /.*te\?st/);
267
+ assert.deepStrictEqual(WebdaQL.ComparisonExpression.likeToRegex("t\\%est"), /t%est/);
268
+ assert.deepStrictEqual(WebdaQL.ComparisonExpression.likeToRegex("t\\eest"), /t\\est/);
269
+ }
270
+ simple() {
271
+ assert.strictEqual(new WebdaQL.QueryValidator("test.attr1 = 'plop'").eval(targets[0]), true);
272
+ assert.strictEqual(new WebdaQL.QueryValidator("test.attr1 = 'plop2'").eval(targets[0]), false);
273
+ }
274
+ contains() {
275
+ assert.strictEqual(new WebdaQL.QueryValidator("test.attr1 CONTAINS 'plop2'").eval(targets[0]), false);
276
+ }
277
+ andQuery() {
278
+ assert.strictEqual(new WebdaQL.QueryValidator("test.attr1 = 'plop' AND attr2 IN ['TEST', 'OK']").eval(targets[0]), true);
279
+ }
280
+ orQuery() {
281
+ new WebdaQL.QueryValidator("(test.attr1 = 'plop' AND attr2 IN ['TEST', 'OK']) OR attr3 <= 12").eval(targets[0]);
282
+ }
283
+ multipleQuery() {
284
+ new WebdaQL.QueryValidator("test.attr1 = 'plop' AND attr2 IN ['TEST', 'OK'] OR attr3 <= 12 AND attr4 = 'ok'").eval(targets[0]);
285
+ new WebdaQL.QueryValidator("test.attr1 = 'plop' AND attr2 IN ['TEST', 'OK'] AND attr3 <= 12 AND attr4 = 'ok'").eval(targets[0]);
286
+ }
287
+ unsanitize() {
288
+ assert.strictEqual(WebdaQL.unsanitize("test.attr1 &lt; 12 AND attr2 &gt; 13"), "test.attr1 < 12 AND attr2 > 13");
289
+ }
290
+ limitOffset() {
291
+ let val = new WebdaQL.QueryValidator('LIMIT 10 OFFSET "pl"');
292
+ // getQuery
293
+ val.getQuery();
294
+ assert.strictEqual(val.getLimit(), 10);
295
+ assert.strictEqual(val.getOffset(), "pl");
296
+ val = new WebdaQL.QueryValidator('OFFSET "pl2"');
297
+ assert.strictEqual(val.getLimit(), 1000);
298
+ assert.strictEqual(val.getOffset(), "pl2");
299
+ }
300
+ badQuery() {
301
+ assert.throws(() => new WebdaQL.QueryValidator("test lo 'plop'").eval(targets[0]));
302
+ }
303
+ setters() {
304
+ let target = {};
305
+ new WebdaQL.SetterValidator('i = 10 AND j = "12"').eval(target);
306
+ assert.strictEqual(target.i, 10);
307
+ assert.strictEqual(target.j, "12");
308
+ target = {};
309
+ new WebdaQL.SetterValidator("").eval(target);
310
+ assert.strictEqual(Object.keys(target).length, 0);
311
+ new WebdaQL.SetterValidator('test.i = 10 AND j.k.l = "12"').eval(target);
312
+ assert.strictEqual(target.test.i, 10);
313
+ assert.strictEqual(target.j.k.l, "12");
314
+ target = {};
315
+ new WebdaQL.SetterValidator("test.__proto__.test = 10").eval(target);
316
+ assert.strictEqual(target.__proto__.test, undefined);
317
+ assert.throws(() => new WebdaQL.SetterValidator('i = 10 OR j = "12"').eval(target), SyntaxError);
318
+ assert.throws(() => new WebdaQL.SetterValidator('i > 10 AND j = "12"').eval(target), SyntaxError);
319
+ }
320
+ parse() {
321
+ // Basic query with filter, order, limit, offset
322
+ const query = WebdaQL.parse("status = 'active' AND age > 18 ORDER BY name DESC LIMIT 50 OFFSET 'token123'");
323
+ assert.ok(query.filter instanceof WebdaQL.AndExpression);
324
+ assert.strictEqual(query.limit, 50);
325
+ assert.strictEqual(query.continuationToken, "token123");
326
+ assert.deepStrictEqual(query.orderBy, [{ field: "name", direction: "DESC" }]);
327
+ // toString returns the original parse tree reconstruction
328
+ assert.ok(query.toString().includes("status"));
329
+ // Empty query
330
+ const empty = WebdaQL.parse("");
331
+ assert.ok(empty.filter instanceof WebdaQL.AndExpression);
332
+ assert.strictEqual(empty.filter.children.length, 0);
333
+ // Filter-only query
334
+ const simple = WebdaQL.parse("x = 1");
335
+ assert.ok(simple.filter.eval({ x: 1 }));
336
+ assert.ok(!simple.filter.eval({ x: 2 }));
337
+ }
338
+ partialValidator() {
339
+ const validator = new WebdaQL.PartialValidator("attr1 = 'plop' AND attr2 = 'ok'");
340
+ assert.ok(validator.eval({ attr1: "plop" }));
341
+ assert.ok(validator.wasPartialMatch());
342
+ assert.ok(!validator.eval({ attr1: "plop" }, false));
343
+ assert.ok(!validator.eval({ attr1: "plop2" }));
344
+ assert.ok(new WebdaQL.PartialValidator("attr1 = 'plop' AND attr2 LIKE '?ok' AND attr3 IN ['test'] AND attr4 CONTAINS 'plop'").eval({ attr1: "plop" }));
345
+ }
346
+ mergeTypeMismatch() {
347
+ // AND filter merged with OR type — should wrap in OrExpression
348
+ const v1 = new WebdaQL.QueryValidator("a = 1 AND b = 2");
349
+ v1.merge("c = 3", "OR");
350
+ const expr1 = v1.getExpression();
351
+ assert.ok(expr1 instanceof WebdaQL.OrExpression);
352
+ // OR filter merged with AND type — should wrap in AndExpression
353
+ const v2 = new WebdaQL.QueryValidator("a = 1 OR b = 2");
354
+ v2.merge("c = 3", "AND");
355
+ const expr2 = v2.getExpression();
356
+ assert.ok(expr2 instanceof WebdaQL.AndExpression);
357
+ }
358
+ getOffsetFallback() {
359
+ // Query without OFFSET should return ""
360
+ const v = new WebdaQL.QueryValidator("a = 1");
361
+ assert.strictEqual(v.getOffset(), "");
362
+ // Query with OFFSET
363
+ const v2 = new WebdaQL.QueryValidator('OFFSET "tok"');
364
+ assert.strictEqual(v2.getOffset(), "tok");
365
+ }
366
+ expressionToString() {
367
+ // Test toString at different depths
368
+ const v = new WebdaQL.QueryValidator("a = 1 AND (b = 2 OR c = 3)");
369
+ const str = v.getExpression().toString();
370
+ assert.ok(str.includes("a = 1"));
371
+ // Empty AND/OR toString
372
+ const emptyAnd = new WebdaQL.AndExpression([]);
373
+ assert.strictEqual(emptyAnd.toString(), "");
374
+ assert.strictEqual(emptyAnd.eval({}), true);
375
+ const emptyOr = new WebdaQL.OrExpression([]);
376
+ assert.strictEqual(emptyOr.toString(), "");
377
+ assert.strictEqual(emptyOr.eval({}), true);
378
+ // Test ComparisonExpression toString with various operators
379
+ const comp = new WebdaQL.ComparisonExpression("!=", "field", "test");
380
+ assert.strictEqual(comp.toString(), 'field != "test"');
381
+ // Boolean values in toString
382
+ const boolComp = new WebdaQL.ComparisonExpression("=", "field", true);
383
+ assert.strictEqual(boolComp.toString(), "field = TRUE");
384
+ // Array values in toString
385
+ const arrComp = new WebdaQL.ComparisonExpression("IN", "field", ["a", 1, true]);
386
+ assert.ok(arrComp.toString().includes("IN"));
387
+ }
388
+ hasCondition() {
389
+ const v1 = new WebdaQL.QueryValidator("a = 1");
390
+ assert.ok(v1.hasCondition());
391
+ const v2 = new WebdaQL.QueryValidator("");
392
+ assert.ok(!v2.hasCondition());
393
+ const v3 = new WebdaQL.QueryValidator("ORDER BY a");
394
+ assert.ok(!v3.hasCondition());
395
+ }
396
+ queryValidatorToString() {
397
+ const v = new WebdaQL.QueryValidator("a = 1 ORDER BY b LIMIT 10");
398
+ const str = v.toString();
399
+ assert.ok(str.includes("a = 1"));
400
+ assert.ok(str.includes("ORDER BY"));
401
+ assert.ok(str.includes("LIMIT 10"));
402
+ }
403
+ complexQueries() {
404
+ // Nested parentheses
405
+ new WebdaQL.QueryValidator("(a = 1 OR b = 2) AND (c = 3 OR d = 4)").eval({ a: 1, c: 3 });
406
+ // Multiple IN values
407
+ const v = new WebdaQL.QueryValidator("field IN ['a', 'b', 'c', 'd']");
408
+ assert.ok(v.eval({ field: "b" }));
409
+ assert.ok(!v.eval({ field: "e" }));
410
+ // Multiple ORDER BY
411
+ const v2 = new WebdaQL.QueryValidator("a = 1 ORDER BY x DESC, y ASC, z DESC");
412
+ assert.strictEqual(v2.toString().includes("ORDER BY"), true);
413
+ // LIMIT + OFFSET + ORDER BY together
414
+ const v3 = new WebdaQL.QueryValidator('a = 1 ORDER BY b LIMIT 5 OFFSET "page2"');
415
+ assert.strictEqual(v3.getOffset(), "page2");
416
+ // Numeric comparisons
417
+ assert.ok(new WebdaQL.QueryValidator("x > 5").eval({ x: 10 }));
418
+ assert.ok(new WebdaQL.QueryValidator("x >= 10").eval({ x: 10 }));
419
+ assert.ok(new WebdaQL.QueryValidator("x < 10").eval({ x: 5 }));
420
+ assert.ok(new WebdaQL.QueryValidator("x <= 10").eval({ x: 10 }));
421
+ // FALSE boolean
422
+ assert.ok(new WebdaQL.QueryValidator("x = FALSE").eval({ x: false }));
423
+ }
424
+ containsNonArray() {
425
+ // CONTAINS on non-array should return false
426
+ assert.ok(!new WebdaQL.QueryValidator("x CONTAINS 'a'").eval({ x: "hello" }));
427
+ assert.ok(!new WebdaQL.QueryValidator("x CONTAINS 'a'").eval({ x: 42 }));
428
+ }
429
+ likeOnNonString() {
430
+ // LIKE on numeric value should toString and match
431
+ assert.ok(new WebdaQL.QueryValidator("x LIKE '1%'").eval({ x: 123 }));
432
+ }
433
+ likeOnMissingAttribute() {
434
+ // A missing or null attribute never matches instead of throwing
435
+ assert.ok(!new WebdaQL.QueryValidator("x LIKE '%a%'").eval({}));
436
+ assert.ok(!new WebdaQL.QueryValidator("x LIKE '%a%'").eval({ x: null }));
437
+ assert.ok(new WebdaQL.QueryValidator("x LIKE '%a%' OR y LIKE '%a%'").eval({ y: "abc" }));
438
+ }
439
+ quotedLiterals() {
440
+ // Doubled quotes (what escapeValue produces) and backslash-escaped quotes are unescaped
441
+ assert.ok(new WebdaQL.QueryValidator("k = 'it''s'").eval({ k: "it's" }));
442
+ assert.ok(new WebdaQL.QueryValidator('k = "say ""hi"""').eval({ k: 'say "hi"' }));
443
+ assert.ok(new WebdaQL.QueryValidator("k = 'it\\'s'").eval({ k: "it's" }));
444
+ assert.strictEqual(WebdaQL.unescapeStringLiteral("'a''b'"), "a'b");
445
+ // Backslash handling
446
+ assert.ok(new WebdaQL.QueryValidator("k = 'a\\\\b'").eval({ k: "a\\b" }));
447
+ assert.strictEqual(WebdaQL.unescapeStringLiteral("'a\\\\''b'"), "a\\'b");
448
+ // LIKE escapes are not touched
449
+ assert.ok(new WebdaQL.QueryValidator("k LIKE '100\\%'").eval({ k: "100%" }));
450
+ assert.ok(!new WebdaQL.QueryValidator("k LIKE '100\\%'").eval({ k: "1000" }));
451
+ }
452
+ lexerIntrospection() {
453
+ const lexer = new WebdaQLLexer(CharStreams.fromString("a = 1"));
454
+ assert.ok(lexer.grammarFileName);
455
+ assert.ok(Array.isArray(lexer.ruleNames));
456
+ assert.ok(typeof lexer.serializedATN === "string");
457
+ assert.ok(Array.isArray(lexer.channelNames));
458
+ assert.ok(Array.isArray(lexer.modeNames));
459
+ }
460
+ parserIntrospection() {
461
+ const lexer = new WebdaQLLexer(CharStreams.fromString("a = 1"));
462
+ const tokenStream = new CommonTokenStream(lexer);
463
+ const parser = new WebdaQLParserParser(tokenStream);
464
+ assert.ok(parser.vocabulary);
465
+ assert.strictEqual(parser.grammarFileName, "WebdaQLParser.g4");
466
+ assert.ok(Array.isArray(parser.ruleNames));
467
+ assert.ok(typeof parser.serializedATN === "string");
468
+ }
469
+ listenerWalk() {
470
+ const lexer = new WebdaQLLexer(CharStreams.fromString('a = 1 AND b LIKE "test%" AND c IN [1, "x", TRUE] AND d CONTAINS "v" AND (e = 2 OR f = 3) ORDER BY g DESC LIMIT 10 OFFSET "tok"'));
471
+ const tokenStream = new CommonTokenStream(lexer);
472
+ const parser = new WebdaQLParserParser(tokenStream);
473
+ const tree = parser.webdaql();
474
+ const visited = [];
475
+ const listener = {};
476
+ // Dynamically add all enter/exit methods to track visits
477
+ for (const rule of parser.ruleNames) {
478
+ const capitalized = rule.charAt(0).toUpperCase() + rule.slice(1);
479
+ listener[`enter${capitalized}`] = () => visited.push(`enter${capitalized}`);
480
+ listener[`exit${capitalized}`] = () => visited.push(`exit${capitalized}`);
68
481
  }
69
- else {
70
- console.log("QUERY", query);
71
- console.log("OPTIMIZED QUERY", validator.getExpression().toString(), "=>", validator.eval(targets[0]));
482
+ ParseTreeWalker.DEFAULT.walk(listener, tree);
483
+ assert.ok(visited.includes("enterWebdaql"));
484
+ assert.ok(visited.includes("exitWebdaql"));
485
+ assert.ok(visited.length > 20);
486
+ }
487
+ contextAccessors() {
488
+ // Exercise parser context accessor methods
489
+ const lexer = new WebdaQLLexer(CharStreams.fromString("a = 1 AND b IN [1, 2] ORDER BY c DESC"));
490
+ const tokenStream = new CommonTokenStream(lexer);
491
+ const parser = new WebdaQLParserParser(tokenStream);
492
+ const tree = parser.webdaql();
493
+ // Access children of the parse tree
494
+ assert.ok(tree.expression());
495
+ const orderExpr = tree.orderExpression();
496
+ assert.ok(orderExpr);
497
+ assert.strictEqual(tree.limitExpression(), undefined);
498
+ // Exercise ruleIndex
499
+ assert.strictEqual(typeof tree.ruleIndex, "number");
500
+ }
501
+ setterAssignment() {
502
+ // Test SetterValidator assignments
503
+ const setter = new WebdaQL.SetterValidator("a = 1 AND b = 'hello' AND c = TRUE");
504
+ const target = { a: 0, b: "", c: false };
505
+ setter.eval(target);
506
+ assert.strictEqual(target.a, 1);
507
+ assert.strictEqual(target.b, "hello");
508
+ assert.strictEqual(target.c, true);
509
+ }
510
+ mergePreservesExistingMeta() {
511
+ // Same-type merge (AND + AND) should push into existing children
512
+ const v = new WebdaQL.QueryValidator("a = 1 AND b = 2");
513
+ v.merge("c = 3", "AND");
514
+ const expr = v.getExpression();
515
+ assert.ok(expr instanceof WebdaQL.AndExpression);
516
+ assert.strictEqual(expr.children.length, 3);
517
+ }
518
+ booleanLiterals() {
519
+ const check = (query, target = {}) => new WebdaQL.QueryValidator(query).eval(target);
520
+ assert.strictEqual(check("TRUE"), true);
521
+ assert.strictEqual(check("FALSE"), false);
522
+ assert.strictEqual(check("(TRUE)"), true);
523
+ assert.strictEqual(check("TRUE AND x = 1", { x: 1 }), true);
524
+ assert.strictEqual(check("TRUE AND x = 1", { x: 2 }), false);
525
+ assert.strictEqual(check("FALSE OR x = 1", { x: 1 }), true);
526
+ assert.strictEqual(check("FALSE AND x = 1", { x: 1 }), false);
527
+ assert.strictEqual(check("TRUE OR x = 1", { x: 2 }), true);
528
+ assert.strictEqual(check("x = 1 OR (y = 2 AND FALSE)", { y: 2 }), false);
529
+ }
530
+ booleanLiteralsFold() {
531
+ // Store query translators only ever see the existing expression classes
532
+ const all = WebdaQL.parse("TRUE").filter;
533
+ assert.ok(all instanceof WebdaQL.AndExpression);
534
+ assert.strictEqual(all.children.length, 0);
535
+ assert.strictEqual(WebdaQL.parse("TRUE AND x = 1").filter.toString(), "x = 1");
536
+ assert.ok(WebdaQL.parse("TRUE OR x = 1").filter instanceof WebdaQL.AndExpression);
537
+ const none = WebdaQL.parse("FALSE").filter;
538
+ assert.ok(none instanceof WebdaQL.BooleanExpression);
539
+ assert.strictEqual(none.toString(), "FALSE");
540
+ assert.strictEqual(WebdaQL.parse("x = 1 AND FALSE").filter.toString(), "FALSE");
541
+ }
542
+ bareAtomsAreSyntaxErrors() {
543
+ assert.throws(() => new WebdaQL.QueryValidator("active"), SyntaxError);
544
+ assert.throws(() => new WebdaQL.QueryValidator("42"), SyntaxError);
545
+ assert.throws(() => new WebdaQL.QueryValidator("x = 1 AND 'a'"), SyntaxError);
546
+ }
547
+ partialValidatorWithBooleans() {
548
+ // A missing attribute is a partial match, TRUE is folded away
549
+ assert.ok(new WebdaQL.PartialValidator("TRUE AND x = 1").eval({}));
550
+ assert.ok(!new WebdaQL.PartialValidator("FALSE").eval({}));
551
+ }
552
+ isNull() {
553
+ const target = { a: null, b: "x", c: 0, d: false, e: "", nested: { f: null, g: 1 } };
554
+ const cases = {
555
+ "a IS NULL": true,
556
+ "a IS NOT NULL": false,
557
+ "missing IS NULL": true,
558
+ "missing IS NOT NULL": false,
559
+ "b IS NULL": false,
560
+ "b IS NOT NULL": true,
561
+ // Falsy values are not null
562
+ "c IS NOT NULL": true,
563
+ "d IS NOT NULL": true,
564
+ "e IS NOT NULL": true,
565
+ "nested.f IS NULL": true,
566
+ "nested.g IS NOT NULL": true,
567
+ "nested.missing.deep IS NULL": true,
568
+ "a IS NULL AND b IS NOT NULL": true,
569
+ "a IS NOT NULL OR b = 'x'": true
570
+ };
571
+ for (const [query, expected] of Object.entries(cases)) {
572
+ assert.strictEqual(new WebdaQL.QueryValidator(query).eval(target), expected, query);
72
573
  }
574
+ // Keywords only win on an exact match: identifiers starting with them still parse
575
+ assert.ok(new WebdaQL.QueryValidator("notes IS NOT NULL AND isActive = TRUE AND nullable IS NULL").eval({
576
+ notes: "n",
577
+ isActive: true
578
+ }));
579
+ // NULL is not a value
580
+ assert.throws(() => new WebdaQL.QueryValidator("a = NULL"), SyntaxError);
581
+ assert.throws(() => new WebdaQL.QueryValidator("a IS 'x'"), SyntaxError);
73
582
  }
74
- }
75
- prependQuery() {
76
- // merge method is tested through the prependQuery helper
77
- const strictEqual = (arg1, arg2) => {
78
- // Ensure it is parsable
79
- new WebdaQL.QueryValidator(arg1);
80
- assert.deepStrictEqual(arg1, arg2);
81
- };
82
- strictEqual(WebdaQL.PrependCondition("", "test='plop'"), 'test = "plop"');
83
- strictEqual(WebdaQL.PrependCondition("test='plop'", ""), 'test = "plop"');
84
- strictEqual(WebdaQL.PrependCondition("test='plip'", "test='plop'"), 'test = "plip" AND test = "plop"');
85
- strictEqual(WebdaQL.PrependCondition("ORDER BY test", "test='plop'"), 'test = "plop" ORDER BY test ASC');
86
- strictEqual(WebdaQL.PrependCondition("ORDER BY test LIMIT 100", "test='plop'"), 'test = "plop" ORDER BY test ASC LIMIT 100');
87
- strictEqual(WebdaQL.PrependCondition("ORDER BY test DESC LIMIT 100", "test='plop'"), 'test = "plop" ORDER BY test DESC LIMIT 100');
88
- strictEqual(WebdaQL.PrependCondition("test='plip' ORDER BY test ASC LIMIT 100", "test='plop'"), 'test = "plip" AND test = "plop" ORDER BY test ASC LIMIT 100');
89
- strictEqual(WebdaQL.PrependCondition("test='plip' LIMIT 100", "test='plop'"), 'test = "plip" AND test = "plop" LIMIT 100');
90
- strictEqual(WebdaQL.PrependCondition("test='plip' OFFSET 'test'", "test='plop'"), 'test = "plip" AND test = "plop" OFFSET "test"');
91
- strictEqual(WebdaQL.PrependCondition("test='ORDER BY plop' OFFSET 'test'", "test='plop'"), 'test = "ORDER BY plop" AND test = "plop" OFFSET "test"');
92
- strictEqual(WebdaQL.PrependCondition('test="ORDER BY plop" LIMIT 100', "test='plop'"), 'test = "ORDER BY plop" AND test = "plop" LIMIT 100');
93
- // Composed ORDER BY
94
- strictEqual(WebdaQL.PrependCondition('test="plop" ORDER BY ab ASC LIMIT 100', "ORDER BY forced DESC"), 'test = "plop" ORDER BY forced DESC, ab ASC LIMIT 100');
95
- // Opposite ORDER BY
96
- strictEqual(WebdaQL.PrependCondition('test="plop" ORDER BY forced ASC LIMIT 100', "ORDER BY forced DESC"), 'test = "plop" ORDER BY forced DESC LIMIT 100');
97
- // Overwrite LIMIT
98
- strictEqual(WebdaQL.PrependCondition('test="plop" ORDER BY forced ASC LIMIT 100', "LIMIT 5"), 'test = "plop" ORDER BY forced ASC LIMIT 5');
99
- // Overwrite OFFSET
100
- strictEqual(WebdaQL.PrependCondition('OFFSET "test"', "OFFSET 'plop'"), 'OFFSET "plop"');
101
- strictEqual(WebdaQL.PrependCondition('plop = "ok"', "OFFSET 'plop'"), 'plop = "ok" OFFSET "plop"');
102
- }
103
- likeRegexp() {
104
- assert.deepStrictEqual(WebdaQL.ComparisonExpression.likeToRegex("test"), /test/);
105
- assert.deepStrictEqual(WebdaQL.ComparisonExpression.likeToRegex("t_est"), /t.{1}est/);
106
- assert.deepStrictEqual(WebdaQL.ComparisonExpression.likeToRegex("_test"), /.{1}test/);
107
- assert.deepStrictEqual(WebdaQL.ComparisonExpression.likeToRegex("t\\_est"), /t_est/);
108
- assert.deepStrictEqual(WebdaQL.ComparisonExpression.likeToRegex("t%est"), /t.*est/);
109
- assert.deepStrictEqual(WebdaQL.ComparisonExpression.likeToRegex("%te?st"), /.*te\?st/);
110
- assert.deepStrictEqual(WebdaQL.ComparisonExpression.likeToRegex("t\\%est"), /t%est/);
111
- assert.deepStrictEqual(WebdaQL.ComparisonExpression.likeToRegex("t\\eest"), /t\\est/);
112
- }
113
- simple() {
114
- assert.strictEqual(new WebdaQL.QueryValidator("test.attr1 = 'plop'").eval(targets[0]), true);
115
- assert.strictEqual(new WebdaQL.QueryValidator("test.attr1 = 'plop2'").eval(targets[0]), false);
116
- }
117
- contains() {
118
- assert.strictEqual(new WebdaQL.QueryValidator("test.attr1 CONTAINS 'plop2'").eval(targets[0]), false);
119
- }
120
- andQuery() {
121
- assert.strictEqual(new WebdaQL.QueryValidator("test.attr1 = 'plop' AND attr2 IN ['TEST', 'OK']").eval(targets[0]), true);
122
- }
123
- orQuery() {
124
- new WebdaQL.QueryValidator("(test.attr1 = 'plop' AND attr2 IN ['TEST', 'OK']) OR attr3 <= 12").eval(targets[0]);
125
- }
126
- multipleQuery() {
127
- new WebdaQL.QueryValidator("test.attr1 = 'plop' AND attr2 IN ['TEST', 'OK'] OR attr3 <= 12 AND attr4 = 'ok'").eval(targets[0]);
128
- new WebdaQL.QueryValidator("test.attr1 = 'plop' AND attr2 IN ['TEST', 'OK'] AND attr3 <= 12 AND attr4 = 'ok'").eval(targets[0]);
129
- }
130
- unsanitize() {
131
- assert.strictEqual(WebdaQL.unsanitize("test.attr1 &lt; 12 AND attr2 &gt; 13"), "test.attr1 < 12 AND attr2 > 13");
132
- }
133
- limitOffset() {
134
- let val = new WebdaQL.QueryValidator('LIMIT 10 OFFSET "pl"');
135
- // getQuery
136
- val.getQuery();
137
- assert.strictEqual(val.getLimit(), 10);
138
- assert.strictEqual(val.getOffset(), "pl");
139
- val = new WebdaQL.QueryValidator('OFFSET "pl2"');
140
- assert.strictEqual(val.getLimit(), 1000);
141
- assert.strictEqual(val.getOffset(), "pl2");
142
- }
143
- badQuery() {
144
- assert.throws(() => new WebdaQL.QueryValidator("test lo 'plop'").eval(targets[0]));
145
- }
146
- setters() {
147
- let target = {};
148
- new WebdaQL.SetterValidator('i = 10 AND j = "12"').eval(target);
149
- assert.strictEqual(target.i, 10);
150
- assert.strictEqual(target.j, "12");
151
- target = {};
152
- new WebdaQL.SetterValidator("").eval(target);
153
- assert.strictEqual(Object.keys(target).length, 0);
154
- new WebdaQL.SetterValidator('test.i = 10 AND j.k.l = "12"').eval(target);
155
- assert.strictEqual(target.test.i, 10);
156
- assert.strictEqual(target.j.k.l, "12");
157
- target = {};
158
- new WebdaQL.SetterValidator("test.__proto__.test = 10").eval(target);
159
- assert.strictEqual(target.__proto__.test, undefined);
160
- assert.throws(() => new WebdaQL.SetterValidator('i = 10 OR j = "12"').eval(target), SyntaxError);
161
- assert.throws(() => new WebdaQL.SetterValidator('i > 10 AND j = "12"').eval(target), SyntaxError);
162
- }
163
- partialValidator() {
164
- const validator = new WebdaQL.PartialValidator("attr1 = 'plop' AND attr2 = 'ok'");
165
- assert.ok(validator.eval({ attr1: "plop" }));
166
- assert.ok(validator.wasPartialMatch());
167
- assert.ok(!validator.eval({ attr1: "plop" }, false));
168
- assert.ok(!validator.eval({ attr1: "plop2" }));
169
- assert.ok(new WebdaQL.PartialValidator("attr1 = 'plop' AND attr2 LIKE '?ok' AND attr3 IN ['test'] AND attr4 CONTAINS 'plop'").eval({ attr1: "plop" }));
170
- }
171
- };
172
- __decorate([
173
- test
174
- ], QueryTest.prototype, "dev", null);
175
- __decorate([
176
- test
177
- ], QueryTest.prototype, "prependQuery", null);
178
- __decorate([
179
- test
180
- ], QueryTest.prototype, "likeRegexp", null);
181
- __decorate([
182
- test
183
- ], QueryTest.prototype, "simple", null);
184
- __decorate([
185
- test
186
- ], QueryTest.prototype, "contains", null);
187
- __decorate([
188
- test
189
- ], QueryTest.prototype, "andQuery", null);
190
- __decorate([
191
- test
192
- ], QueryTest.prototype, "orQuery", null);
193
- __decorate([
194
- test
195
- ], QueryTest.prototype, "multipleQuery", null);
196
- __decorate([
197
- test
198
- ], QueryTest.prototype, "unsanitize", null);
199
- __decorate([
200
- test
201
- ], QueryTest.prototype, "limitOffset", null);
202
- __decorate([
203
- test
204
- ], QueryTest.prototype, "badQuery", null);
205
- __decorate([
206
- test
207
- ], QueryTest.prototype, "setters", null);
208
- __decorate([
209
- test
210
- ], QueryTest.prototype, "partialValidator", null);
211
- QueryTest = __decorate([
212
- suite
213
- ], QueryTest);
583
+ isNullToString() {
584
+ const expression = new WebdaQL.QueryValidator("a IS NULL AND b.c IS NOT NULL").getExpression();
585
+ assert.strictEqual(expression.toString(), "a IS NULL AND b.c IS NOT NULL");
586
+ const comparison = expression.children[0];
587
+ assert.strictEqual(comparison.operator, "IS NULL");
588
+ assert.deepStrictEqual(comparison.attribute, ["a"]);
589
+ // The printed query parses back to the same expression
590
+ assert.strictEqual(new WebdaQL.QueryValidator(expression.toString()).getExpression().toString(), expression.toString());
591
+ // Merging keeps the operators printable and parsable
592
+ assert.strictEqual(WebdaQL.PrependCondition("a IS NULL ORDER BY a", "b IS NOT NULL"), "a IS NULL AND b IS NOT NULL ORDER BY a ASC");
593
+ const merged = new WebdaQL.QueryValidator("a IS NULL").merge("b IS NOT NULL", "OR").toString();
594
+ assert.strictEqual(merged, "a IS NULL OR b IS NOT NULL");
595
+ assert.ok(new WebdaQL.QueryValidator(merged).eval({ a: null }));
596
+ }
597
+ isNullPartial() {
598
+ // A missing attribute is a partial match
599
+ const validator = new WebdaQL.PartialValidator("a IS NOT NULL AND b IS NULL");
600
+ assert.ok(validator.eval({}));
601
+ assert.ok(validator.wasPartialMatch());
602
+ assert.ok(!validator.eval({ a: null, b: null }));
603
+ assert.ok(validator.eval({ a: 1, b: null }));
604
+ assert.ok(!validator.eval({ a: 1, b: 2 }));
605
+ }
606
+ constructor() {
607
+ __runInitializers(this, _instanceExtraInitializers);
608
+ }
609
+ };
610
+ return QueryTest = _classThis;
611
+ })();
214
612
  //# sourceMappingURL=query.spec.js.map