@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.
- package/CHANGELOG.md +44 -1
- package/conformance/cases.json +2 -0
- package/dist/chunk-2ZMIVDES.cjs +1661 -0
- package/dist/chunk-2ZMIVDES.cjs.map +1 -0
- package/dist/chunk-6PWYYYWU.js +1637 -0
- package/dist/chunk-6PWYYYWU.js.map +1 -0
- package/dist/chunk-74QKHM6Y.cjs +145 -0
- package/dist/chunk-74QKHM6Y.cjs.map +1 -0
- package/dist/chunk-ATRXHPBJ.js +112 -0
- package/dist/chunk-ATRXHPBJ.js.map +1 -0
- package/dist/chunk-EJFHVGBC.cjs +63 -0
- package/dist/chunk-EJFHVGBC.cjs.map +1 -0
- package/dist/chunk-ITPY6VTV.cjs +115 -0
- package/dist/chunk-ITPY6VTV.cjs.map +1 -0
- package/dist/chunk-JK6OYDLX.js +712 -0
- package/dist/chunk-JK6OYDLX.js.map +1 -0
- package/dist/chunk-N7DS4QNW.js +60 -0
- package/dist/chunk-N7DS4QNW.js.map +1 -0
- package/dist/chunk-PNBQ2HIJ.js +133 -0
- package/dist/chunk-PNBQ2HIJ.js.map +1 -0
- package/dist/chunk-VMFEQXBB.js +578 -0
- package/dist/chunk-VMFEQXBB.js.map +1 -0
- package/dist/cjs/text/index.d.ts +1 -1
- package/dist/cjs/text/parse.d.ts +2 -1
- package/dist/cjs/text/quote.d.ts +17 -0
- package/dist/cjs/text/suggest.d.ts +27 -5
- package/dist/cli.js +13 -2395
- package/dist/cli.js.map +1 -1
- package/dist/global.cjs +26 -1849
- package/dist/global.cjs.map +1 -1
- package/dist/global.js +8 -1831
- package/dist/global.js.map +1 -1
- package/dist/index.cjs +143 -1996
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -2493
- package/dist/index.js.map +1 -1
- package/dist/mongo.cjs +20 -119
- package/dist/mongo.cjs.map +1 -1
- package/dist/mongo.js +2 -101
- package/dist/mongo.js.map +1 -1
- package/dist/text/index.d.ts +1 -1
- package/dist/text/parse.d.ts +2 -1
- package/dist/text/quote.d.ts +17 -0
- package/dist/text/suggest.d.ts +27 -5
- package/dist/text.cjs +146 -101
- package/dist/text.cjs.map +1 -1
- package/dist/text.js +3 -665
- package/dist/text.js.map +1 -1
- package/docs/course/11-the-search-box.md +49 -2
- package/docs/course/16-extending.md +1 -1
- package/docs/reference/api.md +14 -1
- package/docs/reference/specification.md +9 -2
- package/docs/reference/text-syntax.md +41 -6
- package/package.json +2 -2
package/dist/mongo.js.map
CHANGED
|
@@ -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.js","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":[],"mappings":"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","file":"mongo.js","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/text/index.d.ts
CHANGED
|
@@ -8,5 +8,5 @@ export { parseText, MAX_TEXT_CHARS, MAX_TEXT_DEPTH, TEXT_OPERATORS } from "./par
|
|
|
8
8
|
export { suggest } from "./suggest.js";
|
|
9
9
|
export { toText } from "./write.js";
|
|
10
10
|
export type { TextOptions } from "./parse.js";
|
|
11
|
-
export type { Suggestions } from "./suggest.js";
|
|
11
|
+
export type { Suggestions, SuggestOptions } from "./suggest.js";
|
|
12
12
|
export type { TextForm, Unexpressed } from "./write.js";
|
package/dist/text/parse.d.ts
CHANGED
|
@@ -20,7 +20,8 @@ import { type Vocabulary } from "../vocabulary.js";
|
|
|
20
20
|
* at:>-1h the last hour, and it stays the last hour
|
|
21
21
|
* has:rule -has:rule the field is set, or is not
|
|
22
22
|
* (verdict:human $or score:<20) $and $not path:/health
|
|
23
|
-
* "
|
|
23
|
+
* "connection reset" a phrase, spaces and all — matched against one
|
|
24
|
+
* value, never joined across two fields
|
|
24
25
|
* ```
|
|
25
26
|
*
|
|
26
27
|
* **Adjacent terms mean AND**, which is what narrowing means. `$or` binds more loosely than
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writing a value so the tokenizer reads it back whole.
|
|
3
|
+
*
|
|
4
|
+
* Shared by the writer and by completion, because both hand a value to somebody who will
|
|
5
|
+
* feed it straight back to the parser. They disagreed once: `toText` quoted a value with a
|
|
6
|
+
* space in it and `suggest` did not, so completing `status:` with `in progress` produced
|
|
7
|
+
* `status:in progress` — which parses as `status:in` and a loose search word. A completion
|
|
8
|
+
* that changes the query is worse than no completion at all, and one copy of this rule is
|
|
9
|
+
* how the two stay honest.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* The value, quoted if it has to be — or `undefined` when the syntax cannot write it.
|
|
13
|
+
*
|
|
14
|
+
* `inSet` is for a value inside `$in(…)`, where a comma separates values and so has to be
|
|
15
|
+
* quoted as well.
|
|
16
|
+
*/
|
|
17
|
+
export declare function quote(value: string, inSet: boolean): string | undefined;
|
package/dist/text/suggest.d.ts
CHANGED
|
@@ -3,14 +3,36 @@ import { type Vocabulary } from "../vocabulary.js";
|
|
|
3
3
|
* What to offer for the token the caret is in.
|
|
4
4
|
*
|
|
5
5
|
* Returns the completions and the span they replace, so a caller can put one in without
|
|
6
|
-
* disturbing the rest of the input. A token that already names a field with a
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* disturbing the rest of the input. A token that already names a field with a known set of
|
|
7
|
+
* values completes the value; anything else completes the field name. Every name offered
|
|
8
|
+
* comes from the vocabulary the parser reads, so nothing offered is something the language
|
|
9
|
+
* would not accept.
|
|
10
|
+
*
|
|
11
|
+
* The rule the whole module is written to: **what is offered, inserted, means what it
|
|
12
|
+
* says.** A completion is text somebody will press Tab on without reading, so one that
|
|
13
|
+
* parses into a different query is worse than no completion — the box looks as though it
|
|
14
|
+
* filled itself in correctly and the result is wrong.
|
|
10
15
|
*/
|
|
11
16
|
export interface Suggestions {
|
|
12
17
|
readonly options: readonly string[];
|
|
13
18
|
readonly from: number;
|
|
14
19
|
readonly to: number;
|
|
15
20
|
}
|
|
16
|
-
|
|
21
|
+
/** Anything completion needs that the vocabulary cannot know when it is written. */
|
|
22
|
+
export interface SuggestOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Values seen at run time, by field name — the statuses actually in the table, the paths
|
|
25
|
+
* actually in the log.
|
|
26
|
+
*
|
|
27
|
+
* A vocabulary's `values` is a *closed set*: what the field is allowed to hold, known
|
|
28
|
+
* when the vocabulary is written. Plenty of fields have no such set and still have a
|
|
29
|
+
* handful of values in practice, and a console that has just listed a thousand rows knows
|
|
30
|
+
* them. Passing them here completes the field; leaving them out still offers nothing,
|
|
31
|
+
* because guessing is inventing options rather than completing them.
|
|
32
|
+
*
|
|
33
|
+
* Declared values come first and the two are merged, so a field may have both. Names are
|
|
34
|
+
* resolved through the vocabulary, so an alias works.
|
|
35
|
+
*/
|
|
36
|
+
readonly values?: Readonly<Record<string, readonly string[]>> | undefined;
|
|
37
|
+
}
|
|
38
|
+
export declare function suggest(input: string, caret: number, vocabulary?: Vocabulary<never, object>, options?: SuggestOptions): Suggestions;
|
package/dist/text.cjs
CHANGED
|
@@ -1,83 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
var
|
|
5
|
-
var SHAPE = /^(?:\d+(?:ms|s|m|h|d|w))+$/;
|
|
6
|
-
var UNITS = {
|
|
7
|
-
ms: 1,
|
|
8
|
-
s: 1e3,
|
|
9
|
-
m: 6e4,
|
|
10
|
-
h: 36e5,
|
|
11
|
-
d: 864e5,
|
|
12
|
-
w: 6048e5
|
|
13
|
-
};
|
|
14
|
-
function parseDuration(text) {
|
|
15
|
-
if (!SHAPE.test(text)) return void 0;
|
|
16
|
-
let total = 0;
|
|
17
|
-
PART.lastIndex = 0;
|
|
18
|
-
for (; ; ) {
|
|
19
|
-
const part = PART.exec(text);
|
|
20
|
-
if (part === null) break;
|
|
21
|
-
const count = Number(part[1]);
|
|
22
|
-
if (!Number.isSafeInteger(count)) return void 0;
|
|
23
|
-
total += count * UNITS[part[2]];
|
|
24
|
-
}
|
|
25
|
-
return Number.isSafeInteger(total) ? total : void 0;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// src/errors.ts
|
|
29
|
-
var JqlError = class extends Error {
|
|
30
|
-
/**
|
|
31
|
-
* Where in the query the problem is, written the way you would reach it in code:
|
|
32
|
-
* `$or[1].age.$gt`. Empty for the query as a whole.
|
|
33
|
-
*/
|
|
34
|
-
at;
|
|
35
|
-
constructor(message, at = "") {
|
|
36
|
-
super(at === "" ? message : `at ${show(at)}: ${message}`);
|
|
37
|
-
this.name = "JqlError";
|
|
38
|
-
this.at = at;
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
function show(at) {
|
|
42
|
-
return at.length > 80 ? `${at.slice(0, 80)}\u2026` : at;
|
|
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 describe(value) {
|
|
52
|
-
if (value === null) return "null";
|
|
53
|
-
if (typeof value === "function") return "a function";
|
|
54
|
-
if (Array.isArray(value)) return "an array";
|
|
55
|
-
if (value instanceof RegExp) return "a RegExp object";
|
|
56
|
-
if (value instanceof Date) return "a Date object";
|
|
57
|
-
if (value instanceof Map) return "a Map";
|
|
58
|
-
if (value instanceof Set) return "a Set";
|
|
59
|
-
if (typeof value === "object") {
|
|
60
|
-
if (isPlainObject(value)) return "an object";
|
|
61
|
-
const name = nameOf(value);
|
|
62
|
-
return name === void 0 ? "an object" : `an instance of ${name}`;
|
|
63
|
-
}
|
|
64
|
-
if (typeof value === "string") return `the string ${JSON.stringify(value.length > 40 ? `${value.slice(0, 40)}\u2026` : value)}`;
|
|
65
|
-
return `${typeof value} ${String(value)}`;
|
|
66
|
-
}
|
|
67
|
-
function nameOf(value) {
|
|
68
|
-
const prototype = Object.getPrototypeOf(value);
|
|
69
|
-
const name = prototype?.constructor?.name;
|
|
70
|
-
return typeof name === "string" && name !== "" && name !== "Object" ? name : void 0;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// src/vocabulary.ts
|
|
74
|
-
function checkVocabulary(value, at = "vocabulary") {
|
|
75
|
-
if (value === void 0 || value !== null && typeof value === "object" && value.lookup instanceof Map) return value;
|
|
76
|
-
throw new JqlError(
|
|
77
|
-
`is not a vocabulary, but ${describe(value)}: build one with defineVocabulary()({ fields: \u2026 }) \u2014 the empty parentheses first \u2014 and pass what that returns`,
|
|
78
|
-
at
|
|
79
|
-
);
|
|
80
|
-
}
|
|
3
|
+
var chunkEJFHVGBC_cjs = require('./chunk-EJFHVGBC.cjs');
|
|
4
|
+
var chunk74QKHM6Y_cjs = require('./chunk-74QKHM6Y.cjs');
|
|
81
5
|
|
|
82
6
|
// src/text/parse.ts
|
|
83
7
|
var MAX_TEXT_CHARS = 8192;
|
|
@@ -85,7 +9,7 @@ var MAX_TEXT_DEPTH = 16;
|
|
|
85
9
|
var EXISTS_TERM = "has";
|
|
86
10
|
var TEXT_OPERATORS = ["$and", "$or", "$not", "$in", "$notin"];
|
|
87
11
|
function parseText(input, options = {}) {
|
|
88
|
-
checkVocabulary(options.vocabulary);
|
|
12
|
+
chunkEJFHVGBC_cjs.checkVocabulary(options.vocabulary);
|
|
89
13
|
const trimmed = plainQuotes(String(input).trim());
|
|
90
14
|
const tokens = tokenize(trimmed.length > MAX_TEXT_CHARS ? trimmed.slice(0, MAX_TEXT_CHARS) : trimmed);
|
|
91
15
|
const mode = options.fields ?? (options.vocabulary === void 0 ? "any" : "vocabulary");
|
|
@@ -381,7 +305,7 @@ function readScalar(value, kind) {
|
|
|
381
305
|
const relative = RELATIVE.exec(trimmed);
|
|
382
306
|
if (relative !== null) {
|
|
383
307
|
const span = trimmed.slice(1);
|
|
384
|
-
if (parseDuration(span) === void 0) return void 0;
|
|
308
|
+
if (chunk74QKHM6Y_cjs.parseDuration(span) === void 0) return void 0;
|
|
385
309
|
return { $date: relative[1] === "-" ? { $ago: span } : { $ahead: span } };
|
|
386
310
|
}
|
|
387
311
|
return trimmed !== "" && !Number.isNaN(Date.parse(trimmed)) ? { $date: trimmed } : void 0;
|
|
@@ -414,30 +338,158 @@ function comparison(value, kind) {
|
|
|
414
338
|
}
|
|
415
339
|
}
|
|
416
340
|
|
|
341
|
+
// src/text/quote.ts
|
|
342
|
+
function quote(value, inSet) {
|
|
343
|
+
const plain = !/[\s()"'“”‘’]/.test(value) && !/^[-!$]/.test(value) && !(inSet && value.includes(","));
|
|
344
|
+
if (plain) return value;
|
|
345
|
+
if (!value.includes('"')) return `"${value}"`;
|
|
346
|
+
if (!value.includes("'")) return `'${value}'`;
|
|
347
|
+
return void 0;
|
|
348
|
+
}
|
|
349
|
+
|
|
417
350
|
// src/text/suggest.ts
|
|
418
|
-
function
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
351
|
+
function tokenAt(before) {
|
|
352
|
+
let start = 0;
|
|
353
|
+
let started = false;
|
|
354
|
+
let quote2;
|
|
355
|
+
let setQuote;
|
|
356
|
+
let depth = 0;
|
|
357
|
+
let current = "";
|
|
358
|
+
const begin = (at) => {
|
|
359
|
+
if (!started) {
|
|
360
|
+
start = at;
|
|
361
|
+
started = true;
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
const flush = () => {
|
|
365
|
+
started = false;
|
|
366
|
+
current = "";
|
|
367
|
+
};
|
|
368
|
+
for (let i = 0; i < before.length; i++) {
|
|
369
|
+
const character = before[i];
|
|
370
|
+
if (depth > 0) {
|
|
371
|
+
current += character;
|
|
372
|
+
if (setQuote !== void 0) {
|
|
373
|
+
if (character === setQuote) setQuote = void 0;
|
|
374
|
+
} else if (character === '"' || character === "'") setQuote = character;
|
|
375
|
+
else if (character === "(") depth++;
|
|
376
|
+
else if (character === ")") {
|
|
377
|
+
depth--;
|
|
378
|
+
if (depth === 0) flush();
|
|
379
|
+
}
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
if (quote2 !== void 0) {
|
|
383
|
+
if (character === quote2) quote2 = void 0;
|
|
384
|
+
else current += character;
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
if (character === '"') {
|
|
388
|
+
begin(i);
|
|
389
|
+
quote2 = '"';
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
if (character === "'" && (current === "" || current === "-" || current === "!" || current.endsWith(":"))) {
|
|
393
|
+
begin(i);
|
|
394
|
+
quote2 = "'";
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
if (character === "(") {
|
|
398
|
+
if (/\$(in|notin)$/i.test(current)) {
|
|
399
|
+
current += character;
|
|
400
|
+
depth = 1;
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
flush();
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
if (character === ")") {
|
|
407
|
+
flush();
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
if (/\s/.test(character)) {
|
|
411
|
+
flush();
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
begin(i);
|
|
415
|
+
current += character;
|
|
416
|
+
}
|
|
417
|
+
return start;
|
|
418
|
+
}
|
|
419
|
+
function valuesFor(name, vocabulary, given) {
|
|
420
|
+
const field = vocabulary?.lookup.get(name);
|
|
421
|
+
const declared = field?.values;
|
|
422
|
+
const seen = given?.[name] ?? (field === void 0 ? void 0 : given?.[field.name]);
|
|
423
|
+
if (declared === void 0 && seen === void 0) return void 0;
|
|
424
|
+
const out = [];
|
|
425
|
+
for (const value of [...declared ?? [], ...seen ?? []]) if (!out.includes(value)) out.push(value);
|
|
426
|
+
return out;
|
|
427
|
+
}
|
|
428
|
+
function checkGivenValues(values) {
|
|
429
|
+
if (values === void 0) return;
|
|
430
|
+
if (values === null || typeof values !== "object" || Array.isArray(values)) {
|
|
431
|
+
throw new chunk74QKHM6Y_cjs.JqlError("takes an object of field names to lists of values", "options.values");
|
|
432
|
+
}
|
|
433
|
+
for (const [name, list] of Object.entries(values)) {
|
|
434
|
+
if (!Array.isArray(list) || list.some((value) => typeof value !== "string")) {
|
|
435
|
+
throw new chunk74QKHM6Y_cjs.JqlError("takes a list of strings", `options.values.${name}`);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function suggest(input, caret, vocabulary, options) {
|
|
440
|
+
chunkEJFHVGBC_cjs.checkVocabulary(vocabulary);
|
|
441
|
+
checkGivenValues(options?.values);
|
|
442
|
+
const to = Math.max(0, Math.min(caret, input.length));
|
|
443
|
+
const before = input.slice(0, to);
|
|
444
|
+
const start = tokenAt(before);
|
|
422
445
|
const token = before.slice(start);
|
|
423
|
-
const to = caret;
|
|
424
446
|
const negated = token.startsWith("-") || token.startsWith("!");
|
|
425
447
|
const body = negated ? token.slice(1) : token;
|
|
426
448
|
const sign = negated ? token[0] : "";
|
|
449
|
+
const none = { options: [], from: start, to };
|
|
450
|
+
if (body.startsWith('"') || body.startsWith("'")) return none;
|
|
427
451
|
const colon = body.indexOf(":");
|
|
428
452
|
if (colon === -1) {
|
|
429
|
-
const
|
|
430
|
-
if (
|
|
453
|
+
const partial = body.toLowerCase();
|
|
454
|
+
if (partial.startsWith("$")) return { options: TEXT_OPERATORS.filter((name2) => name2.startsWith(partial)), from: start, to };
|
|
431
455
|
const names = vocabulary?.names ?? [];
|
|
432
|
-
return { options: names.filter((name2) => name2.startsWith(
|
|
456
|
+
return { options: names.filter((name2) => name2.startsWith(partial)).map((name2) => `${sign}${name2}:`), from: start, to };
|
|
433
457
|
}
|
|
434
458
|
const name = body.slice(0, colon).toLowerCase();
|
|
435
|
-
const
|
|
436
|
-
if (values === void 0) return { options: [], from: start, to };
|
|
437
|
-
const partial = body.slice(colon + 1).toLowerCase();
|
|
459
|
+
const rest = body.slice(colon + 1);
|
|
438
460
|
const prefix = `${sign}${name}:`;
|
|
439
|
-
const
|
|
440
|
-
|
|
461
|
+
const values = valuesFor(name, vocabulary, options?.values);
|
|
462
|
+
const set = /^\$(in|notin)\(/i.exec(rest);
|
|
463
|
+
if (set !== null) {
|
|
464
|
+
if (values === void 0) return none;
|
|
465
|
+
const opener = set[0];
|
|
466
|
+
const inside = rest.slice(opener.length);
|
|
467
|
+
const comma = inside.lastIndexOf(",");
|
|
468
|
+
const kept = comma === -1 ? "" : inside.slice(0, comma + 1);
|
|
469
|
+
const typed = comma === -1 ? inside : inside.slice(comma + 1);
|
|
470
|
+
const partial = typed.trimStart();
|
|
471
|
+
const spacing = typed.slice(0, typed.length - partial.length);
|
|
472
|
+
return { options: offer(values, partial, true, (written) => `${prefix}${opener}${kept}${spacing}${written}`), from: start, to };
|
|
473
|
+
}
|
|
474
|
+
const forms = rest.startsWith("$") ? ["$in(", "$notin("].filter((form) => form.startsWith(rest)).map((form) => `${prefix}${form}`) : [];
|
|
475
|
+
if (values === void 0) return { options: forms, from: start, to };
|
|
476
|
+
return { options: [...forms, ...offer(values, unopened(rest), false, (written) => `${prefix}${written}`)], from: start, to };
|
|
477
|
+
}
|
|
478
|
+
function unopened(rest) {
|
|
479
|
+
const first = rest[0];
|
|
480
|
+
if (first !== '"' && first !== "'") return rest;
|
|
481
|
+
const body = rest.slice(1);
|
|
482
|
+
return body.endsWith(first) ? body.slice(0, -1) : body;
|
|
483
|
+
}
|
|
484
|
+
function offer(values, partial, inSet, write) {
|
|
485
|
+
const wanted = partial.toLowerCase();
|
|
486
|
+
const out = [];
|
|
487
|
+
for (const value of values) {
|
|
488
|
+
if (!value.toLowerCase().startsWith(wanted)) continue;
|
|
489
|
+
const written = quote(value, inSet);
|
|
490
|
+
if (written !== void 0) out.push(write(written));
|
|
491
|
+
}
|
|
492
|
+
return out;
|
|
441
493
|
}
|
|
442
494
|
|
|
443
495
|
// src/text/write.ts
|
|
@@ -457,7 +509,7 @@ function inferKind(condition) {
|
|
|
457
509
|
return numeric ? "number" : "text";
|
|
458
510
|
}
|
|
459
511
|
function toText(query, options = {}) {
|
|
460
|
-
const vocabulary = checkVocabulary(options.vocabulary);
|
|
512
|
+
const vocabulary = chunkEJFHVGBC_cjs.checkVocabulary(options.vocabulary);
|
|
461
513
|
const mode = options.fields ?? (vocabulary === void 0 ? "any" : "vocabulary");
|
|
462
514
|
const left = [];
|
|
463
515
|
const text = writeQuery(query, { vocabulary, mode, left }, "", false);
|
|
@@ -650,13 +702,6 @@ function writeDate(value) {
|
|
|
650
702
|
}
|
|
651
703
|
return void 0;
|
|
652
704
|
}
|
|
653
|
-
function quote(value, inSet) {
|
|
654
|
-
const plain = !/[\s()"'“”‘’]/.test(value) && !/^[-!$]/.test(value) && !(inSet && value.includes(","));
|
|
655
|
-
if (plain) return value;
|
|
656
|
-
if (!value.includes('"')) return `"${value}"`;
|
|
657
|
-
if (!value.includes("'")) return `'${value}'`;
|
|
658
|
-
return void 0;
|
|
659
|
-
}
|
|
660
705
|
function describeValue(value) {
|
|
661
706
|
if (value === null) return "null";
|
|
662
707
|
if (Array.isArray(value)) return "a list";
|