@osqd/jql 0.1.1 → 0.1.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +44 -1
  2. package/conformance/cases.json +2 -0
  3. package/dist/chunk-2ZMIVDES.cjs +1661 -0
  4. package/dist/chunk-2ZMIVDES.cjs.map +1 -0
  5. package/dist/chunk-6PWYYYWU.js +1637 -0
  6. package/dist/chunk-6PWYYYWU.js.map +1 -0
  7. package/dist/chunk-74QKHM6Y.cjs +145 -0
  8. package/dist/chunk-74QKHM6Y.cjs.map +1 -0
  9. package/dist/chunk-ATRXHPBJ.js +112 -0
  10. package/dist/chunk-ATRXHPBJ.js.map +1 -0
  11. package/dist/chunk-EJFHVGBC.cjs +63 -0
  12. package/dist/chunk-EJFHVGBC.cjs.map +1 -0
  13. package/dist/chunk-ITPY6VTV.cjs +115 -0
  14. package/dist/chunk-ITPY6VTV.cjs.map +1 -0
  15. package/dist/chunk-JK6OYDLX.js +712 -0
  16. package/dist/chunk-JK6OYDLX.js.map +1 -0
  17. package/dist/chunk-N7DS4QNW.js +60 -0
  18. package/dist/chunk-N7DS4QNW.js.map +1 -0
  19. package/dist/chunk-PNBQ2HIJ.js +133 -0
  20. package/dist/chunk-PNBQ2HIJ.js.map +1 -0
  21. package/dist/chunk-VMFEQXBB.js +578 -0
  22. package/dist/chunk-VMFEQXBB.js.map +1 -0
  23. package/dist/cjs/text/index.d.ts +1 -1
  24. package/dist/cjs/text/parse.d.ts +2 -1
  25. package/dist/cjs/text/quote.d.ts +17 -0
  26. package/dist/cjs/text/suggest.d.ts +27 -5
  27. package/dist/cli.js +13 -2395
  28. package/dist/cli.js.map +1 -1
  29. package/dist/global.cjs +26 -1849
  30. package/dist/global.cjs.map +1 -1
  31. package/dist/global.js +8 -1831
  32. package/dist/global.js.map +1 -1
  33. package/dist/index.cjs +143 -1996
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.js +5 -2493
  36. package/dist/index.js.map +1 -1
  37. package/dist/mongo.cjs +20 -119
  38. package/dist/mongo.cjs.map +1 -1
  39. package/dist/mongo.js +2 -101
  40. package/dist/mongo.js.map +1 -1
  41. package/dist/text/index.d.ts +1 -1
  42. package/dist/text/parse.d.ts +2 -1
  43. package/dist/text/quote.d.ts +17 -0
  44. package/dist/text/suggest.d.ts +27 -5
  45. package/dist/text.cjs +146 -101
  46. package/dist/text.cjs.map +1 -1
  47. package/dist/text.js +3 -665
  48. package/dist/text.js.map +1 -1
  49. package/docs/course/11-the-search-box.md +49 -2
  50. package/docs/course/16-extending.md +1 -1
  51. package/docs/reference/api.md +14 -1
  52. package/docs/reference/specification.md +9 -2
  53. package/docs/reference/text-syntax.md +41 -6
  54. package/package.json +2 -2
package/dist/mongo.cjs CHANGED
@@ -1,106 +1,7 @@
1
1
  'use strict';
2
2
 
3
- // src/errors.ts
4
- var JqlError = class extends Error {
5
- /**
6
- * Where in the query the problem is, written the way you would reach it in code:
7
- * `$or[1].age.$gt`. Empty for the query as a whole.
8
- */
9
- at;
10
- constructor(message, at = "") {
11
- super(at === "" ? message : `at ${show(at)}: ${message}`);
12
- this.name = "JqlError";
13
- this.at = at;
14
- }
15
- };
16
- function show(at) {
17
- return at.length > 80 ? `${at.slice(0, 80)}\u2026` : at;
18
- }
19
-
20
- // src/internal/duration.ts
21
- var PART = /(\d+)(ms|s|m|h|d|w)/g;
22
- var SHAPE = /^(?:\d+(?:ms|s|m|h|d|w))+$/;
23
- var UNITS = {
24
- ms: 1,
25
- s: 1e3,
26
- m: 6e4,
27
- h: 36e5,
28
- d: 864e5,
29
- w: 6048e5
30
- };
31
- function parseDuration(text) {
32
- if (!SHAPE.test(text)) return void 0;
33
- let total = 0;
34
- PART.lastIndex = 0;
35
- for (; ; ) {
36
- const part = PART.exec(text);
37
- if (part === null) break;
38
- const count = Number(part[1]);
39
- if (!Number.isSafeInteger(count)) return void 0;
40
- total += count * UNITS[part[2]];
41
- }
42
- return Number.isSafeInteger(total) ? total : void 0;
43
- }
44
-
45
- // src/internal/values.ts
46
- function isPlainObject(value) {
47
- if (value === null || typeof value !== "object") return false;
48
- const prototype = Object.getPrototypeOf(value);
49
- return prototype === Object.prototype || prototype === null;
50
- }
51
- function isDateLiteral(value) {
52
- if (!isPlainObject(value) || !("$date" in value)) return false;
53
- for (const key in value) if (key !== "$date") return false;
54
- return true;
55
- }
56
- function resolveDate(value, now) {
57
- if (typeof value === "number") return value;
58
- if (typeof value === "string") return value === "now" ? now : instant(value);
59
- if (isPlainObject(value)) {
60
- const keys = Object.keys(value);
61
- if (keys.length !== 1) return Number.NaN;
62
- const amount = keys[0] === "$ago" || keys[0] === "$ahead" ? value[keys[0]] : void 0;
63
- if (typeof amount !== "string") return Number.NaN;
64
- const span = parseDuration(amount);
65
- if (span === void 0) return Number.NaN;
66
- return keys[0] === "$ago" ? now - span : now + span;
67
- }
68
- return Number.NaN;
69
- }
70
- var NO_OFFSET = /^(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?)$/;
71
- function instant(text) {
72
- const bare = NO_OFFSET.exec(text);
73
- return bare === null ? Date.parse(text) : Date.parse(`${bare[1]}T${bare[2]}Z`);
74
- }
75
-
76
- // src/internal/glob.ts
77
- var ANY = { any: true };
78
- var ONE = { one: true };
79
- function compileGlobPattern(pattern2, quoteLiteral2) {
80
- return tokenize(pattern2).map((token) => token === ANY ? "[\\s\\S]*" : token === ONE ? "[\\s\\S]" : quoteLiteral2(token)).join("");
81
- }
82
- function tokenize(pattern2) {
83
- const tokens = [];
84
- let escaped = false;
85
- for (const character of pattern2) {
86
- if (escaped) {
87
- tokens.push(character);
88
- escaped = false;
89
- continue;
90
- }
91
- if (character === "\\") {
92
- escaped = true;
93
- continue;
94
- }
95
- if (character === "*") {
96
- if (tokens[tokens.length - 1] !== ANY) tokens.push(ANY);
97
- continue;
98
- }
99
- tokens.push(character === "?" ? ONE : character);
100
- }
101
- if (escaped) tokens.push("\\");
102
- return tokens;
103
- }
3
+ var chunkITPY6VTV_cjs = require('./chunk-ITPY6VTV.cjs');
4
+ var chunk74QKHM6Y_cjs = require('./chunk-74QKHM6Y.cjs');
104
5
 
105
6
  // src/targets/mongo.ts
