@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.
Files changed (115) hide show
  1. package/CHANGELOG.md +220 -0
  2. package/LICENSE +102 -0
  3. package/README.md +84 -0
  4. package/bin/jql.mjs +5 -0
  5. package/conformance/cases.json +290 -0
  6. package/dist/array.d.ts +11 -0
  7. package/dist/async.d.ts +44 -0
  8. package/dist/canonical.d.ts +18 -0
  9. package/dist/cjs/array.d.ts +11 -0
  10. package/dist/cjs/async.d.ts +44 -0
  11. package/dist/cjs/canonical.d.ts +18 -0
  12. package/dist/cjs/cli.d.ts +15 -0
  13. package/dist/cjs/collections.d.ts +34 -0
  14. package/dist/cjs/core.d.ts +117 -0
  15. package/dist/cjs/errors.d.ts +17 -0
  16. package/dist/cjs/explain.d.ts +30 -0
  17. package/dist/cjs/global.d.ts +90 -0
  18. package/dist/cjs/group.d.ts +47 -0
  19. package/dist/cjs/index.d.ts +25 -0
  20. package/dist/cjs/internal/closest.d.ts +9 -0
  21. package/dist/cjs/internal/duration.d.ts +15 -0
  22. package/dist/cjs/internal/equal.d.ts +34 -0
  23. package/dist/cjs/internal/glob.d.ts +30 -0
  24. package/dist/cjs/internal/order.d.ts +24 -0
  25. package/dist/cjs/internal/path.d.ts +109 -0
  26. package/dist/cjs/internal/record.d.ts +18 -0
  27. package/dist/cjs/internal/values.d.ts +41 -0
  28. package/dist/cjs/limits.d.ts +64 -0
  29. package/dist/cjs/operators.d.ts +81 -0
  30. package/dist/cjs/package.json +3 -0
  31. package/dist/cjs/plan.d.ts +79 -0
  32. package/dist/cjs/search.d.ts +42 -0
  33. package/dist/cjs/targets/mongo.d.ts +55 -0
  34. package/dist/cjs/text/index.d.ts +12 -0
  35. package/dist/cjs/text/parse.d.ts +91 -0
  36. package/dist/cjs/text/suggest.d.ts +16 -0
  37. package/dist/cjs/text/write.d.ts +34 -0
  38. package/dist/cjs/types.d.ts +236 -0
  39. package/dist/cjs/vocabulary.d.ts +106 -0
  40. package/dist/cli.d.ts +15 -0
  41. package/dist/cli.js +2729 -0
  42. package/dist/cli.js.map +1 -0
  43. package/dist/collections.d.ts +34 -0
  44. package/dist/core.d.ts +117 -0
  45. package/dist/errors.d.ts +17 -0
  46. package/dist/explain.d.ts +30 -0
  47. package/dist/global.cjs +1953 -0
  48. package/dist/global.cjs.map +1 -0
  49. package/dist/global.d.ts +90 -0
  50. package/dist/global.js +1950 -0
  51. package/dist/global.js.map +1 -0
  52. package/dist/group.d.ts +47 -0
  53. package/dist/index.cjs +2529 -0
  54. package/dist/index.cjs.map +1 -0
  55. package/dist/index.d.ts +25 -0
  56. package/dist/index.js +2495 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/internal/closest.d.ts +9 -0
  59. package/dist/internal/duration.d.ts +15 -0
  60. package/dist/internal/equal.d.ts +34 -0
  61. package/dist/internal/glob.d.ts +30 -0
  62. package/dist/internal/order.d.ts +24 -0
  63. package/dist/internal/path.d.ts +109 -0
  64. package/dist/internal/record.d.ts +18 -0
  65. package/dist/internal/values.d.ts +41 -0
  66. package/dist/limits.d.ts +64 -0
  67. package/dist/mongo.cjs +357 -0
  68. package/dist/mongo.cjs.map +1 -0
  69. package/dist/mongo.js +354 -0
  70. package/dist/mongo.js.map +1 -0
  71. package/dist/operators.d.ts +81 -0
  72. package/dist/plan.d.ts +79 -0
  73. package/dist/search.d.ts +42 -0
  74. package/dist/targets/mongo.d.ts +55 -0
  75. package/dist/text/index.d.ts +12 -0
  76. package/dist/text/parse.d.ts +91 -0
  77. package/dist/text/suggest.d.ts +16 -0
  78. package/dist/text/write.d.ts +34 -0
  79. package/dist/text.cjs +674 -0
  80. package/dist/text.cjs.map +1 -0
  81. package/dist/text.js +667 -0
  82. package/dist/text.js.map +1 -0
  83. package/dist/types.d.ts +236 -0
  84. package/dist/vocabulary.d.ts +106 -0
  85. package/docs/course/01-first-query.md +217 -0
  86. package/docs/course/02-operators.md +285 -0
  87. package/docs/course/03-arrays-and-paths.md +239 -0
  88. package/docs/course/04-combining.md +221 -0
  89. package/docs/course/05-dates.md +214 -0
  90. package/docs/course/06-typed-queries.md +240 -0
  91. package/docs/course/07-requests.md +261 -0
  92. package/docs/course/08-grouping.md +210 -0
  93. package/docs/course/09-explaining.md +171 -0
  94. package/docs/course/10-vocabulary.md +276 -0
  95. package/docs/course/11-the-search-box.md +349 -0
  96. package/docs/course/12-untrusted.md +257 -0
  97. package/docs/course/13-saved-filters.md +199 -0
  98. package/docs/course/14-streams-and-cli.md +276 -0
  99. package/docs/course/15-pushdown.md +240 -0
  100. package/docs/course/16-extending.md +199 -0
  101. package/docs/course/index.md +185 -0
  102. package/docs/design/decisions.md +198 -0
  103. package/docs/design/performance.md +102 -0
  104. package/docs/guides/adopting.md +81 -0
  105. package/docs/guides/pushdown.md +147 -0
  106. package/docs/guides/typescript.md +115 -0
  107. package/docs/guides/untrusted-input.md +86 -0
  108. package/docs/index.md +102 -0
  109. package/docs/reference/api.md +266 -0
  110. package/docs/reference/cli.md +103 -0
  111. package/docs/reference/index.md +12 -0
  112. package/docs/reference/specification.md +549 -0
  113. package/docs/reference/text-syntax.md +152 -0
  114. package/docs/start/quick-start.md +84 -0
  115. package/package.json +136 -0
