@osqd/jql 0.1.1
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 +220 -0
- package/LICENSE +102 -0
- package/README.md +84 -0
- package/bin/jql.mjs +5 -0
- package/conformance/cases.json +290 -0
- package/dist/array.d.ts +11 -0
- package/dist/async.d.ts +44 -0
- package/dist/canonical.d.ts +18 -0
- package/dist/cjs/array.d.ts +11 -0
- package/dist/cjs/async.d.ts +44 -0
- package/dist/cjs/canonical.d.ts +18 -0
- package/dist/cjs/cli.d.ts +15 -0
- package/dist/cjs/collections.d.ts +34 -0
- package/dist/cjs/core.d.ts +117 -0
- package/dist/cjs/errors.d.ts +17 -0
- package/dist/cjs/explain.d.ts +30 -0
- package/dist/cjs/global.d.ts +90 -0
- package/dist/cjs/group.d.ts +47 -0
- package/dist/cjs/index.d.ts +25 -0
- package/dist/cjs/internal/closest.d.ts +9 -0
- package/dist/cjs/internal/duration.d.ts +15 -0
- package/dist/cjs/internal/equal.d.ts +34 -0
- package/dist/cjs/internal/glob.d.ts +30 -0
- package/dist/cjs/internal/order.d.ts +24 -0
- package/dist/cjs/internal/path.d.ts +109 -0
- package/dist/cjs/internal/record.d.ts +18 -0
- package/dist/cjs/internal/values.d.ts +41 -0
- package/dist/cjs/limits.d.ts +64 -0
- package/dist/cjs/operators.d.ts +81 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/plan.d.ts +79 -0
- package/dist/cjs/search.d.ts +42 -0
- package/dist/cjs/targets/mongo.d.ts +55 -0
- package/dist/cjs/text/index.d.ts +12 -0
- package/dist/cjs/text/parse.d.ts +91 -0
- package/dist/cjs/text/suggest.d.ts +16 -0
- package/dist/cjs/text/write.d.ts +34 -0
- package/dist/cjs/types.d.ts +236 -0
- package/dist/cjs/vocabulary.d.ts +106 -0
- package/dist/cli.d.ts +15 -0
- package/dist/cli.js +2729 -0
- package/dist/cli.js.map +1 -0
- package/dist/collections.d.ts +34 -0
- package/dist/core.d.ts +117 -0
- package/dist/errors.d.ts +17 -0
- package/dist/explain.d.ts +30 -0
- package/dist/global.cjs +1953 -0
- package/dist/global.cjs.map +1 -0
- package/dist/global.d.ts +90 -0
- package/dist/global.js +1950 -0
- package/dist/global.js.map +1 -0
- package/dist/group.d.ts +47 -0
- package/dist/index.cjs +2529 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +2495 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/closest.d.ts +9 -0
- package/dist/internal/duration.d.ts +15 -0
- package/dist/internal/equal.d.ts +34 -0
- package/dist/internal/glob.d.ts +30 -0
- package/dist/internal/order.d.ts +24 -0
- package/dist/internal/path.d.ts +109 -0
- package/dist/internal/record.d.ts +18 -0
- package/dist/internal/values.d.ts +41 -0
- package/dist/limits.d.ts +64 -0
- package/dist/mongo.cjs +357 -0
- package/dist/mongo.cjs.map +1 -0
- package/dist/mongo.js +354 -0
- package/dist/mongo.js.map +1 -0
- package/dist/operators.d.ts +81 -0
- package/dist/plan.d.ts +79 -0
- package/dist/search.d.ts +42 -0
- package/dist/targets/mongo.d.ts +55 -0
- package/dist/text/index.d.ts +12 -0
- package/dist/text/parse.d.ts +91 -0
- package/dist/text/suggest.d.ts +16 -0
- package/dist/text/write.d.ts +34 -0
- package/dist/text.cjs +674 -0
- package/dist/text.cjs.map +1 -0
- package/dist/text.js +667 -0
- package/dist/text.js.map +1 -0
- package/dist/types.d.ts +236 -0
- package/dist/vocabulary.d.ts +106 -0
- package/docs/course/01-first-query.md +217 -0
- package/docs/course/02-operators.md +285 -0
- package/docs/course/03-arrays-and-paths.md +239 -0
- package/docs/course/04-combining.md +221 -0
- package/docs/course/05-dates.md +214 -0
- package/docs/course/06-typed-queries.md +240 -0
- package/docs/course/07-requests.md +261 -0
- package/docs/course/08-grouping.md +210 -0
- package/docs/course/09-explaining.md +171 -0
- package/docs/course/10-vocabulary.md +276 -0
- package/docs/course/11-the-search-box.md +349 -0
- package/docs/course/12-untrusted.md +257 -0
- package/docs/course/13-saved-filters.md +199 -0
- package/docs/course/14-streams-and-cli.md +276 -0
- package/docs/course/15-pushdown.md +240 -0
- package/docs/course/16-extending.md +199 -0
- package/docs/course/index.md +185 -0
- package/docs/design/decisions.md +198 -0
- package/docs/design/performance.md +102 -0
- package/docs/guides/adopting.md +81 -0
- package/docs/guides/pushdown.md +147 -0
- package/docs/guides/typescript.md +115 -0
- package/docs/guides/untrusted-input.md +86 -0
- package/docs/index.md +102 -0
- package/docs/reference/api.md +266 -0
- package/docs/reference/cli.md +103 -0
- package/docs/reference/index.md +12 -0
- package/docs/reference/specification.md +549 -0
- package/docs/reference/text-syntax.md +152 -0
- package/docs/start/quick-start.md +84 -0
- package/package.json +136 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Did you mean" for names that almost exist.
|
|
3
|
+
*
|
|
4
|
+
* An unknown operator is refused either way; naming the one that was probably meant turns
|
|
5
|
+
* a lookup in the documentation into a one-character fix.
|
|
6
|
+
*/
|
|
7
|
+
export declare function closest(word: string, candidates: Iterable<string>): string | undefined;
|
|
8
|
+
/** The suffix of an error sentence that offers the likely name, or nothing. */
|
|
9
|
+
export declare function didYouMean(word: string, candidates: Iterable<string>): string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durations, for the relative dates a saved filter is written with.
|
|
3
|
+
*
|
|
4
|
+
* `"1h"`, `"30m"`, `"1h30m"`, `"7d"`. Written as one or more counts with a unit, largest
|
|
5
|
+
* first by convention but in any order, because a filter is typed by a person and
|
|
6
|
+
* `"30m1h"` means what it says.
|
|
7
|
+
*
|
|
8
|
+
* Months and years are deliberately absent: neither has a fixed length, so `"1mo"` would
|
|
9
|
+
* mean something slightly different in February, and a filter whose window changes size
|
|
10
|
+
* with the calendar is one nobody can reason about. Use days or weeks.
|
|
11
|
+
*/
|
|
12
|
+
/** The unit names, for an error that says what is accepted. */
|
|
13
|
+
export declare const DURATION_UNITS: string[];
|
|
14
|
+
/** A duration in milliseconds, or `undefined` when the text is not one. */
|
|
15
|
+
export declare function parseDuration(text: string): number | undefined;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Equality between a document value and a query literal.
|
|
3
|
+
*
|
|
4
|
+
* Built once per literal rather than called generically, because the literal is known at
|
|
5
|
+
* compile time and the document value is the only thing that changes: a primitive
|
|
6
|
+
* literal becomes one `===`, and only an object or array literal pays for a walk.
|
|
7
|
+
*
|
|
8
|
+
* The rules, which the specification states and every implementation must share:
|
|
9
|
+
* objects are equal when they have the same keys with equal values, **in any order**
|
|
10
|
+
* (JSON does not promise key order, so a comparison that depended on it would depend on
|
|
11
|
+
* whoever serialised the query); a key whose value is `undefined` counts as absent;
|
|
12
|
+
* arrays are equal element by element, in order; a `$date` literal equals any value that
|
|
13
|
+
* reads as the same instant.
|
|
14
|
+
*/
|
|
15
|
+
export type Equals = (value: unknown) => boolean;
|
|
16
|
+
/**
|
|
17
|
+
* The same whole number as a big integer, when a literal is one and could be written either
|
|
18
|
+
* way.
|
|
19
|
+
*
|
|
20
|
+
* Settled once, when the query compiles, so the comparison stays a single `===` against a
|
|
21
|
+
* value prepared in advance. Only a whole number gets one: nothing is a big integer and a
|
|
22
|
+
* fraction, and a string or a boolean can never be one either.
|
|
23
|
+
*/
|
|
24
|
+
export declare function alsoBig(literal: unknown): bigint | undefined;
|
|
25
|
+
export declare function equalsLiteral(literal: unknown, now: number, ignoreCase?: boolean): Equals;
|
|
26
|
+
/**
|
|
27
|
+
* Equality between two values that both came out of a document, for `$field` references.
|
|
28
|
+
*
|
|
29
|
+
* The same rules as equality with a literal — same type, arrays in order, objects in any
|
|
30
|
+
* key order — but neither side is known when the query compiles, so nothing can be
|
|
31
|
+
* prepared. Missing is not equal to anything, including itself: a reference that reaches
|
|
32
|
+
* nothing matches nothing.
|
|
33
|
+
*/
|
|
34
|
+
export declare function valuesEqual(a: unknown, b: unknown): boolean;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Glob matching: `*`, `?`, and `\` to escape either of them.
|
|
3
|
+
*
|
|
4
|
+
* What people mean when they reach for a pattern in a search box — `/api/*`,
|
|
5
|
+
* `*.example.com` — and the reason it is not `$regex` with the sharp edges filed off: this
|
|
6
|
+
* runs without a regular-expression engine, so it is available when patterns are turned
|
|
7
|
+
* off for untrusted callers, and it cannot backtrack exponentially.
|
|
8
|
+
*
|
|
9
|
+
* No character classes. `[a-z]` is a literal `[a-z]`, because half a class syntax is worse
|
|
10
|
+
* than none: somebody would write `[!abc]` expecting negation and get a match against those
|
|
11
|
+
* four characters instead, with nothing to say so.
|
|
12
|
+
*
|
|
13
|
+
* **A pattern is taken apart once and becomes the narrowest matcher that fits it.** Nearly
|
|
14
|
+
* every real glob is a prefix, a suffix, something in the middle, or a run of literals
|
|
15
|
+
* separated by stars, and each of those is one or two native string calls per value. Only a
|
|
16
|
+
* pattern with `?` in it walks character by character, and only that walk pays for splitting
|
|
17
|
+
* the text into characters — which, measured against `startsWith`, was four times the cost of
|
|
18
|
+
* the whole comparison.
|
|
19
|
+
*/
|
|
20
|
+
export type GlobMatcher = (text: string) => boolean;
|
|
21
|
+
export declare function compileGlob(pattern: string): GlobMatcher;
|
|
22
|
+
/**
|
|
23
|
+
* The same glob as the body of a regular expression, for a target that speaks patterns
|
|
24
|
+
* rather than running the matcher — a store this query is being pushed down to.
|
|
25
|
+
*
|
|
26
|
+
* It shares the tokenizer with the matcher on purpose: an escape read one way here and
|
|
27
|
+
* another way there would mean a query that found different things depending on which half
|
|
28
|
+
* of the system answered it.
|
|
29
|
+
*/
|
|
30
|
+
export declare function compileGlobPattern(pattern: string, quoteLiteral: (text: string) => string): string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One total order over every value a document can hold.
|
|
3
|
+
*
|
|
4
|
+
* A sort has to put *something* first when one item's field is a number and another's is
|
|
5
|
+
* a string, or missing. The order is fixed and written down in the specification, so two
|
|
6
|
+
* implementations sort a shared result the same way:
|
|
7
|
+
*
|
|
8
|
+
* missing and null < numbers < strings < objects < booleans < dates
|
|
9
|
+
*
|
|
10
|
+
* Strings compare by UTF-16 code unit, not by locale. A locale-aware sort would put a
|
|
11
|
+
* shared link's rows in a different order for each reader, which is worse than an order
|
|
12
|
+
* that is merely unidiomatic for some of them. Objects compare equal to one another, so
|
|
13
|
+
* their order is the order they arrived in.
|
|
14
|
+
*/
|
|
15
|
+
export declare function compareValues(a: unknown, b: unknown): number;
|
|
16
|
+
/**
|
|
17
|
+
* The value an item sorts by, from everything its path reached.
|
|
18
|
+
*
|
|
19
|
+
* A path that reaches an array — or fans out over one — reaches several values. Ascending
|
|
20
|
+
* sorts by the smallest of them and descending by the largest: an order
|
|
21
|
+
* by "tags" puts first whatever has the tag that sorts first. Arrays nested past sixteen
|
|
22
|
+
* levels are passed over, so an array that contains itself ends the walk.
|
|
23
|
+
*/
|
|
24
|
+
export declare function sortValue(values: readonly unknown[], descending: boolean, depth?: number): unknown;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reaching a field inside a document.
|
|
3
|
+
*
|
|
4
|
+
* A path is dot-separated: `address.city`, `items.id`, `items.0.id`. Three rules:
|
|
5
|
+
*
|
|
6
|
+
* - **An array is seen through.** `items.id` reaches the `id` of every element, and a
|
|
7
|
+
* condition on it holds when it holds for any of them.
|
|
8
|
+
* - **A numeric segment on an array is a position.** `items.0.id` is the first element's.
|
|
9
|
+
* - **A `Map` is read by key**, so a document that keeps a lookup table in a `Map` can be
|
|
10
|
+
* queried the same way as one that keeps it in an object.
|
|
11
|
+
*
|
|
12
|
+
* Only own properties are read. `{ constructor: { $exists: true } }` would otherwise match
|
|
13
|
+
* every object ever made, and `__proto__` would reach somewhere a query has no business
|
|
14
|
+
* being. The check costs a `hasOwn` call, so it is paid only for the handful of names
|
|
15
|
+
* `Object.prototype` and `Map.prototype` actually have — every other name is read
|
|
16
|
+
* directly. `Map.prototype` is on the list because a `Map` document read directly would
|
|
17
|
+
* answer `size` or `get` with its own method rather than with the entry of that name.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Calls `test` with each value the path reaches, stopping at the first `true`.
|
|
21
|
+
*
|
|
22
|
+
* A path that reaches nothing calls `test(undefined)` once, so "missing" is a value a
|
|
23
|
+
* condition can ask about: that is how `{ field: null }` and `$exists: false` see it.
|
|
24
|
+
*/
|
|
25
|
+
export type Reach = (document: unknown, test: (value: unknown) => boolean) => boolean;
|
|
26
|
+
/** A single-valued read, for paths that cannot fan out. Absent when the path can. */
|
|
27
|
+
export type Read = (document: unknown) => unknown;
|
|
28
|
+
/** What a probe returns when the path met an array before its last segment, and so may reach several values. */
|
|
29
|
+
export declare const FANOUT: unique symbol;
|
|
30
|
+
/**
|
|
31
|
+
* A straight read of a multi-segment path: the one value it reaches, or `FANOUT` if an
|
|
32
|
+
* array stood in the way and only `Reach` can answer.
|
|
33
|
+
*
|
|
34
|
+
* The point is the common case. `address.city` on a record with no arrays on the way has
|
|
35
|
+
* exactly one value, and reading it directly lets the engine test it the way a hand-written
|
|
36
|
+
* `p.address.city === "x"` would — one read, one comparison — instead of through a walk and
|
|
37
|
+
* a callback. A missing or primitive step reads as `undefined`, the same as `Reach` sees it.
|
|
38
|
+
*/
|
|
39
|
+
export type Probe = (document: unknown) => unknown;
|
|
40
|
+
/** Whether a name could be found on a plain object's prototype, and so must always be asked for carefully. */
|
|
41
|
+
export declare function needsGuard(key: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* What a plain object's prototype is, for the cheapest plainness test there is.
|
|
44
|
+
*
|
|
45
|
+
* `document.__proto__ === PLAIN` is a map check — about as fast as reading a field — where
|
|
46
|
+
* `Object.getPrototypeOf` is a call and `Object.hasOwn` is a second lookup. Reading it is
|
|
47
|
+
* safe in both directions: a document carrying its own `__proto__` field (which is what
|
|
48
|
+
* `JSON.parse` makes of one) simply fails the test and takes the careful path, and so does
|
|
49
|
+
* anything with a prototype of its own.
|
|
50
|
+
*/
|
|
51
|
+
export declare const PLAIN: Object;
|
|
52
|
+
/**
|
|
53
|
+
* One field of one value: own properties and `Map` entries, nothing else.
|
|
54
|
+
*
|
|
55
|
+
* Ownership is checked **before** the read, not after, and for every name rather than for a
|
|
56
|
+
* list of known-inherited ones. The list was only ever `Object.prototype` and
|
|
57
|
+
* `Map.prototype`, so a document that was a class instance answered `{ role: "admin" }` from
|
|
58
|
+
* its prototype — and `{ secret: … }` by *running* an inherited getter, which is a query
|
|
59
|
+
* executing code the document's author wrote. Asking first is also why no accessor outside
|
|
60
|
+
* the item can run at all.
|
|
61
|
+
*/
|
|
62
|
+
export declare function readOwn(value: object, key: string): unknown;
|
|
63
|
+
/**
|
|
64
|
+
* The value at a single-segment path.
|
|
65
|
+
*
|
|
66
|
+
* The overwhelmingly common case — `{ id: 2 }` — so it is written out: a direct property
|
|
67
|
+
* read, with the `Map` check paid only when the read found nothing, which a plain object
|
|
68
|
+
* with the field never does.
|
|
69
|
+
*/
|
|
70
|
+
export declare function readKey(key: string): Read;
|
|
71
|
+
/** The whole document, for queries that are conditions on the value itself. */
|
|
72
|
+
export declare const readSelf: Read;
|
|
73
|
+
/**
|
|
74
|
+
* Builds the walk for a multi-segment path.
|
|
75
|
+
*
|
|
76
|
+
* A loop while the path meets only objects, which is nearly always, and the recursive
|
|
77
|
+
* fan-out only from the first array it meets. `address.city` on a record with no arrays in
|
|
78
|
+
* it costs two property reads and one call.
|
|
79
|
+
*/
|
|
80
|
+
export declare function reachPath(keys: readonly string[]): Reach;
|
|
81
|
+
/** Builds the straight read for a multi-segment path. */
|
|
82
|
+
export declare function probePath(keys: readonly string[]): Probe;
|
|
83
|
+
/** The same, from a value some other accessor produced. */
|
|
84
|
+
export declare function probeFrom(read: Read, keys: readonly string[]): Probe;
|
|
85
|
+
/** Continues a walk from a value some other accessor produced. */
|
|
86
|
+
export declare function reachFrom(read: Read, keys: readonly string[]): Reach;
|
|
87
|
+
/**
|
|
88
|
+
* How many segments a path may have.
|
|
89
|
+
*
|
|
90
|
+
* The same number as the document depth above, and for the same reason: a path longer than
|
|
91
|
+
* the deepest document a walk will follow can reach nothing anyway, so refusing it costs a
|
|
92
|
+
* query nothing real. What it buys is two things. A field name is the one part of a query
|
|
93
|
+
* whose size the node count does not bound — `{"a.a.a…": 1}` is a single node however long
|
|
94
|
+
* it is — and each segment becomes an object and a step in a walk, so a field name pasted
|
|
95
|
+
* from somewhere could turn a small query into a large one.
|
|
96
|
+
*
|
|
97
|
+
* And it makes the same path mean the same thing everywhere. Projection walked its own tree
|
|
98
|
+
* of segments and stopped at sixty-four, silently, so a query matched on a path that
|
|
99
|
+
* `fields` then dropped from the result: the filter and the projection disagreed about a
|
|
100
|
+
* path they were both given.
|
|
101
|
+
*/
|
|
102
|
+
export declare const MAX_PATH_SEGMENTS = 512;
|
|
103
|
+
/**
|
|
104
|
+
* Splits a field name into segments, refusing the ones that can reach nothing.
|
|
105
|
+
*
|
|
106
|
+
* Returns the reason as a sentence for the caller to raise, so this module stays free of
|
|
107
|
+
* the error type and its location bookkeeping.
|
|
108
|
+
*/
|
|
109
|
+
export declare function splitPath(path: string): string[] | string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writing a field onto a result object.
|
|
3
|
+
*
|
|
4
|
+
* `target[key] = value` looks harmless until the key is `__proto__`. Assignment goes
|
|
5
|
+
* through the setter inherited from `Object.prototype`, which *replaces the object's
|
|
6
|
+
* prototype* instead of adding a property — so the field the caller asked for is missing
|
|
7
|
+
* and the object answers to names nobody put on it.
|
|
8
|
+
*
|
|
9
|
+
* It is reachable from data rather than from code: `JSON.parse('{"__proto__": …}')` makes
|
|
10
|
+
* an own `__proto__` property, so any document read from a file, a request body or a store
|
|
11
|
+
* can carry one. Projecting such a document used to lose that field and hand back a result
|
|
12
|
+
* whose prototype was whatever the document said, which the projection's own test could not
|
|
13
|
+
* see because the property it was looking for was genuinely there — on the prototype.
|
|
14
|
+
*
|
|
15
|
+
* Defining the property puts it where it was meant to go. Every other key takes the plain
|
|
16
|
+
* assignment, so the guard costs one comparison on the path that builds results.
|
|
17
|
+
*/
|
|
18
|
+
export declare function setField(target: Record<string, unknown>, key: string, value: unknown): void;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { DateLiteral, FieldReference, TypeName } from "../types.js";
|
|
2
|
+
/** An object literal or a JSON-parsed object: what a query document is made of. */
|
|
3
|
+
export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
4
|
+
/** `{ $date: … }` and nothing else. */
|
|
5
|
+
export declare function isDateLiteral(value: unknown): value is DateLiteral;
|
|
6
|
+
/** `{ $field: "path" }` and nothing else: a comparison with another field of the same item. */
|
|
7
|
+
export declare function isFieldReference(value: unknown): value is FieldReference;
|
|
8
|
+
/**
|
|
9
|
+
* The instant a `$date` literal stands for, in epoch milliseconds, or `NaN`.
|
|
10
|
+
*
|
|
11
|
+
* `now` is passed in rather than read here, because a query holding `{ "$ago": "1h" }` has
|
|
12
|
+
* to resolve against one instant for the whole run: reading the clock per comparison would
|
|
13
|
+
* let the window move while a scan is in progress, so two items a second apart could be
|
|
14
|
+
* judged against different hours. It is also what makes a relative filter testable without
|
|
15
|
+
* waiting.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveDate(value: unknown, now: number): number;
|
|
18
|
+
/**
|
|
19
|
+
* An instant from a string, read the same way on every machine.
|
|
20
|
+
*
|
|
21
|
+
* JavaScript reads a date-only string as midnight **UTC** and a timestamp with no offset as
|
|
22
|
+
* **local time** — so `"2026-01-01T12:00:00"` was a different instant in Tokyo and in London,
|
|
23
|
+
* and a query and a document that both held one answered differently depending on where the
|
|
24
|
+
* process happened to be running. That is the failure the language already rules out for
|
|
25
|
+
* `"12/31/2020"`: the same query meaning two things. A missing offset is UTC here, which is
|
|
26
|
+
* the rule the date-only form already follows.
|
|
27
|
+
*/
|
|
28
|
+
export declare function instant(text: string): number;
|
|
29
|
+
export declare function toTime(value: unknown): number;
|
|
30
|
+
/** The type names `$type` knows, in one list, so the error for a wrong one can offer the right one. */
|
|
31
|
+
export declare const TYPE_NAMES: readonly TypeName[];
|
|
32
|
+
/** Whether a document value is of the named type. A missing value is of no type. */
|
|
33
|
+
export declare function isOfType(value: unknown, type: TypeName): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* A readable name for a value in an error sentence.
|
|
36
|
+
*
|
|
37
|
+
* Specific about what it was given, because "a query is an object, not an object" — which
|
|
38
|
+
* is what a `Map`, a `Set` and a class instance all used to produce — tells the reader
|
|
39
|
+
* nothing at all about the thing they passed.
|
|
40
|
+
*/
|
|
41
|
+
export declare function describe(value: unknown): string;
|
package/dist/limits.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How much of a query the engine will take.
|
|
3
|
+
*
|
|
4
|
+
* Queries arrive from places the caller does not control — a URL, a request body, a
|
|
5
|
+
* saved filter somebody else wrote — and every limit here is a way such a query could
|
|
6
|
+
* otherwise cost more than the data it filters. Each one refuses with a `JqlError` rather
|
|
7
|
+
* than truncating, because a query that was quietly cut short answers a different
|
|
8
|
+
* question from the one that was asked.
|
|
9
|
+
*
|
|
10
|
+
* The defaults are far past anything a person writes and far short of anything that
|
|
11
|
+
* hurts. The caps are exported so a caller can say what it accepts in its own
|
|
12
|
+
* documentation and refuse a query before it is even parsed.
|
|
13
|
+
*/
|
|
14
|
+
export interface Limits {
|
|
15
|
+
/** How deep operators may nest. Default 32: recursion over pasted input must stop well short of the stack. */
|
|
16
|
+
readonly maxDepth: number;
|
|
17
|
+
/** How many operators and fields a query may hold in total. Default 10 000. `$in` lists do not count: they cost one set lookup each. */
|
|
18
|
+
readonly maxNodes: number;
|
|
19
|
+
/** The longest `$regex` pattern. Default 1024. */
|
|
20
|
+
readonly maxPatternLength: number;
|
|
21
|
+
/**
|
|
22
|
+
* The longest `$glob`. Default 1024, and 256 for untrusted input.
|
|
23
|
+
*
|
|
24
|
+
* A glob cannot backtrack exponentially, but matching one holding `?` is the length of the
|
|
25
|
+
* pattern times the length of the value — so a megabyte of `?` against a megabyte of text
|
|
26
|
+
* is hours per document. It has a cap of its own because `$regex` is turned *off* for
|
|
27
|
+
* untrusted callers while `$glob` stays on, which is exactly when the cap has to hold.
|
|
28
|
+
*/
|
|
29
|
+
readonly maxGlobLength: number;
|
|
30
|
+
/** How deep `$text` looks into a document. Default 16: past that a document is a graph, not a record. */
|
|
31
|
+
readonly maxTextDepth: number;
|
|
32
|
+
/**
|
|
33
|
+
* Which operators a query may use. Default `"all"`.
|
|
34
|
+
*
|
|
35
|
+
* An allowlist, because a public endpoint's answer to "what may a caller ask?" should be
|
|
36
|
+
* a list it wrote rather than everything the language grows later. `$text` walks a whole
|
|
37
|
+
* document and `$elemMatch` carries a query of its own; an endpoint that needs neither can
|
|
38
|
+
* say so once, here, instead of discovering later which of them somebody found.
|
|
39
|
+
*
|
|
40
|
+
* Names are checked when the query compiles: an operator outside the list is refused by
|
|
41
|
+
* name, never ignored. `$options` and `$comment` are exempt — they modify and annotate
|
|
42
|
+
* rather than ask anything — and `$field` counts as an operator, because comparing two
|
|
43
|
+
* fields is a capability in its own right.
|
|
44
|
+
*/
|
|
45
|
+
readonly allowOperators: readonly string[] | "all";
|
|
46
|
+
/**
|
|
47
|
+
* Whether `$regex` is accepted at all. Default `true`.
|
|
48
|
+
*
|
|
49
|
+
* Turn it off for queries from untrusted callers. No engine can tell a pattern that
|
|
50
|
+
* backtracks for a minute from one that does not without running it, and the string
|
|
51
|
+
* operators (`$contains`, `$startsWith`, `$endsWith`, `$word`) answer almost every
|
|
52
|
+
* question a pattern would, in linear time.
|
|
53
|
+
*/
|
|
54
|
+
readonly allowRegex: boolean;
|
|
55
|
+
}
|
|
56
|
+
/** The defaults, frozen, so they can be spread and never edited in place. */
|
|
57
|
+
export declare const DEFAULT_LIMITS: Readonly<Limits>;
|
|
58
|
+
/**
|
|
59
|
+
* What an untrusted query gets: no patterns, and a tighter size.
|
|
60
|
+
*
|
|
61
|
+
* Public so a service taking queries from a URL can use the same answer as every other
|
|
62
|
+
* service in the project, rather than each one picking its own numbers.
|
|
63
|
+
*/
|
|
64
|
+
export declare const UNTRUSTED_LIMITS: Readonly<Limits>;
|