wizard-ql 1.0.0

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.
@@ -0,0 +1,51 @@
1
+ import { type ConvertTypeRecord, type Expression, type Token, type TypeRecord } from './spec';
2
+ export declare const TOKEN_REGEX: RegExp;
3
+ export declare const QUOTE_REGEX: RegExp;
4
+ export declare const QUOTE_EDGE_REGEX: RegExp;
5
+ /**
6
+ * Take a string and tokenize it for parsing
7
+ * @param expression The expression to tokenize
8
+ * @returns An array of tokens
9
+ */
10
+ export declare function tokenize(expression: string): Token[];
11
+ export interface ExpressionConstraints<T extends TypeRecord, V extends boolean> {
12
+ /**
13
+ * Restricted fields.\
14
+ * Restrict an entire field by setting it to true.\
15
+ * By default allow any value and restrict a collection of values by passing ['deny', VALUES[]]\
16
+ * By default restrict any value and allow a collection of values by passing ['allow', VALUES[]]\
17
+ * If the field query is of array type, it will check all entries of the array.
18
+ * @warn If 42 (the number) is prohibited, "42" (the string) will still be allowed
19
+ */
20
+ restricted?: Partial<Record<keyof T | (string & {}), boolean | ['allow' | 'deny', Array<boolean | string | number | RegExp>]>>;
21
+ /**
22
+ * The types of fields\
23
+ * Either provide the field type singularly or permit multiple types with an array of field types
24
+ */
25
+ types?: T;
26
+ /**
27
+ * Field names in restriction checks and type checks are case insensitive
28
+ * @note If enabled, all fields will be returned as their casing denoted by the types or restricted record
29
+ * @warn Mismatching casing between the restricted record and the type record will prioritize the restricted record
30
+ */
31
+ caseInsensitive?: boolean;
32
+ /**
33
+ * Disallow fields that are not present in the "restricted" record or the "types" record
34
+ */
35
+ disallowUnvalidated?: V;
36
+ /**
37
+ * Interpret date string values as numbers\
38
+ * If true, use `new Date()` constructor to make this determination\
39
+ * If a callback is passed, use that. A value of `NaN` denotes an invalid date
40
+ */
41
+ interpretDates?: boolean | ((v: string) => number);
42
+ }
43
+ /**
44
+ * Parse a Wizard expression into its object form
45
+ * @template T A type record, mapping field names to their types
46
+ * @param expression The Wizard expression as a string or as an array of tokens
47
+ * @param constraints Constraints to add on parsing such as forced types or restricted columns
48
+ * @returns The object representation
49
+ * @throws {ParseError | ConstraintError}
50
+ */
51
+ export declare function parse<const T extends TypeRecord, const V extends boolean>(expression: string | string[] | Token[], constraints?: ExpressionConstraints<T, V>): Expression<ConvertTypeRecord<T>, V> | null;
@@ -0,0 +1,11 @@
1
+ export declare const ESCAPE_REGEX = "(?<=(?<!\\\\)(?:\\\\\\\\)*)";
2
+ /**
3
+ * Create a Regex to look for quotes
4
+ * @returns The regular expression
5
+ */
6
+ export declare function createQuoteRegexString(): string;
7
+ /**
8
+ * Create a Regex to find tokens
9
+ * @returns The regular expression
10
+ */
11
+ export declare function createTokenRegexString(): string;
package/dist/spec.d.ts ADDED
@@ -0,0 +1,169 @@
1
+ /** All available operation aliases (alias -> operation) */
2
+ export declare const OPERATION_ALIAS_DICTIONARY: {
3
+ readonly AND: "AND";
4
+ readonly '&&': "AND";
5
+ readonly '&': "AND";
6
+ readonly '^': "AND";
7
+ readonly OR: "OR";
8
+ readonly '||': "OR";
9
+ readonly '|': "OR";
10
+ readonly V: "OR";
11
+ readonly GEQ: "GEQ";
12
+ readonly '>=': "GEQ";
13
+ readonly '=>': "GEQ";
14
+ readonly LEQ: "LEQ";
15
+ readonly '<=': "LEQ";
16
+ readonly '=<': "LEQ";
17
+ readonly NOTEQUALS: "NOTEQUAL";
18
+ readonly NOTEQUAL: "NOTEQUAL";
19
+ readonly NEQ: "NOTEQUAL";
20
+ readonly ISNT: "NOTEQUAL";
21
+ readonly '!==': "NOTEQUAL";
22
+ readonly '!=': "NOTEQUAL";
23
+ readonly EQUALS: "EQUAL";
24
+ readonly EQUAL: "EQUAL";
25
+ readonly EQ: "EQUAL";
26
+ readonly IS: "EQUAL";
27
+ readonly '==': "EQUAL";
28
+ readonly '=': "EQUAL";
29
+ readonly LESS: "LESS";
30
+ readonly '<': "LESS";
31
+ readonly GREATER: "GREATER";
32
+ readonly '>': "GREATER";
33
+ readonly MORE: "GREATER";
34
+ readonly NOTIN: "NOTIN";
35
+ readonly '!:': "NOTIN";
36
+ readonly IN: "IN";
37
+ readonly ':': "IN";
38
+ readonly NOTMATCHES: "NOTMATCH";
39
+ readonly NOTMATCH: "NOTMATCH";
40
+ readonly '!~': "NOTMATCH";
41
+ readonly MATCHES: "MATCH";
42
+ readonly MATCH: "MATCH";
43
+ readonly '~': "MATCH";
44
+ };
45
+ export interface Token {
46
+ /** The text content of the token */
47
+ content: string;
48
+ /** The index in the original expression this token originates from */
49
+ index: number;
50
+ }
51
+ export type Operation = (typeof OPERATION_ALIAS_DICTIONARY)[keyof typeof OPERATION_ALIAS_DICTIONARY];
52
+ export declare const ALIASES: string[];
53
+ /** All base operations and their type */
54
+ export declare const OPERATION_PURPOSE_DICTIONARY: {
55
+ readonly AND: "junction";
56
+ readonly OR: "junction";
57
+ readonly EQUAL: "comparison";
58
+ readonly NOTEQUAL: "comparison";
59
+ readonly LESS: "comparison";
60
+ readonly GREATER: "comparison";
61
+ readonly GEQ: "comparison";
62
+ readonly LEQ: "comparison";
63
+ readonly IN: "comparison";
64
+ readonly NOTIN: "comparison";
65
+ readonly MATCH: "comparison";
66
+ readonly NOTMATCH: "comparison";
67
+ };
68
+ type KeysWhereValue<T, V> = Exclude<{
69
+ [K in keyof T]: T[K] extends V ? K : never;
70
+ }[keyof T], never>;
71
+ export type JunctionOperation = KeysWhereValue<typeof OPERATION_PURPOSE_DICTIONARY, 'junction'>;
72
+ export type ComparisonOperation = KeysWhereValue<typeof OPERATION_PURPOSE_DICTIONARY, 'comparison'>;
73
+ /** All comparison operations and their types */
74
+ export declare const COMPARISON_TYPE_DICTIONARY: {
75
+ readonly EQUAL: "primitive";
76
+ readonly NOTEQUAL: "primitive";
77
+ readonly GEQ: "number";
78
+ readonly GREATER: "number";
79
+ readonly LEQ: "number";
80
+ readonly LESS: "number";
81
+ readonly IN: "array";
82
+ readonly NOTIN: "array";
83
+ readonly MATCH: "string";
84
+ readonly NOTMATCH: "string";
85
+ };
86
+ /** Convert an operation's comparison type to a language server type */
87
+ export type ComparisonTypeToTSType<T extends keyof typeof COMPARISON_TYPE_DICTIONARY> = {
88
+ primitive: Primitive;
89
+ boolean: boolean;
90
+ string: string;
91
+ number: number;
92
+ array: Primitive[];
93
+ }[typeof COMPARISON_TYPE_DICTIONARY[T]];
94
+ export type FieldType = 'boolean' | 'string' | 'number';
95
+ export type TypeRecord = Record<string, FieldType | FieldType[]>;
96
+ /** Convert a field type string to a language server type */
97
+ export type FieldTypeToTSType<T extends FieldType> = {
98
+ boolean: boolean;
99
+ string: string;
100
+ number: number;
101
+ }[T];
102
+ /** Primitive values that can be used in comparisons */
103
+ export type Primitive = FieldTypeToTSType<FieldType>;
104
+ /** Convert input type record to language server type record */
105
+ export type ConvertTypeRecord<T extends TypeRecord> = {
106
+ [K in keyof T]: (T[K] extends FieldType[] ? FieldTypeToTSType<T[K][number]> : T[K] extends FieldType ? FieldTypeToTSType<T[K]> : Primitive) & Primitive;
107
+ };
108
+ /**
109
+ * A group of conditions joined by a junction operator
110
+ * @template R A record mapping field names to values
111
+ */
112
+ export interface Group<R extends Record<string, Primitive> = Record<string, Primitive>, V extends boolean = false> {
113
+ type: 'group';
114
+ /** The junction operator */
115
+ operation: JunctionOperation;
116
+ /** The members of the group */
117
+ constituents: Array<Expression<R, V>>;
118
+ }
119
+ /**
120
+ * A query on a field, validated by type constraints
121
+ * @template R A record mapping field names to values
122
+ * @template F The name of the field being queried
123
+ */
124
+ export interface Condition<R extends Record<string, Primitive>, F extends keyof R, O extends ComparisonOperation> {
125
+ type: 'condition';
126
+ /** The operation */
127
+ operation: O;
128
+ /** The name of the field */
129
+ field: F;
130
+ /** The value being checked */
131
+ value: typeof COMPARISON_TYPE_DICTIONARY[O] extends 'array' ? Array<R[F]> : R[F] & ComparisonTypeToTSType<O>;
132
+ /** Was this condition validated by the constraints or is its type unknown? */
133
+ validated: true;
134
+ }
135
+ /**
136
+ * A query on a field
137
+ */
138
+ export interface UncheckedCondition<O extends ComparisonOperation = ComparisonOperation> {
139
+ type: 'condition';
140
+ /** The operation */
141
+ operation: O;
142
+ /** The name of the field */
143
+ field: string;
144
+ /** The value being checked */
145
+ value: ComparisonTypeToTSType<O>;
146
+ /** Was this condition validated by the constraints or is its type unknown? */
147
+ validated: false;
148
+ }
149
+ /**
150
+ * Reverse the keys and values of a type and aggregate by common value
151
+ */
152
+ type ReverseAggregate<T extends Record<any, any>> = {
153
+ [V in T[keyof T]]: {
154
+ [K in keyof T]: T[K] extends V ? K : never;
155
+ }[keyof T];
156
+ };
157
+ type ReverseAggregatedTypes = ReverseAggregate<typeof COMPARISON_TYPE_DICTIONARY>;
158
+ /** Create a union of conditions; an intersection of the operation type validation and constraint type validation */
159
+ export type CheckedConditionSpread<R extends Record<string, Primitive>> = {
160
+ [K in keyof R]: {
161
+ [O in keyof ReverseAggregatedTypes]: Condition<R, K, ReverseAggregatedTypes[O]>;
162
+ }[keyof ReverseAggregatedTypes];
163
+ }[keyof R];
164
+ /** Create a union of conditions that are operation type validated */
165
+ export type UncheckedConditionSpread = {
166
+ [O in keyof ReverseAggregatedTypes]: UncheckedCondition<ReverseAggregatedTypes[O]>;
167
+ }[keyof ReverseAggregatedTypes];
168
+ export type Expression<R extends Record<string, Primitive> = Record<string, Primitive>, V extends boolean = false> = Group<R, V> | (V extends true ? CheckedConditionSpread<R> : CheckedConditionSpread<R> | UncheckedConditionSpread);
169
+ export {};
@@ -0,0 +1,42 @@
1
+ import type { Expression } from './spec';
2
+ export interface StringifyOptions {
3
+ /**
4
+ * The notation to use for junction operations
5
+ * @example Programmatic: foo & bar
6
+ * @example Linguistic: foo AND bar
7
+ * @example Formal: foo ^ bar
8
+ * @default 'programmatic'
9
+ */
10
+ junctionNotation?: 'programmatic' | 'linguistic' | 'formal';
11
+ /**
12
+ * The notation to use for comparison expressions
13
+ * @example Programmatic: foo = bar
14
+ * @example Linguistic: foo EQUALS bar
15
+ * @default 'programmatic'
16
+ */
17
+ comparisonNotation?: 'programmatic' | 'linguistic';
18
+ /**
19
+ * Always surround a group with parentheses, even if unnecessary
20
+ * @default false
21
+ */
22
+ alwaysParenthesize?: boolean;
23
+ /**
24
+ * Don't include spaces (Spaces will be included around operators that are "linguistic")
25
+ * @default false
26
+ */
27
+ compact?: boolean;
28
+ /**
29
+ * Condense boolean values to their implicit denotations
30
+ * @example 'field'
31
+ * @example '!field'
32
+ * @default false
33
+ */
34
+ condenseBooleans?: boolean;
35
+ }
36
+ /**
37
+ * Convert a parsed WizardQL expression to a string
38
+ * @param expression The expression
39
+ * @param opts Formatting options
40
+ * @returns The formatted string
41
+ */
42
+ export declare function stringify(expression: Expression, opts?: StringifyOptions): string;
@@ -0,0 +1,15 @@
1
+ import type { ComparisonOperation, Expression, ComparisonTypeToTSType } from './spec';
2
+ export interface AggregationValue<O extends ComparisonOperation = ComparisonOperation> {
3
+ /** The value being checked */
4
+ value: ComparisonTypeToTSType<O>;
5
+ /** The operator being used */
6
+ operation: O;
7
+ /** Is this an exclusionary operator? (Ex: NOT, NOTIN) */
8
+ exclusionary: boolean;
9
+ }
10
+ /**
11
+ * Summarize a parsed expression by aggregating its queries by field
12
+ * @param expressions The expression or expressions to aggregate
13
+ * @returns The aggregation as a map, mapping field name to queries
14
+ */
15
+ export declare function summarize(expressions: Expression | Expression[]): Map<string, AggregationValue[]>;
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "wizard-ql",
3
+ "author": {
4
+ "name": "Arthur Neuman",
5
+ "url": "https://github.com/exoRift"
6
+ },
7
+ "homepage": "https://github.com/guidance-analytics/wizard-ql",
8
+ "description": "WizardQL is a natural-language-like query language for constructing data queries for resources that meet conditions.",
9
+ "version": "1.0.0",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/guidance-analytics/wizard-ql.git"
13
+ },
14
+ "keywords": ["query", "language", "querylanguage", "parser", "cel", "expression"],
15
+ "license": "Apache-2.0",
16
+ "main": "dist/index.js",
17
+ "module": "dist/index.js",
18
+ "type": "module",
19
+ "types": "dist/index.d.ts",
20
+ "files": ["dist/"],
21
+ "scripts": {
22
+ "build": "[ -d dist ] && rm -r dist; bun build --target=node --format=esm --packages external src/index.ts --outfile dist/index.js && tsc --project tsconfig.build.json",
23
+ "lint": "eslint ."
24
+ },
25
+ "devDependencies": {
26
+ "@types/bun": "latest",
27
+ "eslint": "^9.25.1",
28
+ "eslint-plugin-jsdoc": "^50.6.11",
29
+ "neostandard": "^0.12.1"
30
+ },
31
+ "optionalDependencies": {
32
+ "knex": "^3.1.0"
33
+ },
34
+ "peerDependencies": {
35
+ "typescript": "^5.8.3"
36
+ }
37
+ }