@@ -0,0 +1,34 @@
1
+ import { type CompileOptions } from "./core.js";
2
+ import type { QueryLike, Source } from "./types.js";
3
+ /** The first item that matches, or `undefined`. Stops at the first match. */
4
+ export declare function find<K, V, Extra extends object = {}>(source: ReadonlyMap<K, V>, query: QueryLike<V, NoInfer<Extra>>, options?: CompileOptions<Extra>): V | undefined;
5
+ export declare function find<T, Extra extends object = {}>(source: Source<T>, query: QueryLike<T, NoInfer<Extra>>, options?: CompileOptions<Extra>): T | undefined;
6
+ /** The position of the first item that matches, or -1. For a `Map`, the position in insertion order. */
7
+ export declare function findIndex<K, V, Extra extends object = {}>(source: ReadonlyMap<K, V>, query: QueryLike<V, NoInfer<Extra>>, options?: CompileOptions<Extra>): number;
8
+ export declare function findIndex<T, Extra extends object = {}>(source: Source<T>, query: QueryLike<T, NoInfer<Extra>>, options?: CompileOptions<Extra>): number;
9
+ /** Every item that matches, in order, as a new array. */
10
+ export declare function filter<K, V, Extra extends object = {}>(source: ReadonlyMap<K, V>, query: QueryLike<V, NoInfer<Extra>>, options?: CompileOptions<Extra>): V[];
11
+ export declare function filter<T, Extra extends object = {}>(source: Source<T>, query: QueryLike<T, NoInfer<Extra>>, options?: CompileOptions<Extra>): T[];
12
+ /** How many items match. Counts in place rather than building the list it would count. */
13
+ export declare function count<K, V, Extra extends object = {}>(source: ReadonlyMap<K, V>, query: QueryLike<V, NoInfer<Extra>>, options?: CompileOptions<Extra>): number;
14
+ export declare function count<T, Extra extends object = {}>(source: Source<T>, query: QueryLike<T, NoInfer<Extra>>, options?: CompileOptions<Extra>): number;
15
+ /** Whether any item matches. Stops at the first. */
16
+ export declare function some<K, V, Extra extends object = {}>(source: ReadonlyMap<K, V>, query: QueryLike<V, NoInfer<Extra>>, options?: CompileOptions<Extra>): boolean;
17
+ export declare function some<T, Extra extends object = {}>(source: Source<T>, query: QueryLike<T, NoInfer<Extra>>, options?: CompileOptions<Extra>): boolean;
18
+ /** Whether every item matches. True for an empty collection. Stops at the first that does not. */
19
+ export declare function every<K, V, Extra extends object = {}>(source: ReadonlyMap<K, V>, query: QueryLike<V, NoInfer<Extra>>, options?: CompileOptions<Extra>): boolean;
20
+ export declare function every<T, Extra extends object = {}>(source: Source<T>, query: QueryLike<T, NoInfer<Extra>>, options?: CompileOptions<Extra>): boolean;
21
+ /** The matches and the rest, in one pass. */
22
+ export declare function partition<K, V, Extra extends object = {}>(source: ReadonlyMap<K, V>, query: QueryLike<V, NoInfer<Extra>>, options?: CompileOptions<Extra>): [V[], V[]];
23
+ export declare function partition<T, Extra extends object = {}>(source: Source<T>, query: QueryLike<T, NoInfer<Extra>>, options?: CompileOptions<Extra>): [T[], T[]];
24
+ /** The entries of a `Map` whose value matches, as a new `Map` with the same keys. */
25
+ export declare function filterMap<K, V, Extra extends object = {}>(source: ReadonlyMap<K, V>, query: QueryLike<V, NoInfer<Extra>>, options?: CompileOptions<Extra>): Map<K, V>;
26
+ /**
27
+ * The properties of a plain object whose value matches, as a new object.
28
+ *
29
+ * Own enumerable properties only — the same ones `Object.entries` sees.
30
+ */
31
+ export declare function filterRecord<V, Extra extends object = {}>(source: Readonly<Record<string, V>>, query: QueryLike<V, NoInfer<Extra>>, options?: CompileOptions<Extra>): Record<string, V>;
32
+ /** The first value of a plain object that matches, with its key. */
33
+ export declare function findEntry<V, Extra extends object = {}>(source: Readonly<Record<string, V>>, query: QueryLike<V, NoInfer<Extra>>, options?: CompileOptions<Extra>): [string, V] | undefined;
34
+ export declare function findEntry<K, V, Extra extends object = {}>(source: ReadonlyMap<K, V>, query: QueryLike<V, NoInfer<Extra>>, options?: CompileOptions<Extra>): [K, V] | undefined;
package/dist/core.d.ts ADDED
@@ -0,0 +1,117 @@
1
+ import { JqlError } from "./errors.js";
2
+ import { type Reach } from "./internal/path.js";
3
+ import { type Limits } from "./limits.js";
4
+ import type { Matcher, QueryLike, UntypedQuery } from "./types.js";
5
+ import { type Vocabulary } from "./vocabulary.js";
6
+ /**
7
+ * The engine: a query document in, a predicate out.
8
+ *
9
+ * **Compile once, test many.** Everything that depends only on the query — validating it,
10
+ * resolving field names, splitting paths, building sets for `$in`, lower-casing for `$options:
11
+ * "i"`, compiling patterns — happens here, once. What comes back is a tree of small closures
12
+ * that does only the part that depends on the document. Filtering a million items with one
13
+ * query parses that query once, not a million times.
14
+ *
15
+ * **No code generation.** Emitting JavaScript source and calling `new Function` would be
16
+ * faster still, and it would also be refused by every page with a Content-Security-Policy
17
+ * worth having — including the dashboards in this project, whose build fails on a
18
+ * `new Function`. Closures run everywhere.
19
+ *
20
+ * **Cheapest first.** The parts of an `$and` are reordered so an equality test runs before a
21
+ * pattern and a pattern before a free-text walk, because the first part to fail decides the
22
+ * answer and the rest never run. Every operator is a pure test, so the order changes the
23
+ * cost and never the result.
24
+ *
25
+ * **No implicit cache.** A cache keyed on the query object would hand back a stale predicate
26
+ * the moment somebody edited the object they had already queried with — a filter that looks
27
+ * updated and is not. Compiling a small query costs about as much as testing a handful of
28
+ * documents, so the helpers compile per call, and a hot loop calls `compile` once and keeps
29
+ * the result.
30
+ */
31
+ export interface CompileOptions<Extra extends object = {}> {
32
+ /** The field names, aliases and computed fields a query may use. Its fields become names a typed query may use. */
33
+ readonly vocabulary?: Vocabulary<never, Extra> | undefined;
34
+ /** How much of a query to accept. Default `DEFAULT_LIMITS`; use `UNTRUSTED_LIMITS` for queries from outside. */
35
+ readonly limits?: Partial<Limits> | undefined;
36
+ /**
37
+ * Where "now" comes from, for the relative dates in `{ "$date": { "$ago": "1h" } }`.
38
+ * Default the system clock, read **once**, when the query compiles.
39
+ *
40
+ * Read once because a window that moved while a scan was in progress would judge two
41
+ * items a second apart against different hours. It also means a long-lived compiled
42
+ * query keeps the instant it was compiled at: recompile to move the window, which costs
43
+ * well under a microsecond.
44
+ */
45
+ readonly now?: (() => number) | undefined;
46
+ /**
47
+ * Operators this project adds to the language, such as an address-in-network test that
48
+ * no amount of JSON could express.
49
+ *
50
+ * **A query using one is no longer portable JQL**, which is why their names must begin
51
+ * `$x`: somebody reading a stored filter can see at a glance that it needs more than a
52
+ * standard engine, and a future version of the language cannot collide with one.
53
+ */
54
+ readonly operators?: readonly OperatorDefinition[] | undefined;
55
+ }
56
+ /** An operator a project adds to the language. Its name must match `$x` followed by a capital: `$xCidr`. */
57
+ export interface OperatorDefinition {
58
+ readonly name: string;
59
+ /**
60
+ * Builds the test, once, when the query compiles. Throw `JqlError` for an operand this
61
+ * operator cannot use; the message reaches whoever wrote the query.
62
+ */
63
+ readonly compile: (operand: unknown, at: string) => (value: unknown) => boolean;
64
+ /** Roughly what it costs against the others, so an `$and` can run the cheap parts first. Default 4. */
65
+ readonly cost?: number | undefined;
66
+ /** Whether it is also tried against each element of an array value. Default true, like the built-in operators. */
67
+ readonly elementwise?: boolean | undefined;
68
+ }
69
+ /** The operators that combine queries. */
70
+ export declare const QUERY_OPERATORS: readonly string[];
71
+ /** The operators that test a field. At the top of a query they test the item itself. */
72
+ export declare const FIELD_OPERATORS: readonly string[];
73
+ /**
74
+ * Operators whose meaning `$options: "i"` changes.
75
+ *
76
+ * Not in the barrel — this is the language's own list (specification §5.5), shared with
77
+ * `explain`, which has to know which pieces of a condition the flag travels with.
78
+ */
79
+ export declare const CASE_AWARE: Set<string>;
80
+ /**
81
+ * Compiles a query into a predicate.
82
+ *
83
+ * Throws `JqlError` for anything that is not a valid query, naming where in the query the
84
+ * problem is. A function passed in is returned as it is, so every helper that takes a
85
+ * query also takes a predicate somebody already has.
86
+ */
87
+ export declare function compile<T = unknown, Extra extends object = {}>(query: QueryLike<T, NoInfer<Extra>>, options?: CompileOptions<Extra>): Matcher<T>;
88
+ /** Whether one value matches a query. For a one-off; in a loop, `compile` once instead. */
89
+ export declare function matches<T, Extra extends object = {}>(value: T, query: QueryLike<T, NoInfer<Extra>>, options?: CompileOptions<Extra>): boolean;
90
+ /**
91
+ * Checks a query, and hands back the predicate when it is a good one.
92
+ *
93
+ * What a service calls on a query it has just received, so it can answer 400 with the
94
+ * sentence rather than 500 with a stack. The predicate comes with it because the caller
95
+ * needs one next, and the two ways of getting it separately are both worse: compiling
96
+ * again costs a second compile, and — the reason this returns it rather than only a
97
+ * verdict — it is compiled from a second set of options, which is how a query gets
98
+ * checked against the untrusted limits and then run without them.
99
+ */
100
+ export declare function validate<T = unknown>(query: unknown, options?: CompileOptions<object>): {
101
+ valid: true;
102
+ test: Matcher<T>;
103
+ } | {
104
+ valid: false;
105
+ error: JqlError;
106
+ };
107
+ /**
108
+ * Marks a query from outside the type system — parsed JSON, a URL parameter — as one to be
109
+ * checked when it is compiled rather than by the compiler. Returns the same object.
110
+ */
111
+ export declare function untyped(query: unknown): UntypedQuery;
112
+ /**
113
+ * How a field name is reached, by the same rules a query uses. For sorting and projection,
114
+ * which must name fields exactly as the query beside them does. Not part of the public
115
+ * surface.
116
+ */
117
+ export declare function reachField(name: string, vocabulary: Vocabulary<unknown, object> | undefined, at: string): Reach;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The one way a query is refused.
3
+ *
4
+ * A query that looks right and matches nothing is the failure this library exists to
5
+ * prevent: a misspelt operator, a field value that is `undefined` because a variable was,
6
+ * a regex that does not compile. Each of those used to be a filter that silently let
7
+ * everything through or nothing through, and nobody could tell which from the result. So
8
+ * `compile` refuses, with a sentence saying what was wrong and where in the query it was.
9
+ */
10
+ export declare class JqlError extends Error {
11
+ /**
12
+ * Where in the query the problem is, written the way you would reach it in code:
13
+ * `$or[1].age.$gt`. Empty for the query as a whole.
14
+ */
15
+ readonly at: string;
16
+ constructor(message: string, at?: string);
17
+ }
@@ -0,0 +1,30 @@
1
+ import { type CompileOptions } from "./core.js";
2
+ import type { QueryLike } from "./types.js";
3
+ /**
4
+ * Why one item does or does not match a query.
5
+ *
6
+ * A filter that returns nothing tells you nothing about which part of it was wrong. This
7
+ * takes the query apart and answers for each piece separately, with the values it actually
8
+ * read, so an operator looking at a row and a saved filter that disagree can see the clause
9
+ * that decided it.
10
+ *
11
+ * **It runs the real engine on every piece.** Each clause is compiled on its own and tested,
12
+ * rather than interpreted here by a second reading of the language — a second reading would
13
+ * be a second set of semantics, and the one thing worse than a filter you cannot explain is
14
+ * an explanation that does not match what the filter did. The cost is one small compile per
15
+ * clause, which is why this is for one item at a time and `compile` is for the collection.
16
+ */
17
+ export interface Explanation {
18
+ /** Whether this clause holds for the item. */
19
+ readonly matched: boolean;
20
+ /** Where the clause is in the query, written as it would be reached in code: `$or[1].age.$gt`. */
21
+ readonly at: string;
22
+ /** The clause itself, as the JSON fragment that was evaluated. */
23
+ readonly clause: unknown;
24
+ /** One sentence: what was read, or how the parts came out. */
25
+ readonly because: string;
26
+ /** The clauses this one is made of. */
27
+ readonly parts?: readonly Explanation[];
28
+ }
29
+ /** Explains one item against one query. */
30
+ export declare function explain<T, Extra extends object = {}>(query: QueryLike<T, NoInfer<Extra>>, item: T, options?: CompileOptions<Extra>): Explanation;