106
7
  var MONGO_CAPABILITIES = Object.freeze({
@@ -162,14 +63,14 @@ function pushable(condition) {
162
63
  }
163
64
  function composite(value) {
164
65
  if (Array.isArray(value)) return value.some(composite);
165
- return isPlainObject(value) && !isDateLiteral(value);
66
+ return chunk74QKHM6Y_cjs.isPlainObject(value) && !chunk74QKHM6Y_cjs.isDateLiteral(value);
166
67
  }
167
68
  function toMongoFilter(query, options = {}) {
168
69
  const now = options.now === void 0 ? Date.now() : options.now();
169
70
  return translateQuery(query, { vocabulary: options.vocabulary, now }, "");
170
71
  }
171
72
  function translateQuery(query, context, at) {
172
- if (!isPlainObject(query)) throw new JqlError(`a query is an object, not ${typeof query}`, at);
73
+ if (!chunk74QKHM6Y_cjs.isPlainObject(query)) throw new chunk74QKHM6Y_cjs.JqlError(`a query is an object, not ${typeof query}`, at);
173
74
  const parts = [];
174
75
  for (const [key, value] of Object.entries(query)) {
175
76
  const here = at === "" ? key : `${at}.${key}`;
@@ -179,7 +80,7 @@ function translateQuery(query, context, at) {
179
80
  case "$and":
180
81
  case "$or":
181
82
  case "$nor": {
182
- if (!Array.isArray(value)) throw new JqlError(`${key} takes a list of queries`, here);
83
+ if (!Array.isArray(value)) throw new chunk74QKHM6Y_cjs.JqlError(`${key} takes a list of queries`, here);
183
84
  parts.push({ [key]: value.map((part, index) => translateQuery(part, context, `${here}[${index}]`)) });
184
85
  break;
185
86
  }
@@ -187,7 +88,7 @@ function translateQuery(query, context, at) {
187
88
  parts.push({ $nor: [translateQuery(value, context, here)] });
188
89
  break;
189
90
  default: {
190
- if (key.startsWith("$")) throw new JqlError(`"${key}" has no MongoDB filter of its own; plan() keeps it out of what is pushed`, here);
91
+ if (key.startsWith("$")) throw new chunk74QKHM6Y_cjs.JqlError(`"${key}" has no MongoDB filter of its own; plan() keeps it out of what is pushed`, here);
191
92
  parts.push(translateField(key, value, context, here));
192
93
  }
193
94
  }
@@ -205,9 +106,9 @@ function translateQuery(query, context, at) {
205
106
  }
206
107
  function translateField(name, value, context, at) {
207
108
  const field = context.vocabulary?.lookup.get(name.toLowerCase());
208
- if (field?.get !== void 0) throw new JqlError(`"${name}" is computed here, so MongoDB has no such field`, at);
109
+ if (field?.get !== void 0) throw new chunk74QKHM6Y_cjs.JqlError(`"${name}" is computed here, so MongoDB has no such field`, at);
209
110
  const path = field?.path ?? name;
210
- if (!isPlainObject(value) || isDateLiteral(value)) return { [path]: literal(value, context, at) };
111
+ if (!chunk74QKHM6Y_cjs.isPlainObject(value) || chunk74QKHM6Y_cjs.isDateLiteral(value)) return { [path]: literal(value, context, at) };
211
112
  const keys = Object.keys(value);
212
113
  if (keys.length === 0 || !keys.every((key) => key.startsWith("$"))) return { [path]: literal(value, context, at) };
213
114
  const flags = typeof value.$options === "string" ? value.$options : "";
@@ -237,14 +138,14 @@ function translateField(name, value, context, at) {
237
138
  break;
238
139
  case "$in":
239
140
  case "$nin": {
240
- if (!Array.isArray(operand)) throw new JqlError(`${key} takes a list`, here);
141
+ if (!Array.isArray(operand)) throw new chunk74QKHM6Y_cjs.JqlError(`${key} takes a list`, here);
241
142
  const members = operand.map((item) => ignoreCase && typeof item === "string" ? anchored(quoteLiteral(item), flags, here) : literal(item, context, here));
242
143
  if (key === "$nin" && ignoreCase && members.some((member) => member instanceof RegExp)) beside.push({ $nor: [{ [path]: { $in: members } }] });
243
144
  else condition[key] = members;
244
145
  break;
245
146
  }
246
147
  case "$all": {
247
- if (!Array.isArray(operand)) throw new JqlError("$all takes a list", here);
148
+ if (!Array.isArray(operand)) throw new chunk74QKHM6Y_cjs.JqlError("$all takes a list", here);
248
149
  condition[key] = operand.map((item) => literal(item, context, here));
249
150
  break;
250
151
  }
@@ -265,7 +166,7 @@ function translateField(name, value, context, at) {
265
166
  case "$endsWith":
266
167
  case "$glob": {
267
168
  const patterns = (Array.isArray(operand) ? operand : [operand]).map((item) => {
268
- if (typeof item !== "string") throw new JqlError(`${key} takes strings`, here);
169
+ if (typeof item !== "string") throw new chunk74QKHM6Y_cjs.JqlError(`${key} takes strings`, here);
269
170
  return pattern(key, item, flags, here);
270
171
  });
271
172
  if (patterns.length === 1) beside.push({ [path]: patterns[0] });
@@ -276,12 +177,12 @@ function translateField(name, value, context, at) {
276
177
  condition.$elemMatch = translateQuery(operand, context, here);
277
178
  break;
278
179
  case "$not": {
279
- if (!isPlainObject(operand)) throw new JqlError("$not takes a condition", here);
180
+ if (!chunk74QKHM6Y_cjs.isPlainObject(operand)) throw new chunk74QKHM6Y_cjs.JqlError("$not takes a condition", here);
280
181
  beside.push({ $nor: [translateField(name, operand, context, here)] });
281
182
  break;
282
183
  }
283
184
  default:
284
- throw new JqlError(`"${key}" has no MongoDB filter of its own; plan() keeps it out of what is pushed`, here);
185
+ throw new chunk74QKHM6Y_cjs.JqlError(`"${key}" has no MongoDB filter of its own; plan() keeps it out of what is pushed`, here);
285
186
  }
286
187
  }
287
188
  const written = [];
@@ -291,13 +192,13 @@ function translateField(name, value, context, at) {
291
192
  return { $and: written };
292
193
  }
293
194
  function literal(value, context, at) {
294
- if (isDateLiteral(value)) {
295
- const time = resolveDate(value.$date, context.now);
296
- if (Number.isNaN(time)) throw new JqlError(`${JSON.stringify(value.$date)} is not a date`, at);
195
+ if (chunk74QKHM6Y_cjs.isDateLiteral(value)) {
196
+ const time = chunk74QKHM6Y_cjs.resolveDate(value.$date, context.now);
197
+ if (Number.isNaN(time)) throw new chunk74QKHM6Y_cjs.JqlError(`${JSON.stringify(value.$date)} is not a date`, at);
297
198
  return new Date(time);
298
199
  }
299
200
  if (Array.isArray(value)) return value.map((item) => literal(item, context, at));
300
- if (isPlainObject(value)) {
201
+ if (chunk74QKHM6Y_cjs.isPlainObject(value)) {
301
202
  const out = {};
302
203
  for (const [key, held] of Object.entries(value)) out[key] = literal(held, context, at);
303
204
  return out;
@@ -320,7 +221,7 @@ function mongoTypes(operand, at) {
320
221
  const out = [];
321
222
  for (const name of names) {
322
223
  const mapped = TYPES[name];
323
- if (mapped === void 0) throw new JqlError(`${JSON.stringify(name)} is not a type name`, at);
224
+ if (mapped === void 0) throw new chunk74QKHM6Y_cjs.JqlError(`${JSON.stringify(name)} is not a type name`, at);
324
225
  for (const alias of mapped) if (!out.includes(alias)) out.push(alias);
325
226
  }
326
227
  return out;
@@ -334,7 +235,7 @@ function anchored(body, flags, at) {
334
235
  }
335
236
  function keep(flags, at) {
336
237
  for (const flag of flags) {
337
- if (!"ims".includes(flag)) throw new JqlError(`MongoDB has no "${flag}" flag on a pattern; plan() keeps a clause carrying one here rather than pushing it`, at);
238
+ if (!"ims".includes(flag)) throw new chunk74QKHM6Y_cjs.JqlError(`MongoDB has no "${flag}" flag on a pattern; plan() keeps a clause carrying one here rather than pushing it`, at);
338
239
  }
339
240
  return flags;
340
241
  }
@@ -347,7 +248,7 @@ function pattern(operator, value, flags, at) {
347
248
  case "$endsWith":
348
249
  return new RegExp(`${quoteLiteral(value)}$`, keep(flags, at));
349
250
  default:
350
- return new RegExp(`^${compileGlobPattern(value, quoteLiteral)}$`, keep(flags, at));
251
+ return new RegExp(`^${chunkITPY6VTV_cjs.compileGlobPattern(value, quoteLiteral)}$`, keep(flags, at));
351
252
  }
352
253
  }
353
254
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/internal/duration.ts","../src/internal/values.ts","../src/internal/glob.ts","../src/targets/mongo.ts"],"names":["pattern","quoteLiteral"],"mappings":"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","file":"mongo.cjs","sourcesContent":["/**\n * The one way a query is refused.\n *\n * A query that looks right and matches nothing is the failure this library exists to\n * prevent: a misspelt operator, a field value that is `undefined` because a variable was,\n * a regex that does not compile. Each of those used to be a filter that silently let\n * everything through or nothing through, and nobody could tell which from the result. So\n * `compile` refuses, with a sentence saying what was wrong and where in the query it was.\n */\nexport class JqlError extends Error {\n /**\n * Where in the query the problem is, written the way you would reach it in code:\n * `$or[1].age.$gt`. Empty for the query as a whole.\n */\n readonly at: string;\n\n constructor(message: string, at = \"\") {\n super(at === \"\" ? message : `at ${show(at)}: ${message}`);\n this.name = \"JqlError\";\n this.at = at;\n }\n}\n\n/**\n * The location, short enough to read.\n *\n * A location is built from the query's own field names, and a field name is a string from\n * outside — a long one makes the sentence it prefixes unreadable, and turns a log line into\n * a paragraph. `at` itself keeps the whole path, because that is the part a program reads.\n */\nfunction show(at: string): string {\n return at.length > 80 ? `${at.slice(0, 80)}…` : at;\n}\n","/**\n * Durations, for the relative dates a saved filter is written with.\n *\n * `\"1h\"`, `\"30m\"`, `\"1h30m\"`, `\"7d\"`. Written as one or more counts with a unit, largest\n * first by convention but in any order, because a filter is typed by a person and\n * `\"30m1h\"` means what it says.\n *\n * Months and years are deliberately absent: neither has a fixed length, so `\"1mo\"` would\n * mean something slightly different in February, and a filter whose window changes size\n * with the calendar is one nobody can reason about. Use days or weeks.\n */\n\nconst PART = /(\\d+)(ms|s|m|h|d|w)/g;\nconst SHAPE = /^(?:\\d+(?:ms|s|m|h|d|w))+$/;\n\nconst UNITS: Readonly<Record<string, number>> = {\n ms: 1,\n s: 1_000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n};\n\n/** The unit names, for an error that says what is accepted. */\nexport const DURATION_UNITS = Object.keys(UNITS);\n\n/** A duration in milliseconds, or `undefined` when the text is not one. */\nexport function parseDuration(text: string): number | undefined {\n if (!SHAPE.test(text)) return undefined;\n let total = 0;\n PART.lastIndex = 0;\n for (;;) {\n const part = PART.exec(text);\n if (part === null) break;\n const count = Number(part[1]);\n // A count that does not survive the round trip through a number is not a duration\n // anybody meant; it is a digit string long enough to have lost its tail.\n if (!Number.isSafeInteger(count)) return undefined;\n total += count * (UNITS[part[2] as string] as number);\n }\n return Number.isSafeInteger(total) ? total : undefined;\n}\n","import { parseDuration } from \"./duration.js\";\nimport type { DateLiteral, FieldReference, TypeName } from \"../types.js\";\n\n/** An object literal or a JSON-parsed object: what a query document is made of. */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\") return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n\n/** `{ $date: … }` and nothing else. */\nexport function isDateLiteral(value: unknown): value is DateLiteral {\n if (!isPlainObject(value) || !(\"$date\" in value)) return false;\n for (const key in value) if (key !== \"$date\") return false;\n return true;\n}\n\n/** `{ $field: \"path\" }` and nothing else: a comparison with another field of the same item. */\nexport function isFieldReference(value: unknown): value is FieldReference {\n if (!isPlainObject(value) || typeof value.$field !== \"string\") return false;\n for (const key in value) if (key !== \"$field\") return false;\n return true;\n}\n\n/**\n * The instant a `$date` literal stands for, in epoch milliseconds, or `NaN`.\n *\n * `now` is passed in rather than read here, because a query holding `{ \"$ago\": \"1h\" }` has\n * to resolve against one instant for the whole run: reading the clock per comparison would\n * let the window move while a scan is in progress, so two items a second apart could be\n * judged against different hours. It is also what makes a relative filter testable without\n * waiting.\n */\nexport function resolveDate(value: unknown, now: number): number {\n if (typeof value === \"number\") return value;\n if (typeof value === \"string\") return value === \"now\" ? now : instant(value);\n if (isPlainObject(value)) {\n const keys = Object.keys(value);\n if (keys.length !== 1) return Number.NaN;\n const amount = keys[0] === \"$ago\" || keys[0] === \"$ahead\" ? value[keys[0] as string] : undefined;\n if (typeof amount !== \"string\") return Number.NaN;\n const span = parseDuration(amount);\n if (span === undefined) return Number.NaN;\n return keys[0] === \"$ago\" ? now - span : now + span;\n }\n return Number.NaN;\n}\n\n/**\n * A document value read as a date, in epoch milliseconds, or `NaN`.\n *\n * Only called where the query supplied a `$date`, so a string is only ever parsed as a\n * date because somebody asked for that. `NaN` compares false with everything, which is\n * what a value that is not a date should do.\n */\n/**\n * The forms of a date a document may hold, per the specification: a `Date`, epoch\n * milliseconds, or an **ISO 8601** string.\n *\n * `Date.parse` accepts a great deal more than that — `\"12/31/2020\"`, `\"Mar 5 2021\"`, and\n * `\"5\"`, which it reads as a year — and what it accepts beyond ISO 8601 is left to the\n * implementation by the language it comes from. A document that matched here and nowhere\n * else would make the same query mean different things in different engines, which is the\n * one thing this language promises it does not do. The space instead of `T` is allowed\n * because RFC 3339 allows it and people write it.\n */\nconst ISO_8601 = /^\\d{4}-\\d{2}-\\d{2}(?:[T ]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:?\\d{2})?)?$/;\n\n/** A timestamp with a time and no offset, which is the one shape whose meaning is not fixed. */\nconst NO_OFFSET = /^(\\d{4}-\\d{2}-\\d{2})[T ](\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?)$/;\n\n/**\n * An instant from a string, read the same way on every machine.\n *\n * JavaScript reads a date-only string as midnight **UTC** and a timestamp with no offset as\n * **local time** — so `\"2026-01-01T12:00:00\"` was a different instant in Tokyo and in London,\n * and a query and a document that both held one answered differently depending on where the\n * process happened to be running. That is the failure the language already rules out for\n * `\"12/31/2020\"`: the same query meaning two things. A missing offset is UTC here, which is\n * the rule the date-only form already follows.\n */\nexport function instant(text: string): number {\n const bare = NO_OFFSET.exec(text);\n return bare === null ? Date.parse(text) : Date.parse(`${bare[1] as string}T${bare[2] as string}Z`);\n}\n\nexport function toTime(value: unknown): number {\n if (typeof value === \"number\") return value;\n if (value instanceof Date) return value.getTime();\n if (typeof value === \"string\") return ISO_8601.test(value) ? instant(value) : Number.NaN;\n return Number.NaN;\n}\n\n/** The type names `$type` knows, in one list, so the error for a wrong one can offer the right one. */\nexport const TYPE_NAMES: readonly TypeName[] = [\"string\", \"number\", \"integer\", \"bigint\", \"boolean\", \"null\", \"array\", \"object\", \"date\"];\n\n/** Whether a document value is of the named type. A missing value is of no type. */\nexport function isOfType(value: unknown, type: TypeName): boolean {\n switch (type) {\n case \"string\":\n return typeof value === \"string\";\n case \"number\":\n // A big integer is a number for ordering, so it is one here too: `$gt` and `$type`\n // disagreeing about the same value is a distinction nobody could explain.\n return typeof value === \"number\" || typeof value === \"bigint\";\n case \"integer\":\n return Number.isInteger(value) || typeof value === \"bigint\";\n case \"bigint\":\n return typeof value === \"bigint\";\n case \"boolean\":\n return typeof value === \"boolean\";\n case \"null\":\n return value === null;\n case \"array\":\n return Array.isArray(value);\n case \"date\":\n return value instanceof Date;\n case \"object\":\n return value !== null && typeof value === \"object\" && !Array.isArray(value) && !(value instanceof Date);\n }\n}\n\n/**\n * A readable name for a value in an error sentence.\n *\n * Specific about what it was given, because \"a query is an object, not an object\" — which\n * is what a `Map`, a `Set` and a class instance all used to produce — tells the reader\n * nothing at all about the thing they passed.\n */\nexport function describe(value: unknown): string {\n if (value === null) return \"null\";\n // Named rather than written out: `String(fn)` is the function's entire source, and a\n // closure of any size turned the sentence refusing it into a listing.\n if (typeof value === \"function\") return \"a function\";\n if (Array.isArray(value)) return \"an array\";\n if (value instanceof RegExp) return \"a RegExp object\";\n if (value instanceof Date) return \"a Date object\";\n if (value instanceof Map) return \"a Map\";\n if (value instanceof Set) return \"a Set\";\n if (typeof value === \"object\") {\n if (isPlainObject(value)) return \"an object\";\n const name = nameOf(value);\n return name === undefined ? \"an object\" : `an instance of ${name}`;\n }\n if (typeof value === \"string\") return `the string ${JSON.stringify(value.length > 40 ? `${value.slice(0, 40)}…` : value)}`;\n return `${typeof value} ${String(value)}`;\n}\n\n/** The class an object came from, when it can be read without running anything of the caller's. */\nfunction nameOf(value: object): string | undefined {\n const prototype = Object.getPrototypeOf(value) as { constructor?: { name?: unknown } } | null;\n // Read from the prototype rather than through the object, so a `constructor` field of\n // the caller's own — which a document or a query read from JSON can have — is not what\n // names it.\n const name = prototype?.constructor?.name;\n return typeof name === \"string\" && name !== \"\" && name !== \"Object\" ? name : undefined;\n}\n","/**\n * Glob matching: `*`, `?`, and `\\` to escape either of them.\n *\n * What people mean when they reach for a pattern in a search box — `/api/*`,\n * `*.example.com` — and the reason it is not `$regex` with the sharp edges filed off: this\n * runs without a regular-expression engine, so it is available when patterns are turned\n * off for untrusted callers, and it cannot backtrack exponentially.\n *\n * No character classes. `[a-z]` is a literal `[a-z]`, because half a class syntax is worse\n * than none: somebody would write `[!abc]` expecting negation and get a match against those\n * four characters instead, with nothing to say so.\n *\n * **A pattern is taken apart once and becomes the narrowest matcher that fits it.** Nearly\n * every real glob is a prefix, a suffix, something in the middle, or a run of literals\n * separated by stars, and each of those is one or two native string calls per value. Only a\n * pattern with `?` in it walks character by character, and only that walk pays for splitting\n * the text into characters — which, measured against `startsWith`, was four times the cost of\n * the whole comparison.\n */\n\nexport type GlobMatcher = (text: string) => boolean;\n\n/**\n * What `*` and `?` become once the pattern is taken apart.\n *\n * Objects rather than strings, because a string sentinel is a string a *pattern* can also\n * contain: with a NUL-prefixed marker standing for `*`, the glob `a*\\u0000\\*` re-formed the\n * marker when the tokens were joined back together and was split in the wrong place — a\n * glob that silently matched everything.\n */\nconst ANY = { any: true } as const;\nconst ONE = { one: true } as const;\n\ntype Token = string | typeof ANY | typeof ONE;\n\nexport function compileGlob(pattern: string): GlobMatcher {\n const tokens = tokenize(pattern);\n const stars = tokens.includes(ANY);\n const questions = tokens.includes(ONE);\n\n if (!stars && !questions) {\n const literal = tokens.join(\"\");\n return (text) => text === literal;\n }\n if (!questions) {\n // Literal runs separated by stars: each run is found with `indexOf`, left to right.\n const segments: string[] = [\"\"];\n for (const token of tokens) {\n if (token === ANY) segments.push(\"\");\n else segments[segments.length - 1] += token as string;\n }\n const open = segments[0] === \"\";\n const close = segments[segments.length - 1] === \"\";\n const parts = segments.filter((segment) => segment !== \"\");\n if (parts.length === 0) return () => true;\n if (open && close && parts.length === 1) {\n const inside = parts[0] as string;\n return (text) => text.includes(inside);\n }\n if (!open && close && parts.length === 1) {\n const prefix = parts[0] as string;\n return (text) => text.startsWith(prefix);\n }\n if (open && !close && parts.length === 1) {\n const suffix = parts[0] as string;\n return (text) => text.endsWith(suffix);\n }\n return (text) => scan(text, parts, open, close);\n }\n return (text) => walk([...text], tokens);\n}\n\n/**\n * The same glob as the body of a regular expression, for a target that speaks patterns\n * rather than running the matcher — a store this query is being pushed down to.\n *\n * It shares the tokenizer with the matcher on purpose: an escape read one way here and\n * another way there would mean a query that found different things depending on which half\n * of the system answered it.\n */\nexport function compileGlobPattern(pattern: string, quoteLiteral: (text: string) => string): string {\n return tokenize(pattern)\n .map((token) => (token === ANY ? \"[\\\\s\\\\S]*\" : token === ONE ? \"[\\\\s\\\\S]\" : quoteLiteral(token as string)))\n .join(\"\");\n}\n\n/** Takes the pattern apart: literal characters, `ANY` for `*`, `ONE` for `?`. */\nfunction tokenize(pattern: string): Token[] {\n const tokens: Token[] = [];\n let escaped = false;\n for (const character of pattern) {\n if (escaped) {\n tokens.push(character);\n escaped = false;\n continue;\n }\n if (character === \"\\\\\") {\n escaped = true;\n continue;\n }\n if (character === \"*\") {\n // A run of stars means what one means, and collapsing it here keeps the matcher's\n // backtracking to one mark per run.\n if (tokens[tokens.length - 1] !== ANY) tokens.push(ANY);\n continue;\n }\n tokens.push(character === \"?\" ? ONE : character);\n }\n // A trailing backslash is somebody mid-word, not an error: it stands for itself.\n if (escaped) tokens.push(\"\\\\\");\n return tokens;\n}\n\n/** Literal runs in order, anchored at each end unless a star opens or closes the pattern. */\nfunction scan(text: string, parts: readonly string[], open: boolean, close: boolean): boolean {\n let at = 0;\n const last = parts.length - 1;\n for (let i = 0; i <= last; i++) {\n const part = parts[i] as string;\n if (i === 0 && !open) {\n if (!text.startsWith(part)) return false;\n at = part.length;\n continue;\n }\n if (i === last && !close) {\n // The final run has to be at the very end, and must not overlap what is matched.\n return text.length - part.length >= at && text.endsWith(part);\n }\n const found = text.indexOf(part, at);\n if (found === -1) return false;\n at = found + part.length;\n }\n return true;\n}\n\n/** The two-pointer walk, for patterns holding `?`. Quadratic at worst, linear on anything typed. */\nfunction walk(characters: readonly string[], tokens: readonly Token[]): boolean {\n let at = 0;\n let token = 0;\n let star = -1;\n let mark = 0;\n while (at < characters.length) {\n const current = tokens[token];\n if (current !== undefined && (current === ONE || current === characters[at])) {\n at++;\n token++;\n continue;\n }\n if (current === ANY) {\n star = token++;\n mark = at;\n continue;\n }\n if (star !== -1) {\n token = star + 1;\n at = ++mark;\n continue;\n }\n return false;\n }\n while (tokens[token] === ANY) token++;\n return token === tokens.length;\n}\n","import { JqlError } from \"../errors.js\";\nimport { isDateLiteral, isPlainObject, resolveDate } from \"../internal/values.js\";\nimport { compileGlobPattern } from \"../internal/glob.js\";\nimport type { Capabilities } from \"../plan.js\";\nimport type { TypeName } from \"../types.js\";\nimport type { Vocabulary } from \"../vocabulary.js\";\n\n/**\n * JQL as a MongoDB filter.\n *\n * The first target, because it is the one where the two languages nearly agree: most of\n * JQL's operators *are* MongoDB's, with the same array semantics, so the translation is\n * mostly a copy and the interesting part is the handful that differ.\n *\n * The translation is checked by *running* it, not by comparing its shape: `tests/helpers/mongo.ts`\n * is a second reading of MongoDB's matching rules, taken from its documented behaviour rather\n * than from this engine, and the split test runs both halves of every generated query through\n * it and through the engine and compares the answers.\n *\n * That check found the one difference no capability can express: **an array held directly\n * inside another array**. A path segment in MongoDB applies to an array's elements but not to\n * the elements of *those* arrays; JQL sees an array through at every level. Which rows come\n * back then differs, in either direction, and nothing in the query says so — the shape is in\n * the data. It is written down in specification §3 and in the pushdown guide, because a\n * caller with such a collection must not read `complete` as permission to skip the second\n * pass.\n *\n * What it will not do is guess. Every operator MongoDB cannot answer exactly —\n * `$word`, `$length`, a `{ \"$field\": … }` reference, `$text` — is left out of\n * `MONGO_CAPABILITIES`, so `plan()` keeps those clauses here and this function never sees\n * them. A translation that was *nearly* right would be the worst of both: fewer rows than\n * the query asked for, from a store that looked like it had answered.\n */\n\n/**\n * What a MongoDB `find` filter can answer, in JQL's own names.\n *\n * Give it to `plan()` and it splits a query into the filter to send and the predicate to\n * apply to what comes back.\n */\nexport const MONGO_CAPABILITIES: Capabilities = Object.freeze({\n fields: \"all\",\n operators: Object.freeze([\n \"$eq\",\n \"$ne\",\n \"$gt\",\n \"$gte\",\n \"$lt\",\n \"$lte\",\n \"$in\",\n \"$nin\",\n \"$exists\",\n \"$type\",\n \"$regex\",\n \"$options\",\n \"$mod\",\n \"$size\",\n \"$all\",\n \"$elemMatch\",\n \"$not\",\n // Translated to anchored, escaped patterns, which MongoDB runs exactly as JQL does.\n \"$contains\",\n \"$startsWith\",\n \"$endsWith\",\n \"$glob\",\n ]),\n or: true,\n not: true,\n accepts: (_field: string, condition: Readonly<Record<string, unknown>>) => pushable(condition),\n});\n\n/** Whether MongoDB answers this condition **exactly**, which some of its operators do only for some values. */\nfunction pushable(condition: Readonly<Record<string, unknown>>): boolean {\n const flags = typeof condition.$options === \"string\" ? condition.$options : \"\";\n for (const [key, operand] of Object.entries(condition)) {\n switch (key) {\n case \"$eq\":\n case \"$ne\":\n case \"$in\":\n case \"$nin\":\n case \"$all\": {\n for (const value of Array.isArray(operand) && key !== \"$eq\" && key !== \"$ne\" ? operand : [operand]) {\n // JQL compares objects without regard to key order; MongoDB's embedded-document\n // equality is a byte comparison, so `{ c: 2, b: 1 }` is not `{ b: 1, c: 2 }` there.\n if (composite(value)) return false;\n // Ignoring case reaches the strings inside a list. A pattern can stand in for one\n // string, and for nothing deeper.\n if (flags.includes(\"i\") && typeof value !== \"string\") return false;\n }\n break;\n }\n case \"$type\": {\n // JQL's `integer` is \"a number with no fractional part\"; MongoDB's `int`/`long` are\n // storage types, so a whole number held as a double is one and not the other.\n // `bigint` is worse: it names a run-time type JSON cannot carry at all, and what a\n // driver hands back for a stored `long` is usually an ordinary number — so the store\n // would answer with rows this engine says are not bigints, and `complete` would say\n // no second pass was needed. Both stay here.\n const names = Array.isArray(operand) ? operand : [operand];\n if (names.includes(\"integer\") || names.includes(\"bigint\")) return false;\n break;\n }\n case \"$regex\":\n // MongoDB takes `i`, `m`, `s` and `x`; JQL's `u` is not one it knows, and a filter\n // it refuses does not run at all.\n if ([...flags].some((flag) => !\"ims\".includes(flag))) return false;\n break;\n default:\n break;\n }\n }\n return true;\n}\n\n/** A value MongoDB compares byte for byte, where JQL compares it by its keys. */\nfunction composite(value: unknown): boolean {\n if (Array.isArray(value)) return value.some(composite);\n return isPlainObject(value) && !isDateLiteral(value);\n}\n\nexport interface MongoOptions {\n /**\n * Resolves a vocabulary's names and aliases to the paths the collection stores.\n *\n * Only needed for a query that did not come from `plan()`: what `plan` pushes already\n * carries the stored names.\n */\n readonly vocabulary?: Vocabulary<never, object> | undefined;\n /** Where \"now\" comes from, for a relative date. Default the system clock, read once. */\n readonly now?: (() => number) | undefined;\n}\n\n/** A MongoDB filter document. Values may be `Date` and `RegExp` objects, which a driver expects. */\nexport type MongoFilter = Record<string, unknown>;\n\n/**\n * Translates a JQL query into a MongoDB filter.\n *\n * Give it only what `plan(query, MONGO_CAPABILITIES)` pushed. Anything else is refused by\n * name rather than approximated.\n */\nexport function toMongoFilter(query: unknown, options: MongoOptions = {}): MongoFilter {\n const now = options.now === undefined ? Date.now() : options.now();\n return translateQuery(query, { vocabulary: options.vocabulary, now }, \"\");\n}\n\ninterface Translating {\n readonly vocabulary: Vocabulary<never, object> | undefined;\n readonly now: number;\n}\n\nfunction translateQuery(query: unknown, context: Translating, at: string): MongoFilter {\n if (!isPlainObject(query)) throw new JqlError(`a query is an object, not ${typeof query}`, at);\n const parts: MongoFilter[] = [];\n for (const [key, value] of Object.entries(query)) {\n const here = at === \"\" ? key : `${at}.${key}`;\n switch (key) {\n case \"$comment\":\n break;\n case \"$and\":\n case \"$or\":\n case \"$nor\": {\n if (!Array.isArray(value)) throw new JqlError(`${key} takes a list of queries`, here);\n parts.push({ [key]: value.map((part, index) => translateQuery(part, context, `${here}[${index}]`)) });\n break;\n }\n case \"$not\":\n // MongoDB has no `$not` over a whole query; `$nor` of one is exactly it.\n parts.push({ $nor: [translateQuery(value, context, here)] });\n break;\n default: {\n if (key.startsWith(\"$\")) throw new JqlError(`\"${key}\" has no MongoDB filter of its own; plan() keeps it out of what is pushed`, here);\n parts.push(translateField(key, value, context, here));\n }\n }\n }\n if (parts.length === 0) return {};\n if (parts.length === 1) return parts[0] as MongoFilter;\n // Merged where the keys do not collide, and `$and` where they do — the same filter either\n // way, and the merged one is what somebody reading the query in a shell would have written.\n const merged: MongoFilter = {};\n for (const part of parts) {\n for (const [key, value] of Object.entries(part)) {\n if (key in merged) return { $and: parts };\n merged[key] = value;\n }\n }\n return merged;\n}\n\nfunction translateField(name: string, value: unknown, context: Translating, at: string): MongoFilter {\n const field = context.vocabulary?.lookup.get(name.toLowerCase());\n if (field?.get !== undefined) throw new JqlError(`\"${name}\" is computed here, so MongoDB has no such field`, at);\n const path = field?.path ?? name;\n if (!isPlainObject(value) || isDateLiteral(value)) return { [path]: literal(value, context, at) };\n const keys = Object.keys(value);\n if (keys.length === 0 || !keys.every((key) => key.startsWith(\"$\"))) return { [path]: literal(value, context, at) };\n\n const flags = typeof value.$options === \"string\" ? value.$options : \"\";\n const ignoreCase = flags.includes(\"i\");\n const condition: Record<string, unknown> = {};\n const beside: MongoFilter[] = [];\n for (const key of keys) {\n if (key === \"$options\" || key === \"$comment\") continue;\n const operand = value[key];\n const here = `${at}.${key}`;\n switch (key) {\n case \"$eq\":\n case \"$ne\": {\n if (ignoreCase && typeof operand === \"string\") {\n // A bare pattern, not `{ $eq: /…/ }`. MongoDB documents the bare form as a match\n // against the pattern, while `$eq` with a regular expression is a comparison with\n // the regex *value* as often as it is a match — and a translation that rests on\n // which reading a server takes is one that returns the wrong rows on the servers\n // that take the other. `$nor` negates it without the same question.\n const match = { [path]: anchored(quoteLiteral(operand), flags, here) };\n beside.push(key === \"$eq\" ? match : { $nor: [match] });\n break;\n }\n condition[key] = literal(operand, context, here);\n break;\n }\n case \"$gt\":\n case \"$gte\":\n case \"$lt\":\n case \"$lte\":\n condition[key] = literal(operand, context, here);\n break;\n case \"$in\":\n case \"$nin\": {\n if (!Array.isArray(operand)) throw new JqlError(`${key} takes a list`, here);\n const members = operand.map((item) => (ignoreCase && typeof item === \"string\" ? anchored(quoteLiteral(item), flags, here) : literal(item, context, here)));\n // `$in` takes patterns; `$nin` with one is less clearly documented, so its\n // case-insensitive form is written as the negation of an `$in`, which is.\n if (key === \"$nin\" && ignoreCase && members.some((member) => member instanceof RegExp)) beside.push({ $nor: [{ [path]: { $in: members } }] });\n else condition[key] = members;\n break;\n }\n case \"$all\": {\n if (!Array.isArray(operand)) throw new JqlError(\"$all takes a list\", here);\n condition[key] = operand.map((item) => literal(item, context, here));\n break;\n }\n case \"$exists\":\n case \"$mod\":\n case \"$size\":\n condition[key] = operand;\n break;\n case \"$type\":\n condition[key] = mongoTypes(operand, here);\n break;\n case \"$regex\":\n condition.$regex = operand;\n // Only the flags MongoDB takes; `plan` keeps back the ones it does not, and this is\n // the second lock on the same door.\n if (keep(flags, here) !== \"\") condition.$options = keep(flags, here);\n break;\n case \"$contains\":\n case \"$startsWith\":\n case \"$endsWith\":\n case \"$glob\": {\n const patterns = (Array.isArray(operand) ? operand : [operand]).map((item) => {\n if (typeof item !== \"string\") throw new JqlError(`${key} takes strings`, here);\n return pattern(key, item, flags, here);\n });\n // A list means \"any of these\", which MongoDB spells `$in` over the patterns.\n if (patterns.length === 1) beside.push({ [path]: patterns[0] as RegExp });\n else beside.push({ [path]: { $in: patterns } });\n break;\n }\n case \"$elemMatch\":\n condition.$elemMatch = translateQuery(operand, context, here);\n break;\n case \"$not\": {\n if (!isPlainObject(operand)) throw new JqlError(\"$not takes a condition\", here);\n // MongoDB's field-level `$not` refuses some of what JQL allows inside it, and\n // `$nor` of the same clause is exactly the same question with none of the rules.\n beside.push({ $nor: [translateField(name, operand, context, here)] });\n break;\n }\n default:\n throw new JqlError(`\"${key}\" has no MongoDB filter of its own; plan() keeps it out of what is pushed`, here);\n }\n }\n const written: MongoFilter[] = [];\n if (Object.keys(condition).length > 0) written.push({ [path]: condition });\n written.push(...beside);\n if (written.length === 1) return written[0] as MongoFilter;\n return { $and: written };\n}\n\n/** A JQL literal as MongoDB would hold it: a date literal becomes a `Date`. */\nfunction literal(value: unknown, context: Translating, at: string): unknown {\n if (isDateLiteral(value)) {\n const time = resolveDate(value.$date, context.now);\n if (Number.isNaN(time)) throw new JqlError(`${JSON.stringify(value.$date)} is not a date`, at);\n return new Date(time);\n }\n if (Array.isArray(value)) return value.map((item) => literal(item, context, at));\n if (isPlainObject(value)) {\n const out: Record<string, unknown> = {};\n for (const [key, held] of Object.entries(value)) out[key] = literal(held, context, at);\n return out;\n }\n return value;\n}\n\n/** JQL's type names, in MongoDB's. */\nconst TYPES: Readonly<Record<TypeName, readonly string[]>> = {\n string: [\"string\"],\n number: [\"double\", \"int\", \"long\", \"decimal\"],\n integer: [\"int\", \"long\"],\n bigint: [\"long\"],\n boolean: [\"bool\"],\n null: [\"null\"],\n array: [\"array\"],\n object: [\"object\"],\n date: [\"date\"],\n};\n\nfunction mongoTypes(operand: unknown, at: string): string[] {\n const names = Array.isArray(operand) ? operand : [operand];\n const out: string[] = [];\n for (const name of names) {\n const mapped = TYPES[name as TypeName];\n if (mapped === undefined) throw new JqlError(`${JSON.stringify(name)} is not a type name`, at);\n for (const alias of mapped) if (!out.includes(alias)) out.push(alias);\n }\n return out;\n}\n\nconst SPECIAL = /[.*+?^${}()|[\\]\\\\]/g;\n\nfunction quoteLiteral(text: string): string {\n return text.replace(SPECIAL, \"\\\\$&\");\n}\n\nfunction anchored(body: string, flags: string, at: string): RegExp {\n return new RegExp(`^${body}$`, keep(flags, at));\n}\n\n/**\n * The flags MongoDB takes on a pattern, and only those.\n *\n * A refusal rather than a filter: `plan` keeps a clause with any other flag here, so\n * reaching this at all means `toMongoFilter` was handed something `plan` did not push. Both\n * of the other answers are worse than saying so — dropping the flag changes what the pattern\n * means, and passing it on gives the server a filter it refuses outright, so the query fails\n * later and somewhere else.\n */\nfunction keep(flags: string, at: string): string {\n for (const flag of flags) {\n if (!\"ims\".includes(flag)) throw new JqlError(`MongoDB has no \"${flag}\" flag on a pattern; plan() keeps a clause carrying one here rather than pushing it`, at);\n }\n return flags;\n}\n\nfunction pattern(operator: string, value: string, flags: string, at: string): RegExp {\n switch (operator) {\n case \"$contains\":\n return new RegExp(quoteLiteral(value), keep(flags, at));\n case \"$startsWith\":\n return new RegExp(`^${quoteLiteral(value)}`, keep(flags, at));\n case \"$endsWith\":\n return new RegExp(`${quoteLiteral(value)}$`, keep(flags, at));\n default:\n return new RegExp(`^${compileGlobPattern(value, quoteLiteral)}$`, keep(flags, at));\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/targets/mongo.ts"],"names":["isPlainObject","isDateLiteral","JqlError","resolveDate","compileGlobPattern"],"mappings":"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","file":"mongo.cjs","sourcesContent":["import { JqlError } from \"../errors.js\";\nimport { isDateLiteral, isPlainObject, resolveDate } from \"../internal/values.js\";\nimport { compileGlobPattern } from \"../internal/glob.js\";\nimport type { Capabilities } from \"../plan.js\";\nimport type { TypeName } from \"../types.js\";\nimport type { Vocabulary } from \"../vocabulary.js\";\n\n/**\n * JQL as a MongoDB filter.\n *\n * The first target, because it is the one where the two languages nearly agree: most of\n * JQL's operators *are* MongoDB's, with the same array semantics, so the translation is\n * mostly a copy and the interesting part is the handful that differ.\n *\n * The translation is checked by *running* it, not by comparing its shape: `tests/helpers/mongo.ts`\n * is a second reading of MongoDB's matching rules, taken from its documented behaviour rather\n * than from this engine, and the split test runs both halves of every generated query through\n * it and through the engine and compares the answers.\n *\n * That check found the one difference no capability can express: **an array held directly\n * inside another array**. A path segment in MongoDB applies to an array's elements but not to\n * the elements of *those* arrays; JQL sees an array through at every level. Which rows come\n * back then differs, in either direction, and nothing in the query says so — the shape is in\n * the data. It is written down in specification §3 and in the pushdown guide, because a\n * caller with such a collection must not read `complete` as permission to skip the second\n * pass.\n *\n * What it will not do is guess. Every operator MongoDB cannot answer exactly —\n * `$word`, `$length`, a `{ \"$field\": … }` reference, `$text` — is left out of\n * `MONGO_CAPABILITIES`, so `plan()` keeps those clauses here and this function never sees\n * them. A translation that was *nearly* right would be the worst of both: fewer rows than\n * the query asked for, from a store that looked like it had answered.\n */\n\n/**\n * What a MongoDB `find` filter can answer, in JQL's own names.\n *\n * Give it to `plan()` and it splits a query into the filter to send and the predicate to\n * apply to what comes back.\n */\nexport const MONGO_CAPABILITIES: Capabilities = Object.freeze({\n fields: \"all\",\n operators: Object.freeze([\n \"$eq\",\n \"$ne\",\n \"$gt\",\n \"$gte\",\n \"$lt\",\n \"$lte\",\n \"$in\",\n \"$nin\",\n \"$exists\",\n \"$type\",\n \"$regex\",\n \"$options\",\n \"$mod\",\n \"$size\",\n \"$all\",\n \"$elemMatch\",\n \"$not\",\n // Translated to anchored, escaped patterns, which MongoDB runs exactly as JQL does.\n \"$contains\",\n \"$startsWith\",\n \"$endsWith\",\n \"$glob\",\n ]),\n or: true,\n not: true,\n accepts: (_field: string, condition: Readonly<Record<string, unknown>>) => pushable(condition),\n});\n\n/** Whether MongoDB answers this condition **exactly**, which some of its operators do only for some values. */\nfunction pushable(condition: Readonly<Record<string, unknown>>): boolean {\n const flags = typeof condition.$options === \"string\" ? condition.$options : \"\";\n for (const [key, operand] of Object.entries(condition)) {\n switch (key) {\n case \"$eq\":\n case \"$ne\":\n case \"$in\":\n case \"$nin\":\n case \"$all\": {\n for (const value of Array.isArray(operand) && key !== \"$eq\" && key !== \"$ne\" ? operand : [operand]) {\n // JQL compares objects without regard to key order; MongoDB's embedded-document\n // equality is a byte comparison, so `{ c: 2, b: 1 }` is not `{ b: 1, c: 2 }` there.\n if (composite(value)) return false;\n // Ignoring case reaches the strings inside a list. A pattern can stand in for one\n // string, and for nothing deeper.\n if (flags.includes(\"i\") && typeof value !== \"string\") return false;\n }\n break;\n }\n case \"$type\": {\n // JQL's `integer` is \"a number with no fractional part\"; MongoDB's `int`/`long` are\n // storage types, so a whole number held as a double is one and not the other.\n // `bigint` is worse: it names a run-time type JSON cannot carry at all, and what a\n // driver hands back for a stored `long` is usually an ordinary number — so the store\n // would answer with rows this engine says are not bigints, and `complete` would say\n // no second pass was needed. Both stay here.\n const names = Array.isArray(operand) ? operand : [operand];\n if (names.includes(\"integer\") || names.includes(\"bigint\")) return false;\n break;\n }\n case \"$regex\":\n // MongoDB takes `i`, `m`, `s` and `x`; JQL's `u` is not one it knows, and a filter\n // it refuses does not run at all.\n if ([...flags].some((flag) => !\"ims\".includes(flag))) return false;\n break;\n default:\n break;\n }\n }\n return true;\n}\n\n/** A value MongoDB compares byte for byte, where JQL compares it by its keys. */\nfunction composite(value: unknown): boolean {\n if (Array.isArray(value)) return value.some(composite);\n return isPlainObject(value) && !isDateLiteral(value);\n}\n\nexport interface MongoOptions {\n /**\n * Resolves a vocabulary's names and aliases to the paths the collection stores.\n *\n * Only needed for a query that did not come from `plan()`: what `plan` pushes already\n * carries the stored names.\n */\n readonly vocabulary?: Vocabulary<never, object> | undefined;\n /** Where \"now\" comes from, for a relative date. Default the system clock, read once. */\n readonly now?: (() => number) | undefined;\n}\n\n/** A MongoDB filter document. Values may be `Date` and `RegExp` objects, which a driver expects. */\nexport type MongoFilter = Record<string, unknown>;\n\n/**\n * Translates a JQL query into a MongoDB filter.\n *\n * Give it only what `plan(query, MONGO_CAPABILITIES)` pushed. Anything else is refused by\n * name rather than approximated.\n */\nexport function toMongoFilter(query: unknown, options: MongoOptions = {}): MongoFilter {\n const now = options.now === undefined ? Date.now() : options.now();\n return translateQuery(query, { vocabulary: options.vocabulary, now }, \"\");\n}\n\ninterface Translating {\n readonly vocabulary: Vocabulary<never, object> | undefined;\n readonly now: number;\n}\n\nfunction translateQuery(query: unknown, context: Translating, at: string): MongoFilter {\n if (!isPlainObject(query)) throw new JqlError(`a query is an object, not ${typeof query}`, at);\n const parts: MongoFilter[] = [];\n for (const [key, value] of Object.entries(query)) {\n const here = at === \"\" ? key : `${at}.${key}`;\n switch (key) {\n case \"$comment\":\n break;\n case \"$and\":\n case \"$or\":\n case \"$nor\": {\n if (!Array.isArray(value)) throw new JqlError(`${key} takes a list of queries`, here);\n parts.push({ [key]: value.map((part, index) => translateQuery(part, context, `${here}[${index}]`)) });\n break;\n }\n case \"$not\":\n // MongoDB has no `$not` over a whole query; `$nor` of one is exactly it.\n parts.push({ $nor: [translateQuery(value, context, here)] });\n break;\n default: {\n if (key.startsWith(\"$\")) throw new JqlError(`\"${key}\" has no MongoDB filter of its own; plan() keeps it out of what is pushed`, here);\n parts.push(translateField(key, value, context, here));\n }\n }\n }\n if (parts.length === 0) return {};\n if (parts.length === 1) return parts[0] as MongoFilter;\n // Merged where the keys do not collide, and `$and` where they do — the same filter either\n // way, and the merged one is what somebody reading the query in a shell would have written.\n const merged: MongoFilter = {};\n for (const part of parts) {\n for (const [key, value] of Object.entries(part)) {\n if (key in merged) return { $and: parts };\n merged[key] = value;\n }\n }\n return merged;\n}\n\nfunction translateField(name: string, value: unknown, context: Translating, at: string): MongoFilter {\n const field = context.vocabulary?.lookup.get(name.toLowerCase());\n if (field?.get !== undefined) throw new JqlError(`\"${name}\" is computed here, so MongoDB has no such field`, at);\n const path = field?.path ?? name;\n if (!isPlainObject(value) || isDateLiteral(value)) return { [path]: literal(value, context, at) };\n const keys = Object.keys(value);\n if (keys.length === 0 || !keys.every((key) => key.startsWith(\"$\"))) return { [path]: literal(value, context, at) };\n\n const flags = typeof value.$options === \"string\" ? value.$options : \"\";\n const ignoreCase = flags.includes(\"i\");\n const condition: Record<string, unknown> = {};\n const beside: MongoFilter[] = [];\n for (const key of keys) {\n if (key === \"$options\" || key === \"$comment\") continue;\n const operand = value[key];\n const here = `${at}.${key}`;\n switch (key) {\n case \"$eq\":\n case \"$ne\": {\n if (ignoreCase && typeof operand === \"string\") {\n // A bare pattern, not `{ $eq: /…/ }`. MongoDB documents the bare form as a match\n // against the pattern, while `$eq` with a regular expression is a comparison with\n // the regex *value* as often as it is a match — and a translation that rests on\n // which reading a server takes is one that returns the wrong rows on the servers\n // that take the other. `$nor` negates it without the same question.\n const match = { [path]: anchored(quoteLiteral(operand), flags, here) };\n beside.push(key === \"$eq\" ? match : { $nor: [match] });\n break;\n }\n condition[key] = literal(operand, context, here);\n break;\n }\n case \"$gt\":\n case \"$gte\":\n case \"$lt\":\n case \"$lte\":\n condition[key] = literal(operand, context, here);\n break;\n case \"$in\":\n case \"$nin\": {\n if (!Array.isArray(operand)) throw new JqlError(`${key} takes a list`, here);\n const members = operand.map((item) => (ignoreCase && typeof item === \"string\" ? anchored(quoteLiteral(item), flags, here) : literal(item, context, here)));\n // `$in` takes patterns; `$nin` with one is less clearly documented, so its\n // case-insensitive form is written as the negation of an `$in`, which is.\n if (key === \"$nin\" && ignoreCase && members.some((member) => member instanceof RegExp)) beside.push({ $nor: [{ [path]: { $in: members } }] });\n else condition[key] = members;\n break;\n }\n case \"$all\": {\n if (!Array.isArray(operand)) throw new JqlError(\"$all takes a list\", here);\n condition[key] = operand.map((item) => literal(item, context, here));\n break;\n }\n case \"$exists\":\n case \"$mod\":\n case \"$size\":\n condition[key] = operand;\n break;\n case \"$type\":\n condition[key] = mongoTypes(operand, here);\n break;\n case \"$regex\":\n condition.$regex = operand;\n // Only the flags MongoDB takes; `plan` keeps back the ones it does not, and this is\n // the second lock on the same door.\n if (keep(flags, here) !== \"\") condition.$options = keep(flags, here);\n break;\n case \"$contains\":\n case \"$startsWith\":\n case \"$endsWith\":\n case \"$glob\": {\n const patterns = (Array.isArray(operand) ? operand : [operand]).map((item) => {\n if (typeof item !== \"string\") throw new JqlError(`${key} takes strings`, here);\n return pattern(key, item, flags, here);\n });\n // A list means \"any of these\", which MongoDB spells `$in` over the patterns.\n if (patterns.length === 1) beside.push({ [path]: patterns[0] as RegExp });\n else beside.push({ [path]: { $in: patterns } });\n break;\n }\n case \"$elemMatch\":\n condition.$elemMatch = translateQuery(operand, context, here);\n break;\n case \"$not\": {\n if (!isPlainObject(operand)) throw new JqlError(\"$not takes a condition\", here);\n // MongoDB's field-level `$not` refuses some of what JQL allows inside it, and\n // `$nor` of the same clause is exactly the same question with none of the rules.\n beside.push({ $nor: [translateField(name, operand, context, here)] });\n break;\n }\n default:\n throw new JqlError(`\"${key}\" has no MongoDB filter of its own; plan() keeps it out of what is pushed`, here);\n }\n }\n const written: MongoFilter[] = [];\n if (Object.keys(condition).length > 0) written.push({ [path]: condition });\n written.push(...beside);\n if (written.length === 1) return written[0] as MongoFilter;\n return { $and: written };\n}\n\n/** A JQL literal as MongoDB would hold it: a date literal becomes a `Date`. */\nfunction literal(value: unknown, context: Translating, at: string): unknown {\n if (isDateLiteral(value)) {\n const time = resolveDate(value.$date, context.now);\n if (Number.isNaN(time)) throw new JqlError(`${JSON.stringify(value.$date)} is not a date`, at);\n return new Date(time);\n }\n if (Array.isArray(value)) return value.map((item) => literal(item, context, at));\n if (isPlainObject(value)) {\n const out: Record<string, unknown> = {};\n for (const [key, held] of Object.entries(value)) out[key] = literal(held, context, at);\n return out;\n }\n return value;\n}\n\n/** JQL's type names, in MongoDB's. */\nconst TYPES: Readonly<Record<TypeName, readonly string[]>> = {\n string: [\"string\"],\n number: [\"double\", \"int\", \"long\", \"decimal\"],\n integer: [\"int\", \"long\"],\n bigint: [\"long\"],\n boolean: [\"bool\"],\n null: [\"null\"],\n array: [\"array\"],\n object: [\"object\"],\n date: [\"date\"],\n};\n\nfunction mongoTypes(operand: unknown, at: string): string[] {\n const names = Array.isArray(operand) ? operand : [operand];\n const out: string[] = [];\n for (const name of names) {\n const mapped = TYPES[name as TypeName];\n if (mapped === undefined) throw new JqlError(`${JSON.stringify(name)} is not a type name`, at);\n for (const alias of mapped) if (!out.includes(alias)) out.push(alias);\n }\n return out;\n}\n\nconst SPECIAL = /[.*+?^${}()|[\\]\\\\]/g;\n\nfunction quoteLiteral(text: string): string {\n return text.replace(SPECIAL, \"\\\\$&\");\n}\n\nfunction anchored(body: string, flags: string, at: string): RegExp {\n return new RegExp(`^${body}$`, keep(flags, at));\n}\n\n/**\n * The flags MongoDB takes on a pattern, and only those.\n *\n * A refusal rather than a filter: `plan` keeps a clause with any other flag here, so\n * reaching this at all means `toMongoFilter` was handed something `plan` did not push. Both\n * of the other answers are worse than saying so — dropping the flag changes what the pattern\n * means, and passing it on gives the server a filter it refuses outright, so the query fails\n * later and somewhere else.\n */\nfunction keep(flags: string, at: string): string {\n for (const flag of flags) {\n if (!\"ims\".includes(flag)) throw new JqlError(`MongoDB has no \"${flag}\" flag on a pattern; plan() keeps a clause carrying one here rather than pushing it`, at);\n }\n return flags;\n}\n\nfunction pattern(operator: string, value: string, flags: string, at: string): RegExp {\n switch (operator) {\n case \"$contains\":\n return new RegExp(quoteLiteral(value), keep(flags, at));\n case \"$startsWith\":\n return new RegExp(`^${quoteLiteral(value)}`, keep(flags, at));\n case \"$endsWith\":\n return new RegExp(`${quoteLiteral(value)}$`, keep(flags, at));\n default:\n return new RegExp(`^${compileGlobPattern(value, quoteLiteral)}$`, keep(flags, at));\n }\n}\n"]}
package/dist/mongo.js CHANGED
@@ -1,104 +1,5 @@
1
- // src/errors.ts
2
- var JqlError = class extends Error {
3
- /**
4
- * Where in the query the problem is, written the way you would reach it in code:
5
- * `$or[1].age.$gt`. Empty for the query as a whole.
6
- */
7
- at;
8
- constructor(message, at = "") {
9
- super(at === "" ? message : `at ${show(at)}: ${message}`);
10
- this.name = "JqlError";
11
- this.at = at;
12
- }
13
- };
14
- function show(at) {
15
- return at.length > 80 ? `${at.slice(0, 80)}\u2026` : at;
16
- }
17
-
18
- // src/internal/duration.ts
19
- var PART = /(\d+)(ms|s|m|h|d|w)/g;
20
- var SHAPE = /^(?:\d+(?:ms|s|m|h|d|w))+$/;
21
- var UNITS = {
22
- ms: 1,
23
- s: 1e3,
24
- m: 6e4,
25
- h: 36e5,
26
- d: 864e5,
27
- w: 6048e5
28
- };
29
- function parseDuration(text) {
30
- if (!SHAPE.test(text)) return void 0;
31
- let total = 0;
32
- PART.lastIndex = 0;
33
- for (; ; ) {
34
- const part = PART.exec(text);
35
- if (part === null) break;
36
- const count = Number(part[1]);
37
- if (!Number.isSafeInteger(count)) return void 0;
38
- total += count * UNITS[part[2]];
39
- }
40
- return Number.isSafeInteger(total) ? total : void 0;
41
- }
42
-
43
- // src/internal/values.ts
44
- function isPlainObject(value) {
45
- if (value === null || typeof value !== "object") return false;
46
- const prototype = Object.getPrototypeOf(value);
47
- return prototype === Object.prototype || prototype === null;
48
- }
49
- function isDateLiteral(value) {
50
- if (!isPlainObject(value) || !("$date" in value)) return false;
51
- for (const key in value) if (key !== "$date") return false;
52
- return true;
53
- }
54
- function resolveDate(value, now) {
55
- if (typeof value === "number") return value;
56
- if (typeof value === "string") return value === "now" ? now : instant(value);
57
- if (isPlainObject(value)) {
58
- const keys = Object.keys(value);
59
- if (keys.length !== 1) return Number.NaN;
60
- const amount = keys[0] === "$ago" || keys[0] === "$ahead" ? value[keys[0]] : void 0;
61
- if (typeof amount !== "string") return Number.NaN;
62
- const span = parseDuration(amount);
63
- if (span === void 0) return Number.NaN;
64
- return keys[0] === "$ago" ? now - span : now + span;
65
- }
66
- return Number.NaN;
67
- }
68
- var NO_OFFSET = /^(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?)$/;
69
- function instant(text) {
70
- const bare = NO_OFFSET.exec(text);
71
- return bare === null ? Date.parse(text) : Date.parse(`${bare[1]}T${bare[2]}Z`);
72
- }
73
-
74
- // src/internal/glob.ts
75
- var ANY = { any: true };
76
- var ONE = { one: true };
77
- function compileGlobPattern(pattern2, quoteLiteral2) {
78
- return tokenize(pattern2).map((token) => token === ANY ? "[\\s\\S]*" : token === ONE ? "[\\s\\S]" : quoteLiteral2(token)).join("");
79
- }
80
- function tokenize(pattern2) {
81
- const tokens = [];
82
- let escaped = false;
83
- for (const character of pattern2) {
84
- if (escaped) {
85
- tokens.push(character);
86
- escaped = false;
87
- continue;
88
- }
89
- if (character === "\\") {
90
- escaped = true;
91
- continue;
92
- }
93
- if (character === "*") {
94
- if (tokens[tokens.length - 1] !== ANY) tokens.push(ANY);
95
- continue;
96
- }
97
- tokens.push(character === "?" ? ONE : character);
98
- }
99
- if (escaped) tokens.push("\\");
100
- return tokens;
101
- }
1
+ import { compileGlobPattern } from './chunk-ATRXHPBJ.js';
2
+ import { isPlainObject, isDateLiteral, JqlError, resolveDate } from './chunk-PNBQ2HIJ.js';
102
3
 
103
4
  // src/targets/mongo.ts
104
5
  var MONGO_CAPABILITIES = Object.freeze({