wizard-ql 2.1.1 → 3.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.
package/dist/spec.d.ts CHANGED
@@ -1,181 +1,96 @@
1
- export declare const QUOTES: string[];
2
- export declare const BRACKETS: Array<[open: string, close: string]>;
3
- export declare const PARENS: Array<[open: string, close: string]>;
4
- export declare const NEGATORS: string[];
5
- export declare const ARRAY_DELIMITERS: string[];
6
- /** All available operation aliases (alias -> operation) */
7
- export declare const OPERATION_ALIAS_DICTIONARY: {
8
- readonly AND: "AND";
9
- readonly '&&': "AND";
10
- readonly '&': "AND";
11
- readonly '^': "AND";
12
- readonly OR: "OR";
13
- readonly '||': "OR";
14
- readonly '|': "OR";
15
- readonly V: "OR";
16
- readonly GEQ: "GEQ";
17
- readonly '>=': "GEQ";
18
- readonly '=>': "GEQ";
19
- readonly LEQ: "LEQ";
20
- readonly '<=': "LEQ";
21
- readonly '=<': "LEQ";
22
- readonly NOTEQUALS: "NOTEQUAL";
23
- readonly NOTEQUAL: "NOTEQUAL";
24
- readonly NEQ: "NOTEQUAL";
25
- readonly ISNT: "NOTEQUAL";
26
- readonly '!==': "NOTEQUAL";
27
- readonly '!=': "NOTEQUAL";
28
- readonly EQUALS: "EQUAL";
29
- readonly EQUAL: "EQUAL";
30
- readonly EQ: "EQUAL";
31
- readonly IS: "EQUAL";
32
- readonly '==': "EQUAL";
33
- readonly '=': "EQUAL";
34
- readonly LESS: "LESS";
35
- readonly '<': "LESS";
36
- readonly GREATER: "GREATER";
37
- readonly '>': "GREATER";
38
- readonly MORE: "GREATER";
39
- readonly NOTIN: "NOTIN";
40
- readonly '!:': "NOTIN";
41
- readonly IN: "IN";
42
- readonly ':': "IN";
43
- readonly NOTMATCHES: "NOTMATCH";
44
- readonly NOTMATCH: "NOTMATCH";
45
- readonly '!~': "NOTMATCH";
46
- readonly MATCHES: "MATCH";
47
- readonly MATCH: "MATCH";
48
- readonly '~': "MATCH";
49
- };
50
- export interface Token {
51
- /** The text content of the token */
52
- content: string;
53
- /** The index in the original expression this token originates from */
54
- index: number;
1
+ export type FieldType = 'boolean' | 'string' | 'number' | 'date';
2
+ export type ConditionOperatorComparisonType = 'primitive' | 'boolean' | 'string' | 'number' | 'date' | 'numeric' | 'array';
3
+ export type JunctionOperatorType = 'sumjunction' | 'productjunction';
4
+ export type OperatorType = JunctionOperatorType | ConditionOperatorComparisonType;
5
+ export type FieldTypeRecord = Record<string, FieldType | FieldType[]>;
6
+ export interface OperatorRecordEntry {
7
+ /** The negation of this operator */
8
+ negationName: Uppercase<string>;
9
+ /** The data type this operator acts upon */
10
+ type: OperatorType;
11
+ /** Aliases for this operator */
12
+ aliases?: ReadonlyArray<Uppercase<string>>;
13
+ /** Aliases for the negation of this operator */
14
+ negationAliases?: ReadonlyArray<Uppercase<string>>;
15
+ /**
16
+ * Does this operator exclude the value being provided?\
17
+ * The negation's exclusionary trait will be the opposite of this
18
+ * @default false
19
+ */
20
+ exclusionary?: boolean;
55
21
  }
56
- export type Operation = (typeof OPERATION_ALIAS_DICTIONARY)[keyof typeof OPERATION_ALIAS_DICTIONARY];
57
- export declare const ALIASES: string[];
58
- /** All base operations and their type */
59
- export declare const OPERATION_PURPOSE_DICTIONARY: {
60
- readonly AND: "junction";
61
- readonly OR: "junction";
62
- readonly EQUAL: "comparison";
63
- readonly NOTEQUAL: "comparison";
64
- readonly LESS: "comparison";
65
- readonly GREATER: "comparison";
66
- readonly GEQ: "comparison";
67
- readonly LEQ: "comparison";
68
- readonly IN: "comparison";
69
- readonly NOTIN: "comparison";
70
- readonly MATCH: "comparison";
71
- readonly NOTMATCH: "comparison";
72
- };
73
- type KeysWhereValue<T, V> = Exclude<{
74
- [K in keyof T]: T[K] extends V ? K : never;
75
- }[keyof T], never>;
76
- export type JunctionOperation = KeysWhereValue<typeof OPERATION_PURPOSE_DICTIONARY, 'junction'>;
77
- export type ComparisonOperation = KeysWhereValue<typeof OPERATION_PURPOSE_DICTIONARY, 'comparison'>;
78
- /** All comparison operations and their types */
79
- export declare const COMPARISON_TYPE_DICTIONARY: {
80
- readonly EQUAL: "primitive";
81
- readonly NOTEQUAL: "primitive";
82
- readonly GEQ: "numeric";
83
- readonly GREATER: "numeric";
84
- readonly LEQ: "numeric";
85
- readonly LESS: "numeric";
86
- readonly IN: "array";
87
- readonly NOTIN: "array";
88
- readonly MATCH: "string";
89
- readonly NOTMATCH: "string";
90
- };
91
- /** Convert an operation's comparison type to a language server type */
92
- export type ComparisonTypeToTSType<T extends keyof typeof COMPARISON_TYPE_DICTIONARY> = {
93
- primitive: Primitive;
22
+ export type OperatorRecord = Record<Uppercase<string>, OperatorRecordEntry>;
23
+ export type GetConditionOperators<O extends OperatorRecord> = string & {
24
+ [K in keyof O]: O[K] extends OperatorRecordEntry ? O[K] extends {
25
+ type: JunctionOperatorType;
26
+ } ? never : K | O[K]['negationName'] : never;
27
+ }[keyof O];
28
+ export type GetJunctionOperators<O extends OperatorRecord> = string & {
29
+ [K in keyof O]: O[K] extends OperatorRecordEntry ? O[K] extends {
30
+ type: JunctionOperatorType;
31
+ } ? K | O[K]['negationName'] : never : never;
32
+ }[keyof O];
33
+ export type GetOperators<O extends OperatorRecord> = GetConditionOperators<O> | GetJunctionOperators<O>;
34
+ export type GetOperatorDefinition<O extends OperatorRecord, P extends GetOperators<O>> = {
35
+ [K in keyof O]: O[K] extends OperatorRecordEntry ? P extends K | O[K]['negationName'] | (O[K]['aliases'] extends ReadonlyArray<infer A> ? A : never) | (O[K]['negationAliases'] extends ReadonlyArray<infer NA> ? NA : never) ? O[K] : never : never;
36
+ }[keyof O];
37
+ type Unroll<T> = T extends ReadonlyArray<infer U> ? U : T;
38
+ export type GetFieldTSType<T extends FieldType | FieldType[]> = {
94
39
  boolean: boolean;
95
40
  string: string;
96
41
  number: number;
97
42
  date: Date;
98
- numeric: number | Date;
99
- array: Primitive[];
100
- }[typeof COMPARISON_TYPE_DICTIONARY[T]];
101
- export type FieldType = 'boolean' | 'string' | 'number' | 'date';
102
- export type TypeRecord = Record<string, FieldType | FieldType[]>;
103
- /** Convert a field type string to a language server type */
104
- export type FieldTypeToTSType<T extends FieldType> = {
43
+ }[Unroll<T>];
44
+ export type Primitive = GetFieldTSType<FieldType>;
45
+ export type GetConditionTSType<T extends ConditionOperatorComparisonType> = {
46
+ primitive: Primitive;
105
47
  boolean: boolean;
106
48
  string: string;
107
49
  number: number;
108
50
  date: Date;
51
+ numeric: number | Date;
52
+ array: Primitive[];
109
53
  }[T];
110
- /** Primitive values that can be used in comparisons */
111
- export type Primitive = FieldTypeToTSType<FieldType>;
112
- /** Convert input type record to language server type record */
113
- export type ConvertTypeRecord<T extends TypeRecord> = {
114
- [K in keyof T]: T[K] extends FieldType[] ? FieldTypeToTSType<T[K][number]> : T[K] extends FieldType ? FieldTypeToTSType<T[K]> : Primitive;
115
- };
116
- export declare const TYPE_PRIORITY: ["boolean", "date", "number", "string"];
117
- /**
118
- * A group of conditions joined by a junction operator
119
- * @template R A record mapping field names to values
120
- */
121
- export interface Group<R extends Record<string, unknown> = Record<string, Primitive>, V extends boolean = false> {
122
- type: 'group';
123
- /** The junction operator */
124
- operation: JunctionOperation;
125
- /** The members of the group */
126
- constituents: Array<Expression<R, V>>;
127
- }
128
- /**
129
- * A query on a field, validated by type constraints
130
- * @template R A record mapping field names to values
131
- * @template F The name of the field being queried
132
- */
133
- export interface Condition<R extends Record<string, unknown>, F extends keyof R, O extends ComparisonOperation> {
54
+ export interface CheckedCondition<F extends FieldTypeRecord = FieldTypeRecord, O extends OperatorRecord = OperatorRecord, I extends keyof F = keyof F, P extends GetConditionOperators<O> = GetConditionOperators<O>> {
134
55
  type: 'condition';
135
- /** The operation */
136
- operation: O;
137
- /** The name of the field */
138
- field: F;
139
- /** The value being checked */
140
- value: typeof COMPARISON_TYPE_DICTIONARY[O] extends 'array' ? Array<R[F]> : Extract<ComparisonTypeToTSType<O>, R[F]>;
141
- /** Was this condition validated by the constraints or is its type unknown? */
56
+ operation: P;
57
+ field: I;
58
+ value: GetOperatorDefinition<O, P>['type'] extends 'array' ? Array<GetFieldTSType<F[I]>> : Extract<GetConditionTSType<GetOperatorDefinition<O, P>['type'] & ConditionOperatorComparisonType>, GetFieldTSType<F[I]>>;
142
59
  validated: true;
143
60
  }
144
- /**
145
- * A query on a field
146
- */
147
- export interface UncheckedCondition<O extends ComparisonOperation = ComparisonOperation> {
61
+ export interface UncheckedCondition<O extends OperatorRecord = OperatorRecord, P extends GetConditionOperators<O> = GetConditionOperators<O>> {
148
62
  type: 'condition';
149
- /** The operation */
150
- operation: O;
151
- /** The name of the field */
63
+ operation: P;
152
64
  field: string;
153
- /** The value being checked */
154
- value: Exclude<ComparisonTypeToTSType<O>, Date>;
155
- /** Was this condition validated by the constraints or is its type unknown? */
65
+ value: GetOperatorDefinition<O, P>['type'] extends 'array' ? Primitive[] : GetConditionTSType<GetOperatorDefinition<O, P>['type'] & ConditionOperatorComparisonType>;
156
66
  validated: false;
157
67
  }
158
- /**
159
- * Reverse the keys and values of a type and aggregate by common value
160
- */
161
- type ReverseAggregate<T extends Record<any, any>> = {
162
- [V in T[keyof T]]: {
163
- [K in keyof T]: T[K] extends V ? K : never;
164
- }[keyof T];
165
- };
166
- type ReverseAggregatedTypes = ReverseAggregate<typeof COMPARISON_TYPE_DICTIONARY>;
68
+ export interface Group<F extends FieldTypeRecord = FieldTypeRecord, O extends OperatorRecord = OperatorRecord, V extends boolean = false> {
69
+ type: 'group';
70
+ operation: GetJunctionOperators<O> & string;
71
+ constituents: Array<Expression<F, O, V>>;
72
+ }
167
73
  /** Create a union of conditions; an intersection of the operation type validation and constraint type validation */
168
- export type CheckedConditionSpread<R extends Record<string, unknown>> = {
169
- [K in keyof R]: {
170
- [O in keyof ReverseAggregatedTypes]: Condition<R, K, ReverseAggregatedTypes[O]>;
171
- }[keyof ReverseAggregatedTypes];
172
- }[keyof R];
74
+ type CheckedConditionSpread<F extends FieldTypeRecord, O extends OperatorRecord> = {
75
+ [I in keyof F]: {
76
+ [P in GetConditionOperators<O>]: [CheckedCondition<F, O, I, P>['value']] extends [never] ? never : CheckedCondition<F, O, I, P>;
77
+ }[GetConditionOperators<O>];
78
+ }[keyof F];
173
79
  /** Create a union of conditions that are operation type validated */
174
- export type UncheckedConditionSpread = {
175
- [O in keyof ReverseAggregatedTypes]: UncheckedCondition<ReverseAggregatedTypes[O]>;
176
- }[keyof ReverseAggregatedTypes];
177
- export type Expression<R extends Record<string, unknown> = Record<string, Primitive>, V extends boolean = false> = Group<R, V> | (V extends true ? CheckedConditionSpread<R> : CheckedConditionSpread<R> | UncheckedConditionSpread);
178
- export type UncheckedExpression = (Omit<Group, 'constituents'> & {
179
- constituents: UncheckedExpression[];
180
- }) | UncheckedConditionSpread;
80
+ type UncheckedConditionSpread<O extends OperatorRecord> = {
81
+ [P in GetConditionOperators<O>]: [UncheckedCondition<O, P>['value']] extends [never] ? never : UncheckedCondition<O, P>;
82
+ }[GetConditionOperators<O>];
83
+ export type Expression<F extends FieldTypeRecord = FieldTypeRecord, O extends OperatorRecord = OperatorRecord, V extends boolean = false> = Group<F, O, V> | (V extends true ? CheckedConditionSpread<F, O> : CheckedConditionSpread<F, O> | UncheckedConditionSpread<O>);
84
+ export declare const TYPE_PRIORITY: ["boolean", "date", "number", "string"];
85
+ export interface Token {
86
+ /** The text content of the token */
87
+ content: string;
88
+ /** The index in the original expression this token originates from */
89
+ index: number;
90
+ }
91
+ export type PartType = 'literal' | 'junctionoperator' | 'conditionoperator' | 'quoted' | 'number' | 'openingarraybracket' | 'closingarraybracket' | 'arraydelimiter' | 'openinggroupbracket' | 'closinggroupbracket' | 'negator';
92
+ export interface ClassifiedToken extends Token {
93
+ /** The part type of the token */
94
+ partType: PartType;
95
+ }
181
96
  export {};
package/package.json CHANGED
@@ -4,12 +4,12 @@
4
4
  "name": "Arthur Neuman",
5
5
  "url": "https://github.com/exoRift"
6
6
  },
7
- "homepage": "https://github.com/guidance-analytics/wizard-ql",
7
+ "homepage": "https://github.com/exoRift/wizard-ql",
8
8
  "description": "WizardQL is a natural-language-like query language for constructing data queries for resources that meet conditions.",
9
- "version": "2.1.1",
9
+ "version": "3.0.0",
10
10
  "repository": {
11
11
  "type": "git",
12
- "url": "git+https://github.com/guidance-analytics/wizard-ql.git"
12
+ "url": "git+https://github.com/exoRift/wizard-ql.git"
13
13
  },
14
14
  "keywords": [
15
15
  "query",
@@ -29,13 +29,21 @@
29
29
  ],
30
30
  "scripts": {
31
31
  "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",
32
- "lint": "eslint ."
32
+ "lint": "eslint .",
33
+ "pretest:types": "bun run build && find test -name '*.test-d.ts' -exec perl -pi -e 's|// \\@ts-expect-error|// ts-expect-error|g' {} +",
34
+ "test:types": "tsd --files 'test/**/*.test-d.ts'",
35
+ "posttest:types": "find test -name '*.test-d.ts' -exec perl -pi -e 's|// ts-expect-error|// \\@ts-expect-error|g' {} +",
36
+ "test": "bun run test:types && bun test",
37
+ "preview": "bun run build && bunx serve ./test/preview",
38
+ "prepack": "bun run build"
33
39
  },
34
40
  "devDependencies": {
35
- "@types/bun": "latest",
41
+ "@types/bun": "^1.3.13",
42
+ "custom-types": "github:exoRift/custom-types",
36
43
  "eslint": "^9.25.1",
44
+ "eslint-config": "github:exoRift/eslint-config",
37
45
  "eslint-plugin-jsdoc": "^50.6.11",
38
- "neostandard": "^0.12.1"
46
+ "tsd": "^0.33.0"
39
47
  },
40
48
  "optionalDependencies": {
41
49
  "knex": "^3.1.0"
package/dist/parse.d.ts DELETED
@@ -1,55 +0,0 @@
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
- * Restrict an exact value by providing a string.\
16
- * Restrict a pattern by providing a Regex expression.\
17
- * By default allow any value and restrict a collection of values by passing ['deny', VALUES[]]\
18
- * By default restrict any value and allow a collection of values by passing ['allow', VALUES[]]\
19
- * If the query value is of array type, it will check all entries of the array and ensure they're all allowed.\
20
- * This check runs before type coercion so the value checked will always be a string. However, quotes and escapes WILL be removed
21
- */
22
- restricted?: Partial<Record<keyof T | (string & {}), boolean | ['allow' | 'deny', Array<string | RegExp>]>>;
23
- /**
24
- * The types of fields\
25
- * Either provide the field type singularly or permit multiple types with an array of field types\
26
- * A field with type 'date' will parse the value into a Date object if possible (Wizard will not attempt to do this otherwise)\
27
- * Type coercion priority: boolean -> date -> number -> string
28
- */
29
- types?: T;
30
- /**
31
- * Field names in restriction checks and type checks are case insensitive
32
- * @note If enabled, all fields will be returned as their casing denoted by the types or restricted record
33
- * @warn Mismatching casing between the restricted record and the type record will prioritize the restricted record
34
- */
35
- caseInsensitive?: boolean;
36
- /**
37
- * Disallow fields that are not present in the "restricted" record or the "types" record
38
- */
39
- disallowUnvalidated?: V;
40
- /**
41
- * A callback that determines how dates are interpreted\
42
- * By default, uses `new Date()`
43
- */
44
- dateInterpreter?: (v: string | number) => Date;
45
- }
46
- /**
47
- * Parse a Wizard expression into its object form
48
- * @template T A type record, mapping field names to their types
49
- * @param expression The Wizard expression as a string or as an array of tokens
50
- * @param constraints Constraints to add on parsing such as forced types or restricted columns
51
- * @returns The object representation
52
- * @throws {ParseError | ConstraintError}
53
- */
54
- export declare function parse<const T extends TypeRecord = {}, // eslint-disable-line @typescript-eslint/no-empty-object-type
55
- const V extends boolean = false>(expression: string | string[] | Token[], constraints?: ExpressionConstraints<T, V>): Expression<ConvertTypeRecord<T>, V> | null;
@@ -1,42 +0,0 @@
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;
@@ -1,15 +0,0 @@
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[]>;