@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
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/errors.ts
|
|
4
|
+
var JqlError = class extends Error {
|
|
5
|
+
/**
|
|
6
|
+
* Where in the query the problem is, written the way you would reach it in code:
|
|
7
|
+
* `$or[1].age.$gt`. Empty for the query as a whole.
|
|
8
|
+
*/
|
|
9
|
+
at;
|
|
10
|
+
constructor(message, at = "") {
|
|
11
|
+
super(at === "" ? message : `at ${show(at)}: ${message}`);
|
|
12
|
+
this.name = "JqlError";
|
|
13
|
+
this.at = at;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
function show(at) {
|
|
17
|
+
return at.length > 80 ? `${at.slice(0, 80)}\u2026` : at;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/internal/duration.ts
|
|
21
|
+
var PART = /(\d+)(ms|s|m|h|d|w)/g;
|
|
22
|
+
var SHAPE = /^(?:\d+(?:ms|s|m|h|d|w))+$/;
|
|
23
|
+
var UNITS = {
|
|
24
|
+
ms: 1,
|
|
25
|
+
s: 1e3,
|
|
26
|
+
m: 6e4,
|
|
27
|
+
h: 36e5,
|
|
28
|
+
d: 864e5,
|
|
29
|
+
w: 6048e5
|
|
30
|
+
};
|
|
31
|
+
var DURATION_UNITS = Object.keys(UNITS);
|
|
32
|
+
function parseDuration(text) {
|
|
33
|
+
if (!SHAPE.test(text)) return void 0;
|
|
34
|
+
let total = 0;
|
|
35
|
+
PART.lastIndex = 0;
|
|
36
|
+
for (; ; ) {
|
|
37
|
+
const part = PART.exec(text);
|
|
38
|
+
if (part === null) break;
|
|
39
|
+
const count = Number(part[1]);
|
|
40
|
+
if (!Number.isSafeInteger(count)) return void 0;
|
|
41
|
+
total += count * UNITS[part[2]];
|
|
42
|
+
}
|
|
43
|
+
return Number.isSafeInteger(total) ? total : void 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/internal/values.ts
|
|
47
|
+
function isPlainObject(value) {
|
|
48
|
+
if (value === null || typeof value !== "object") return false;
|
|
49
|
+
const prototype = Object.getPrototypeOf(value);
|
|
50
|
+
return prototype === Object.prototype || prototype === null;
|
|
51
|
+
}
|
|
52
|
+
function isDateLiteral(value) {
|
|
53
|
+
if (!isPlainObject(value) || !("$date" in value)) return false;
|
|
54
|
+
for (const key in value) if (key !== "$date") return false;
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
function isFieldReference(value) {
|
|
58
|
+
if (!isPlainObject(value) || typeof value.$field !== "string") return false;
|
|
59
|
+
for (const key in value) if (key !== "$field") return false;
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
function resolveDate(value, now) {
|
|
63
|
+
if (typeof value === "number") return value;
|
|
64
|
+
if (typeof value === "string") return value === "now" ? now : instant(value);
|
|
65
|
+
if (isPlainObject(value)) {
|
|
66
|
+
const keys = Object.keys(value);
|
|
67
|
+
if (keys.length !== 1) return Number.NaN;
|
|
68
|
+
const amount = keys[0] === "$ago" || keys[0] === "$ahead" ? value[keys[0]] : void 0;
|
|
69
|
+
if (typeof amount !== "string") return Number.NaN;
|
|
70
|
+
const span = parseDuration(amount);
|
|
71
|
+
if (span === void 0) return Number.NaN;
|
|
72
|
+
return keys[0] === "$ago" ? now - span : now + span;
|
|
73
|
+
}
|
|
74
|
+
return Number.NaN;
|
|
75
|
+
}
|
|
76
|
+
var ISO_8601 = /^\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
77
|
+
var NO_OFFSET = /^(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?)$/;
|
|
78
|
+
function instant(text) {
|
|
79
|
+
const bare = NO_OFFSET.exec(text);
|
|
80
|
+
return bare === null ? Date.parse(text) : Date.parse(`${bare[1]}T${bare[2]}Z`);
|
|
81
|
+
}
|
|
82
|
+
function toTime(value) {
|
|
83
|
+
if (typeof value === "number") return value;
|
|
84
|
+
if (value instanceof Date) return value.getTime();
|
|
85
|
+
if (typeof value === "string") return ISO_8601.test(value) ? instant(value) : Number.NaN;
|
|
86
|
+
return Number.NaN;
|
|
87
|
+
}
|
|
88
|
+
var TYPE_NAMES = ["string", "number", "integer", "bigint", "boolean", "null", "array", "object", "date"];
|
|
89
|
+
function isOfType(value, type) {
|
|
90
|
+
switch (type) {
|
|
91
|
+
case "string":
|
|
92
|
+
return typeof value === "string";
|
|
93
|
+
case "number":
|
|
94
|
+
return typeof value === "number" || typeof value === "bigint";
|
|
95
|
+
case "integer":
|
|
96
|
+
return Number.isInteger(value) || typeof value === "bigint";
|
|
97
|
+
case "bigint":
|
|
98
|
+
return typeof value === "bigint";
|
|
99
|
+
case "boolean":
|
|
100
|
+
return typeof value === "boolean";
|
|
101
|
+
case "null":
|
|
102
|
+
return value === null;
|
|
103
|
+
case "array":
|
|
104
|
+
return Array.isArray(value);
|
|
105
|
+
case "date":
|
|
106
|
+
return value instanceof Date;
|
|
107
|
+
case "object":
|
|
108
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function describe(value) {
|
|
112
|
+
if (value === null) return "null";
|
|
113
|
+
if (typeof value === "function") return "a function";
|
|
114
|
+
if (Array.isArray(value)) return "an array";
|
|
115
|
+
if (value instanceof RegExp) return "a RegExp object";
|
|
116
|
+
if (value instanceof Date) return "a Date object";
|
|
117
|
+
if (value instanceof Map) return "a Map";
|
|
118
|
+
if (value instanceof Set) return "a Set";
|
|
119
|
+
if (typeof value === "object") {
|
|
120
|
+
if (isPlainObject(value)) return "an object";
|
|
121
|
+
const name = nameOf(value);
|
|
122
|
+
return name === void 0 ? "an object" : `an instance of ${name}`;
|
|
123
|
+
}
|
|
124
|
+
if (typeof value === "string") return `the string ${JSON.stringify(value.length > 40 ? `${value.slice(0, 40)}\u2026` : value)}`;
|
|
125
|
+
return `${typeof value} ${String(value)}`;
|
|
126
|
+
}
|
|
127
|
+
function nameOf(value) {
|
|
128
|
+
const prototype = Object.getPrototypeOf(value);
|
|
129
|
+
const name = prototype?.constructor?.name;
|
|
130
|
+
return typeof name === "string" && name !== "" && name !== "Object" ? name : void 0;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
exports.DURATION_UNITS = DURATION_UNITS;
|
|
134
|
+
exports.JqlError = JqlError;
|
|
135
|
+
exports.TYPE_NAMES = TYPE_NAMES;
|
|
136
|
+
exports.describe = describe;
|
|
137
|
+
exports.isDateLiteral = isDateLiteral;
|
|
138
|
+
exports.isFieldReference = isFieldReference;
|
|
139
|
+
exports.isOfType = isOfType;
|
|
140
|
+
exports.isPlainObject = isPlainObject;
|
|
141
|
+
exports.parseDuration = parseDuration;
|
|
142
|
+
exports.resolveDate = resolveDate;
|
|
143
|
+
exports.toTime = toTime;
|
|
144
|
+
//# sourceMappingURL=chunk-74QKHM6Y.cjs.map
|
|
145
|
+
//# sourceMappingURL=chunk-74QKHM6Y.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/internal/duration.ts","../src/internal/values.ts"],"names":[],"mappings":"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","file":"chunk-74QKHM6Y.cjs","sourcesContent":["/**\n * The one way a query is refused.\n *\n * A query that looks right and matches nothing is the failure this library exists to\n * prevent: a misspelt operator, a field value that is `undefined` because a variable was,\n * a regex that does not compile. Each of those used to be a filter that silently let\n * everything through or nothing through, and nobody could tell which from the result. So\n * `compile` refuses, with a sentence saying what was wrong and where in the query it was.\n */\nexport class JqlError extends Error {\n /**\n * Where in the query the problem is, written the way you would reach it in code:\n * `$or[1].age.$gt`. Empty for the query as a whole.\n */\n readonly at: string;\n\n constructor(message: string, at = \"\") {\n super(at === \"\" ? message : `at ${show(at)}: ${message}`);\n this.name = \"JqlError\";\n this.at = at;\n }\n}\n\n/**\n * The location, short enough to read.\n *\n * A location is built from the query's own field names, and a field name is a string from\n * outside — a long one makes the sentence it prefixes unreadable, and turns a log line into\n * a paragraph. `at` itself keeps the whole path, because that is the part a program reads.\n */\nfunction show(at: string): string {\n return at.length > 80 ? `${at.slice(0, 80)}…` : at;\n}\n","/**\n * Durations, for the relative dates a saved filter is written with.\n *\n * `\"1h\"`, `\"30m\"`, `\"1h30m\"`, `\"7d\"`. Written as one or more counts with a unit, largest\n * first by convention but in any order, because a filter is typed by a person and\n * `\"30m1h\"` means what it says.\n *\n * Months and years are deliberately absent: neither has a fixed length, so `\"1mo\"` would\n * mean something slightly different in February, and a filter whose window changes size\n * with the calendar is one nobody can reason about. Use days or weeks.\n */\n\nconst PART = /(\\d+)(ms|s|m|h|d|w)/g;\nconst SHAPE = /^(?:\\d+(?:ms|s|m|h|d|w))+$/;\n\nconst UNITS: Readonly<Record<string, number>> = {\n ms: 1,\n s: 1_000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n};\n\n/** The unit names, for an error that says what is accepted. */\nexport const DURATION_UNITS = Object.keys(UNITS);\n\n/** A duration in milliseconds, or `undefined` when the text is not one. */\nexport function parseDuration(text: string): number | undefined {\n if (!SHAPE.test(text)) return undefined;\n let total = 0;\n PART.lastIndex = 0;\n for (;;) {\n const part = PART.exec(text);\n if (part === null) break;\n const count = Number(part[1]);\n // A count that does not survive the round trip through a number is not a duration\n // anybody meant; it is a digit string long enough to have lost its tail.\n if (!Number.isSafeInteger(count)) return undefined;\n total += count * (UNITS[part[2] as string] as number);\n }\n return Number.isSafeInteger(total) ? total : undefined;\n}\n","import { parseDuration } from \"./duration.js\";\nimport type { DateLiteral, FieldReference, TypeName } from \"../types.js\";\n\n/** An object literal or a JSON-parsed object: what a query document is made of. */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\") return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n\n/** `{ $date: … }` and nothing else. */\nexport function isDateLiteral(value: unknown): value is DateLiteral {\n if (!isPlainObject(value) || !(\"$date\" in value)) return false;\n for (const key in value) if (key !== \"$date\") return false;\n return true;\n}\n\n/** `{ $field: \"path\" }` and nothing else: a comparison with another field of the same item. */\nexport function isFieldReference(value: unknown): value is FieldReference {\n if (!isPlainObject(value) || typeof value.$field !== \"string\") return false;\n for (const key in value) if (key !== \"$field\") return false;\n return true;\n}\n\n/**\n * The instant a `$date` literal stands for, in epoch milliseconds, or `NaN`.\n *\n * `now` is passed in rather than read here, because a query holding `{ \"$ago\": \"1h\" }` has\n * to resolve against one instant for the whole run: reading the clock per comparison would\n * let the window move while a scan is in progress, so two items a second apart could be\n * judged against different hours. It is also what makes a relative filter testable without\n * waiting.\n */\nexport function resolveDate(value: unknown, now: number): number {\n if (typeof value === \"number\") return value;\n if (typeof value === \"string\") return value === \"now\" ? now : instant(value);\n if (isPlainObject(value)) {\n const keys = Object.keys(value);\n if (keys.length !== 1) return Number.NaN;\n const amount = keys[0] === \"$ago\" || keys[0] === \"$ahead\" ? value[keys[0] as string] : undefined;\n if (typeof amount !== \"string\") return Number.NaN;\n const span = parseDuration(amount);\n if (span === undefined) return Number.NaN;\n return keys[0] === \"$ago\" ? now - span : now + span;\n }\n return Number.NaN;\n}\n\n/**\n * A document value read as a date, in epoch milliseconds, or `NaN`.\n *\n * Only called where the query supplied a `$date`, so a string is only ever parsed as a\n * date because somebody asked for that. `NaN` compares false with everything, which is\n * what a value that is not a date should do.\n */\n/**\n * The forms of a date a document may hold, per the specification: a `Date`, epoch\n * milliseconds, or an **ISO 8601** string.\n *\n * `Date.parse` accepts a great deal more than that — `\"12/31/2020\"`, `\"Mar 5 2021\"`, and\n * `\"5\"`, which it reads as a year — and what it accepts beyond ISO 8601 is left to the\n * implementation by the language it comes from. A document that matched here and nowhere\n * else would make the same query mean different things in different engines, which is the\n * one thing this language promises it does not do. The space instead of `T` is allowed\n * because RFC 3339 allows it and people write it.\n */\nconst ISO_8601 = /^\\d{4}-\\d{2}-\\d{2}(?:[T ]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:?\\d{2})?)?$/;\n\n/** A timestamp with a time and no offset, which is the one shape whose meaning is not fixed. */\nconst NO_OFFSET = /^(\\d{4}-\\d{2}-\\d{2})[T ](\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?)$/;\n\n/**\n * An instant from a string, read the same way on every machine.\n *\n * JavaScript reads a date-only string as midnight **UTC** and a timestamp with no offset as\n * **local time** — so `\"2026-01-01T12:00:00\"` was a different instant in Tokyo and in London,\n * and a query and a document that both held one answered differently depending on where the\n * process happened to be running. That is the failure the language already rules out for\n * `\"12/31/2020\"`: the same query meaning two things. A missing offset is UTC here, which is\n * the rule the date-only form already follows.\n */\nexport function instant(text: string): number {\n const bare = NO_OFFSET.exec(text);\n return bare === null ? Date.parse(text) : Date.parse(`${bare[1] as string}T${bare[2] as string}Z`);\n}\n\nexport function toTime(value: unknown): number {\n if (typeof value === \"number\") return value;\n if (value instanceof Date) return value.getTime();\n if (typeof value === \"string\") return ISO_8601.test(value) ? instant(value) : Number.NaN;\n return Number.NaN;\n}\n\n/** The type names `$type` knows, in one list, so the error for a wrong one can offer the right one. */\nexport const TYPE_NAMES: readonly TypeName[] = [\"string\", \"number\", \"integer\", \"bigint\", \"boolean\", \"null\", \"array\", \"object\", \"date\"];\n\n/** Whether a document value is of the named type. A missing value is of no type. */\nexport function isOfType(value: unknown, type: TypeName): boolean {\n switch (type) {\n case \"string\":\n return typeof value === \"string\";\n case \"number\":\n // A big integer is a number for ordering, so it is one here too: `$gt` and `$type`\n // disagreeing about the same value is a distinction nobody could explain.\n return typeof value === \"number\" || typeof value === \"bigint\";\n case \"integer\":\n return Number.isInteger(value) || typeof value === \"bigint\";\n case \"bigint\":\n return typeof value === \"bigint\";\n case \"boolean\":\n return typeof value === \"boolean\";\n case \"null\":\n return value === null;\n case \"array\":\n return Array.isArray(value);\n case \"date\":\n return value instanceof Date;\n case \"object\":\n return value !== null && typeof value === \"object\" && !Array.isArray(value) && !(value instanceof Date);\n }\n}\n\n/**\n * A readable name for a value in an error sentence.\n *\n * Specific about what it was given, because \"a query is an object, not an object\" — which\n * is what a `Map`, a `Set` and a class instance all used to produce — tells the reader\n * nothing at all about the thing they passed.\n */\nexport function describe(value: unknown): string {\n if (value === null) return \"null\";\n // Named rather than written out: `String(fn)` is the function's entire source, and a\n // closure of any size turned the sentence refusing it into a listing.\n if (typeof value === \"function\") return \"a function\";\n if (Array.isArray(value)) return \"an array\";\n if (value instanceof RegExp) return \"a RegExp object\";\n if (value instanceof Date) return \"a Date object\";\n if (value instanceof Map) return \"a Map\";\n if (value instanceof Set) return \"a Set\";\n if (typeof value === \"object\") {\n if (isPlainObject(value)) return \"an object\";\n const name = nameOf(value);\n return name === undefined ? \"an object\" : `an instance of ${name}`;\n }\n if (typeof value === \"string\") return `the string ${JSON.stringify(value.length > 40 ? `${value.slice(0, 40)}…` : value)}`;\n return `${typeof value} ${String(value)}`;\n}\n\n/** The class an object came from, when it can be read without running anything of the caller's. */\nfunction nameOf(value: object): string | undefined {\n const prototype = Object.getPrototypeOf(value) as { constructor?: { name?: unknown } } | null;\n // Read from the prototype rather than through the object, so a `constructor` field of\n // the caller's own — which a document or a query read from JSON can have — is not what\n // names it.\n const name = prototype?.constructor?.name;\n return typeof name === \"string\" && name !== \"\" && name !== \"Object\" ? name : undefined;\n}\n"]}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// src/internal/glob.ts
|
|
2
|
+
var ANY = { any: true };
|
|
3
|
+
var ONE = { one: true };
|
|
4
|
+
function compileGlob(pattern) {
|
|
5
|
+
const tokens = tokenize(pattern);
|
|
6
|
+
const stars = tokens.includes(ANY);
|
|
7
|
+
const questions = tokens.includes(ONE);
|
|
8
|
+
if (!stars && !questions) {
|
|
9
|
+
const literal = tokens.join("");
|
|
10
|
+
return (text) => text === literal;
|
|
11
|
+
}
|
|
12
|
+
if (!questions) {
|
|
13
|
+
const segments = [""];
|
|
14
|
+
for (const token of tokens) {
|
|
15
|
+
if (token === ANY) segments.push("");
|
|
16
|
+
else segments[segments.length - 1] += token;
|
|
17
|
+
}
|
|
18
|
+
const open = segments[0] === "";
|
|
19
|
+
const close = segments[segments.length - 1] === "";
|
|
20
|
+
const parts = segments.filter((segment) => segment !== "");
|
|
21
|
+
if (parts.length === 0) return () => true;
|
|
22
|
+
if (open && close && parts.length === 1) {
|
|
23
|
+
const inside = parts[0];
|
|
24
|
+
return (text) => text.includes(inside);
|
|
25
|
+
}
|
|
26
|
+
if (!open && close && parts.length === 1) {
|
|
27
|
+
const prefix = parts[0];
|
|
28
|
+
return (text) => text.startsWith(prefix);
|
|
29
|
+
}
|
|
30
|
+
if (open && !close && parts.length === 1) {
|
|
31
|
+
const suffix = parts[0];
|
|
32
|
+
return (text) => text.endsWith(suffix);
|
|
33
|
+
}
|
|
34
|
+
return (text) => scan(text, parts, open, close);
|
|
35
|
+
}
|
|
36
|
+
return (text) => walk([...text], tokens);
|
|
37
|
+
}
|
|
38
|
+
function compileGlobPattern(pattern, quoteLiteral) {
|
|
39
|
+
return tokenize(pattern).map((token) => token === ANY ? "[\\s\\S]*" : token === ONE ? "[\\s\\S]" : quoteLiteral(token)).join("");
|
|
40
|
+
}
|
|
41
|
+
function tokenize(pattern) {
|
|
42
|
+
const tokens = [];
|
|
43
|
+
let escaped = false;
|
|
44
|
+
for (const character of pattern) {
|
|
45
|
+
if (escaped) {
|
|
46
|
+
tokens.push(character);
|
|
47
|
+
escaped = false;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (character === "\\") {
|
|
51
|
+
escaped = true;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (character === "*") {
|
|
55
|
+
if (tokens[tokens.length - 1] !== ANY) tokens.push(ANY);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
tokens.push(character === "?" ? ONE : character);
|
|
59
|
+
}
|
|
60
|
+
if (escaped) tokens.push("\\");
|
|
61
|
+
return tokens;
|
|
62
|
+
}
|
|
63
|
+
function scan(text, parts, open, close) {
|
|
64
|
+
let at = 0;
|
|
65
|
+
const last = parts.length - 1;
|
|
66
|
+
for (let i = 0; i <= last; i++) {
|
|
67
|
+
const part = parts[i];
|
|
68
|
+
if (i === 0 && !open) {
|
|
69
|
+
if (!text.startsWith(part)) return false;
|
|
70
|
+
at = part.length;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (i === last && !close) {
|
|
74
|
+
return text.length - part.length >= at && text.endsWith(part);
|
|
75
|
+
}
|
|
76
|
+
const found = text.indexOf(part, at);
|
|
77
|
+
if (found === -1) return false;
|
|
78
|
+
at = found + part.length;
|
|
79
|
+
}
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
function walk(characters, tokens) {
|
|
83
|
+
let at = 0;
|
|
84
|
+
let token = 0;
|
|
85
|
+
let star = -1;
|
|
86
|
+
let mark = 0;
|
|
87
|
+
while (at < characters.length) {
|
|
88
|
+
const current = tokens[token];
|
|
89
|
+
if (current !== void 0 && (current === ONE || current === characters[at])) {
|
|
90
|
+
at++;
|
|
91
|
+
token++;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (current === ANY) {
|
|
95
|
+
star = token++;
|
|
96
|
+
mark = at;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (star !== -1) {
|
|
100
|
+
token = star + 1;
|
|
101
|
+
at = ++mark;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
while (tokens[token] === ANY) token++;
|
|
107
|
+
return token === tokens.length;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export { compileGlob, compileGlobPattern };
|
|
111
|
+
//# sourceMappingURL=chunk-ATRXHPBJ.js.map
|
|
112
|
+
//# sourceMappingURL=chunk-ATRXHPBJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal/glob.ts"],"names":[],"mappings":"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","file":"chunk-ATRXHPBJ.js","sourcesContent":["/**\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"]}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunk74QKHM6Y_cjs = require('./chunk-74QKHM6Y.cjs');
|
|
4
|
+
|
|
5
|
+
// src/vocabulary.ts
|
|
6
|
+
var BAD_NAME = /^\$|\./;
|
|
7
|
+
function defineVocabulary() {
|
|
8
|
+
return (definition) => build(definition);
|
|
9
|
+
}
|
|
10
|
+
function checkVocabulary(value, at = "vocabulary") {
|
|
11
|
+
if (value === void 0 || value !== null && typeof value === "object" && value.lookup instanceof Map) return value;
|
|
12
|
+
throw new chunk74QKHM6Y_cjs.JqlError(
|
|
13
|
+
`is not a vocabulary, but ${chunk74QKHM6Y_cjs.describe(value)}: build one with defineVocabulary()({ fields: \u2026 }) \u2014 the empty parentheses first \u2014 and pass what that returns`,
|
|
14
|
+
at
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
function build(definition) {
|
|
18
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
19
|
+
const fields = [];
|
|
20
|
+
const claim = (name, field) => {
|
|
21
|
+
const key = name.toLowerCase();
|
|
22
|
+
if (name === "" || BAD_NAME.test(name)) throw new chunk74QKHM6Y_cjs.JqlError(`"${name}" cannot name a field: a field name is not empty, does not start with "$" and has no "."`, "vocabulary");
|
|
23
|
+
const holder = lookup.get(key);
|
|
24
|
+
if (holder !== void 0) {
|
|
25
|
+
throw new chunk74QKHM6Y_cjs.JqlError(`"${name}" names both "${holder.name}" and "${field.name}", so a query using it could mean either`, "vocabulary");
|
|
26
|
+
}
|
|
27
|
+
lookup.set(key, field);
|
|
28
|
+
};
|
|
29
|
+
for (const [name, spec] of Object.entries(definition.fields)) {
|
|
30
|
+
if (spec.path !== void 0 && spec.get !== void 0) {
|
|
31
|
+
throw new chunk74QKHM6Y_cjs.JqlError(`"${name}" has both a path and a getter; one of them would be ignored, so say which`, `vocabulary.${name}`);
|
|
32
|
+
}
|
|
33
|
+
if (spec.path === "") throw new chunk74QKHM6Y_cjs.JqlError(`"${name}" has an empty path, which reaches nothing`, `vocabulary.${name}`);
|
|
34
|
+
const field = {
|
|
35
|
+
name,
|
|
36
|
+
kind: spec.kind ?? "text",
|
|
37
|
+
path: spec.path ?? name,
|
|
38
|
+
get: spec.get,
|
|
39
|
+
aliases: spec.aliases ?? [],
|
|
40
|
+
values: spec.values
|
|
41
|
+
};
|
|
42
|
+
fields.push(field);
|
|
43
|
+
claim(name, field);
|
|
44
|
+
for (const alias of field.aliases) claim(alias, field);
|
|
45
|
+
}
|
|
46
|
+
if (definition.text !== void 0) {
|
|
47
|
+
for (const name of definition.text) {
|
|
48
|
+
if (!lookup.has(name.toLowerCase())) throw new chunk74QKHM6Y_cjs.JqlError(`the text field "${name}" is not a field of this vocabulary, so a bare word would never search it`, "vocabulary.text");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
lookup,
|
|
53
|
+
fields,
|
|
54
|
+
names: [...lookup.keys()].sort(),
|
|
55
|
+
text: definition.text,
|
|
56
|
+
strict: definition.strict ?? false
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
exports.checkVocabulary = checkVocabulary;
|
|
61
|
+
exports.defineVocabulary = defineVocabulary;
|
|
62
|
+
//# sourceMappingURL=chunk-EJFHVGBC.cjs.map
|
|
63
|
+
//# sourceMappingURL=chunk-EJFHVGBC.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/vocabulary.ts"],"names":["JqlError","describe"],"mappings":"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","file":"chunk-EJFHVGBC.cjs","sourcesContent":["import { JqlError } from \"./errors.js\";\nimport { describe } from \"./internal/values.js\";\nimport type { PathValue } from \"./types.js\";\n\n/**\n * The names a query may use for one kind of document.\n *\n * Without one, a field name is a path and nothing more. With one, a collection gets the\n * names people actually type — `ip` for `actor`, `ua` for `request.headers.user-agent` —\n * and fields that are computed rather than stored, like BotHandler's `outcome`, which is\n * derived from the action and never kept anywhere.\n *\n * The same vocabulary serves the JSON engine and the text front end. That is deliberate:\n * two places that each kept their own list of field names would be two dialects within a\n * week, and a filter typed into a search box has to mean exactly what the JSON it becomes\n * means.\n */\n\n/**\n * How the text front end reads a value typed after `field:`. The JSON engine ignores it:\n * a JSON query says what it means with its operator.\n *\n * - `text` — contains the value, ignoring case. The default, and right for prose.\n * - `word` — contains it as a whole component (`1.2.3.4` does not find `1.2.3.45`). For identifiers.\n * - `exact` — equals it, ignoring case. For closed sets like a verdict or a method.\n * - `number` — compares: `>70`, `<=5`, `10..20`, `42`.\n * - `date` — compares instants the same way: `>2026-01-01`.\n * - `boolean` — `true`/`false`, `yes`/`no`, `1`/`0`.\n */\nexport type FieldKind = \"text\" | \"word\" | \"exact\" | \"number\" | \"date\" | \"boolean\";\n\nexport interface FieldDefinition<T = unknown> {\n /** Where the value lives. Default: the field's own name. */\n readonly path?: string | undefined;\n /** Computes the value instead. Called once per document the field is tested against; it must not throw. */\n readonly get?: ((document: T) => unknown) | undefined;\n /** Other names that mean this field. */\n readonly aliases?: readonly string[] | undefined;\n /** Default `\"text\"`. */\n readonly kind?: FieldKind | undefined;\n /** The values worth offering as completions, when the set is closed. Leave out for a field that takes anything. */\n readonly values?: readonly string[] | undefined;\n}\n\nexport interface VocabularyDefinition<T = unknown> {\n readonly fields: Readonly<Record<string, FieldDefinition<T>>>;\n /** The fields a bare search word looks in. Default: the whole document. */\n readonly text?: readonly string[] | undefined;\n /**\n * Whether a name outside the vocabulary is refused. Default `false`, so any path still\n * works alongside the named fields. Turn it on where the vocabulary is the whole\n * contract — a public API whose documents may grow fields nobody promised to keep.\n */\n readonly strict?: boolean | undefined;\n}\n\n/** One field, with every name that reaches it already resolved. */\nexport interface VocabularyField<T = unknown> {\n readonly name: string;\n readonly kind: FieldKind;\n readonly path: string;\n readonly get: ((document: T) => unknown) | undefined;\n readonly aliases: readonly string[];\n readonly values: readonly string[] | undefined;\n}\n\n/**\n * A vocabulary ready to use.\n *\n * `Extra` is the type of the fields it names, so a typed query can use them: build one\n * with `defineVocabulary<Doc>()({ … })` and the computed names type-check.\n */\nexport interface Vocabulary<T = unknown, Extra extends object = {}> {\n /** Every name, aliases included, lower-cased, to the field it means. */\n readonly lookup: ReadonlyMap<string, VocabularyField<T>>;\n /** The canonical fields, in the order they were defined. */\n readonly fields: readonly VocabularyField<T>[];\n /** Every name a query may use, aliases included, sorted. For completions. */\n readonly names: readonly string[];\n readonly text: readonly string[] | undefined;\n readonly strict: boolean;\n /** Carries the computed field types. Never present at run time. */\n readonly __extra?: Extra | undefined;\n}\n\ntype ExtraOf<T, F> = {\n [K in keyof F]: F[K] extends { get: (document: T) => infer R } ? R : F[K] extends { path: infer P extends string } ? PathValue<T, P> : K extends string ? PathValue<T, K> : unknown;\n};\n\n/** Names a query may never use for a field: they begin like an operator, or contain a path separator. */\nconst BAD_NAME = /^\\$|\\./;\n\n/**\n * Builds a vocabulary, refusing one that would quietly misbehave.\n *\n * Two names that collide, a text field that is not a field, a field with both a path and a\n * getter: each is a vocabulary that looks defined and means something other than what it\n * says, so each is an error here rather than a surprise at query time.\n *\n * Curried so the document type can be given while the field names are inferred:\n * `defineVocabulary<Request>()({ fields: { … } })`.\n */\nexport function defineVocabulary<T = unknown>(): <const D extends VocabularyDefinition<T>>(definition: D) => Vocabulary<T, ExtraOf<T, D[\"fields\"]>> {\n return (definition) => build(definition) as Vocabulary<T, ExtraOf<T, (typeof definition)[\"fields\"]>>;\n}\n\n/**\n * Refuses anything that is not a vocabulary, where one was expected.\n *\n * Two mistakes land here, and both used to arrive as `Cannot read properties of undefined\n * (reading 'get')` from somewhere inside the engine. `defineVocabulary` is curried, so\n * `defineVocabulary({ fields: … })` — the empty parentheses forgotten — returns a *function*;\n * and a definition passed straight through is an object with no `lookup` on it. Neither is\n * caught by the types from JavaScript, and a `TypeError` is not a `JqlError`, so `validate`\n * re-threw it: the one call whose job is to answer 400 rather than 500 answered 500.\n */\nexport function checkVocabulary<T>(value: T, at = \"vocabulary\"): T {\n if (value === undefined || (value !== null && typeof value === \"object\" && (value as { lookup?: unknown }).lookup instanceof Map)) return value;\n throw new JqlError(\n `is not a vocabulary, but ${describe(value)}: build one with defineVocabulary()({ fields: … }) — the empty parentheses first — and pass what that returns`,\n at,\n );\n}\n\nfunction build<T>(definition: VocabularyDefinition<T>): Vocabulary<T> {\n const lookup = new Map<string, VocabularyField<T>>();\n const fields: VocabularyField<T>[] = [];\n const claim = (name: string, field: VocabularyField<T>): void => {\n const key = name.toLowerCase();\n if (name === \"\" || BAD_NAME.test(name)) throw new JqlError(`\"${name}\" cannot name a field: a field name is not empty, does not start with \"$\" and has no \".\"`, \"vocabulary\");\n const holder = lookup.get(key);\n if (holder !== undefined) {\n throw new JqlError(`\"${name}\" names both \"${holder.name}\" and \"${field.name}\", so a query using it could mean either`, \"vocabulary\");\n }\n lookup.set(key, field);\n };\n\n for (const [name, spec] of Object.entries(definition.fields)) {\n if (spec.path !== undefined && spec.get !== undefined) {\n throw new JqlError(`\"${name}\" has both a path and a getter; one of them would be ignored, so say which`, `vocabulary.${name}`);\n }\n if (spec.path === \"\") throw new JqlError(`\"${name}\" has an empty path, which reaches nothing`, `vocabulary.${name}`);\n const field: VocabularyField<T> = {\n name,\n kind: spec.kind ?? \"text\",\n path: spec.path ?? name,\n get: spec.get,\n aliases: spec.aliases ?? [],\n values: spec.values,\n };\n fields.push(field);\n claim(name, field);\n for (const alias of field.aliases) claim(alias, field);\n }\n\n if (definition.text !== undefined) {\n for (const name of definition.text) {\n if (!lookup.has(name.toLowerCase())) throw new JqlError(`the text field \"${name}\" is not a field of this vocabulary, so a bare word would never search it`, \"vocabulary.text\");\n }\n }\n\n return {\n lookup,\n fields,\n names: [...lookup.keys()].sort(),\n text: definition.text,\n strict: definition.strict ?? false,\n };\n}\n"]}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/internal/glob.ts
|
|
4
|
+
var ANY = { any: true };
|
|
5
|
+
var ONE = { one: true };
|
|
6
|
+
function compileGlob(pattern) {
|
|
7
|
+
const tokens = tokenize(pattern);
|
|
8
|
+
const stars = tokens.includes(ANY);
|
|
9
|
+
const questions = tokens.includes(ONE);
|
|
10
|
+
if (!stars && !questions) {
|
|
11
|
+
const literal = tokens.join("");
|
|
12
|
+
return (text) => text === literal;
|
|
13
|
+
}
|
|
14
|
+
if (!questions) {
|
|
15
|
+
const segments = [""];
|
|
16
|
+
for (const token of tokens) {
|
|
17
|
+
if (token === ANY) segments.push("");
|
|
18
|
+
else segments[segments.length - 1] += token;
|
|
19
|
+
}
|
|
20
|
+
const open = segments[0] === "";
|
|
21
|
+
const close = segments[segments.length - 1] === "";
|
|
22
|
+
const parts = segments.filter((segment) => segment !== "");
|
|
23
|
+
if (parts.length === 0) return () => true;
|
|
24
|
+
if (open && close && parts.length === 1) {
|
|
25
|
+
const inside = parts[0];
|
|
26
|
+
return (text) => text.includes(inside);
|
|
27
|
+
}
|
|
28
|
+
if (!open && close && parts.length === 1) {
|
|
29
|
+
const prefix = parts[0];
|
|
30
|
+
return (text) => text.startsWith(prefix);
|
|
31
|
+
}
|
|
32
|
+
if (open && !close && parts.length === 1) {
|
|
33
|
+
const suffix = parts[0];
|
|
34
|
+
return (text) => text.endsWith(suffix);
|
|
35
|
+
}
|
|
36
|
+
return (text) => scan(text, parts, open, close);
|
|
37
|
+
}
|
|
38
|
+
return (text) => walk([...text], tokens);
|
|
39
|
+
}
|
|
40
|
+
function compileGlobPattern(pattern, quoteLiteral) {
|
|
41
|
+
return tokenize(pattern).map((token) => token === ANY ? "[\\s\\S]*" : token === ONE ? "[\\s\\S]" : quoteLiteral(token)).join("");
|
|
42
|
+
}
|
|
43
|
+
function tokenize(pattern) {
|
|
44
|
+
const tokens = [];
|
|
45
|
+
let escaped = false;
|
|
46
|
+
for (const character of pattern) {
|
|
47
|
+
if (escaped) {
|
|
48
|
+
tokens.push(character);
|
|
49
|
+
escaped = false;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (character === "\\") {
|
|
53
|
+
escaped = true;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (character === "*") {
|
|
57
|
+
if (tokens[tokens.length - 1] !== ANY) tokens.push(ANY);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
tokens.push(character === "?" ? ONE : character);
|
|
61
|
+
}
|
|
62
|
+
if (escaped) tokens.push("\\");
|
|
63
|
+
return tokens;
|
|
64
|
+
}
|
|
65
|
+
function scan(text, parts, open, close) {
|
|
66
|
+
let at = 0;
|
|
67
|
+
const last = parts.length - 1;
|
|
68
|
+
for (let i = 0; i <= last; i++) {
|
|
69
|
+
const part = parts[i];
|
|
70
|
+
if (i === 0 && !open) {
|
|
71
|
+
if (!text.startsWith(part)) return false;
|
|
72
|
+
at = part.length;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (i === last && !close) {
|
|
76
|
+
return text.length - part.length >= at && text.endsWith(part);
|
|
77
|
+
}
|
|
78
|
+
const found = text.indexOf(part, at);
|
|
79
|
+
if (found === -1) return false;
|
|
80
|
+
at = found + part.length;
|
|
81
|
+
}
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
function walk(characters, tokens) {
|
|
85
|
+
let at = 0;
|
|
86
|
+
let token = 0;
|
|
87
|
+
let star = -1;
|
|
88
|
+
let mark = 0;
|
|
89
|
+
while (at < characters.length) {
|
|
90
|
+
const current = tokens[token];
|
|
91
|
+
if (current !== void 0 && (current === ONE || current === characters[at])) {
|
|
92
|
+
at++;
|
|
93
|
+
token++;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (current === ANY) {
|
|
97
|
+
star = token++;
|
|
98
|
+
mark = at;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (star !== -1) {
|
|
102
|
+
token = star + 1;
|
|
103
|
+
at = ++mark;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
while (tokens[token] === ANY) token++;
|
|
109
|
+
return token === tokens.length;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
exports.compileGlob = compileGlob;
|
|
113
|
+
exports.compileGlobPattern = compileGlobPattern;
|
|
114
|
+
//# sourceMappingURL=chunk-ITPY6VTV.cjs.map
|
|
115
|
+
//# sourceMappingURL=chunk-ITPY6VTV.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal/glob.ts"],"names":[],"mappings":"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","file":"chunk-ITPY6VTV.cjs","sourcesContent":["/**\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"]}
|