@cedarjs/gqlorm 2.6.1-next.104

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 (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/dist/cjs/generator/graphqlGenerator.d.ts +24 -0
  4. package/dist/cjs/generator/graphqlGenerator.d.ts.map +1 -0
  5. package/dist/cjs/generator/graphqlGenerator.js +421 -0
  6. package/dist/cjs/live/types.d.ts +262 -0
  7. package/dist/cjs/live/types.d.ts.map +1 -0
  8. package/dist/cjs/live/types.js +16 -0
  9. package/dist/cjs/package.json +1 -0
  10. package/dist/cjs/parser/queryParser.d.ts +27 -0
  11. package/dist/cjs/parser/queryParser.d.ts.map +1 -0
  12. package/dist/cjs/parser/queryParser.js +338 -0
  13. package/dist/cjs/queryBuilder.d.ts +106 -0
  14. package/dist/cjs/queryBuilder.d.ts.map +1 -0
  15. package/dist/cjs/queryBuilder.js +225 -0
  16. package/dist/cjs/react/useLiveQuery.d.ts +12 -0
  17. package/dist/cjs/react/useLiveQuery.d.ts.map +1 -0
  18. package/dist/cjs/react/useLiveQuery.js +62 -0
  19. package/dist/cjs/types/ast.d.ts +80 -0
  20. package/dist/cjs/types/ast.d.ts.map +1 -0
  21. package/dist/cjs/types/ast.js +16 -0
  22. package/dist/cjs/types/orm.d.ts +157 -0
  23. package/dist/cjs/types/orm.d.ts.map +1 -0
  24. package/dist/cjs/types/orm.js +16 -0
  25. package/dist/cjs/types/orm_for_testing.d.ts +44 -0
  26. package/dist/cjs/types/orm_for_testing.d.ts.map +1 -0
  27. package/dist/cjs/types/orm_for_testing.js +16 -0
  28. package/dist/cjs/types/schema.d.ts +4 -0
  29. package/dist/cjs/types/schema.d.ts.map +1 -0
  30. package/dist/cjs/types/schema.js +16 -0
  31. package/dist/cjs/types/typeUtils.d.ts +10 -0
  32. package/dist/cjs/types/typeUtils.d.ts.map +1 -0
  33. package/dist/cjs/types/typeUtils.js +65 -0
  34. package/dist/generator/graphqlGenerator.d.ts +24 -0
  35. package/dist/generator/graphqlGenerator.d.ts.map +1 -0
  36. package/dist/generator/graphqlGenerator.js +399 -0
  37. package/dist/live/types.d.ts +262 -0
  38. package/dist/live/types.d.ts.map +1 -0
  39. package/dist/live/types.js +0 -0
  40. package/dist/parser/queryParser.d.ts +27 -0
  41. package/dist/parser/queryParser.d.ts.map +1 -0
  42. package/dist/parser/queryParser.js +312 -0
  43. package/dist/queryBuilder.d.ts +106 -0
  44. package/dist/queryBuilder.d.ts.map +1 -0
  45. package/dist/queryBuilder.js +196 -0
  46. package/dist/react/useLiveQuery.d.ts +12 -0
  47. package/dist/react/useLiveQuery.d.ts.map +1 -0
  48. package/dist/react/useLiveQuery.js +38 -0
  49. package/dist/types/ast.d.ts +80 -0
  50. package/dist/types/ast.d.ts.map +1 -0
  51. package/dist/types/ast.js +0 -0
  52. package/dist/types/orm.d.ts +157 -0
  53. package/dist/types/orm.d.ts.map +1 -0
  54. package/dist/types/orm.js +0 -0
  55. package/dist/types/orm_for_testing.d.ts +44 -0
  56. package/dist/types/orm_for_testing.d.ts.map +1 -0
  57. package/dist/types/orm_for_testing.js +0 -0
  58. package/dist/types/schema.d.ts +4 -0
  59. package/dist/types/schema.d.ts.map +1 -0
  60. package/dist/types/schema.js +0 -0
  61. package/dist/types/typeUtils.d.ts +10 -0
  62. package/dist/types/typeUtils.d.ts.map +1 -0
  63. package/dist/types/typeUtils.js +34 -0
  64. package/package.json +163 -0
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Type definitions for live query functionality
3
+ * Defines interfaces and types for real-time GraphQL queries with @live directive
4
+ */
5
+ import { type ModelSchema } from '../types/schema.js';
6
+ export interface LiveQueryOptions {
7
+ /**
8
+ * Whether to automatically reconnect on connection loss
9
+ * @default true
10
+ */
11
+ autoReconnect?: boolean;
12
+ /**
13
+ * Maximum number of reconnection attempts
14
+ * @default 5
15
+ */
16
+ maxReconnectAttempts?: number;
17
+ /**
18
+ * Delay between reconnection attempts in milliseconds
19
+ * @default 1000
20
+ */
21
+ reconnectDelay?: number;
22
+ /**
23
+ * Whether to use exponential backoff for reconnection delays
24
+ * @default true
25
+ */
26
+ exponentialBackoff?: boolean;
27
+ /**
28
+ * Maximum delay between reconnection attempts in milliseconds
29
+ * @default 30000
30
+ */
31
+ maxReconnectDelay?: number;
32
+ /**
33
+ * Custom headers to send with live query requests
34
+ */
35
+ headers?: Record<string, string>;
36
+ /**
37
+ * Custom authentication token
38
+ */
39
+ authToken?: string;
40
+ /**
41
+ * WebSocket URL for live query subscriptions
42
+ */
43
+ websocketUrl?: string;
44
+ /**
45
+ * Whether to enable debug logging
46
+ * @default false
47
+ */
48
+ debug?: boolean;
49
+ /**
50
+ * Whether to resubscribe to queries upon reconnection
51
+ * @default true
52
+ */
53
+ refetchOnReconnect?: boolean;
54
+ }
55
+ export type ResolvedLiveQueryOptions = Required<Omit<LiveQueryOptions, 'authToken' | 'websocketUrl'>> & {
56
+ authToken: string | undefined;
57
+ websocketUrl: string | undefined;
58
+ };
59
+ export type LiveQueryConnectionState = 'connecting' | 'connected' | 'disconnected' | 'reconnecting' | 'error' | 'closed';
60
+ export type LiveQueryUpdateType = 'insert' | 'update' | 'delete' | 'refresh';
61
+ export interface LiveQueryUpdate<T = any> {
62
+ /**
63
+ * Type of update that occurred
64
+ */
65
+ type: LiveQueryUpdateType;
66
+ /**
67
+ * Updated data (for insert/update) or deleted item (for delete)
68
+ */
69
+ data?: T;
70
+ /**
71
+ * Complete updated result set (for refresh)
72
+ */
73
+ result?: T;
74
+ /**
75
+ * Timestamp when the update occurred
76
+ */
77
+ timestamp: Date;
78
+ /**
79
+ * Optional metadata about the update
80
+ */
81
+ metadata?: Record<string, any>;
82
+ }
83
+ export interface LiveQueryError {
84
+ /**
85
+ * Error code
86
+ */
87
+ code: string;
88
+ /**
89
+ * Human-readable error message
90
+ */
91
+ message: string;
92
+ /**
93
+ * Optional error details
94
+ */
95
+ details?: any;
96
+ /**
97
+ * Timestamp when the error occurred
98
+ */
99
+ timestamp: Date;
100
+ }
101
+ export interface LiveQuerySubscription<T = any> {
102
+ /**
103
+ * Unique identifier for this subscription
104
+ */
105
+ id: string;
106
+ /**
107
+ * GraphQL query string
108
+ */
109
+ query: string;
110
+ /**
111
+ * Query variables
112
+ */
113
+ variables?: Record<string, any>;
114
+ /**
115
+ * Current connection state
116
+ */
117
+ connectionState: LiveQueryConnectionState;
118
+ /**
119
+ * Latest query result
120
+ */
121
+ data: T | undefined;
122
+ /**
123
+ * Latest error, if any
124
+ */
125
+ error: LiveQueryError | undefined;
126
+ /**
127
+ * Whether the subscription is currently loading
128
+ */
129
+ loading: boolean;
130
+ /**
131
+ * Unsubscribe from live updates
132
+ */
133
+ unsubscribe: () => void;
134
+ /**
135
+ * Manually refetch the query
136
+ */
137
+ refetch: () => Promise<void>;
138
+ /**
139
+ * Register a callback for live data updates
140
+ * Returns a function to unregister the callback
141
+ */
142
+ onUpdate: (callback: (update: LiveQueryUpdate) => void) => () => void;
143
+ }
144
+ export interface LiveQueryClientConfig extends LiveQueryOptions {
145
+ /**
146
+ * GraphQL endpoint URL
147
+ */
148
+ endpoint: string;
149
+ /**
150
+ * WebSocket endpoint URL (optional, will derive from endpoint if not provided)
151
+ */
152
+ websocketUrl?: string;
153
+ /**
154
+ * Model schema defining scalar fields for each model
155
+ */
156
+ schema?: ModelSchema;
157
+ }
158
+ export interface LiveQueryTransport {
159
+ /**
160
+ * Connect to the live query service
161
+ */
162
+ connect(): Promise<void>;
163
+ /**
164
+ * Disconnect from the live query service
165
+ */
166
+ disconnect(): Promise<void>;
167
+ /**
168
+ * Subscribe to a live query
169
+ */
170
+ subscribe<T>(query: string, variables?: Record<string, any>, options?: LiveQueryOptions): LiveQuerySubscription<T>;
171
+ /**
172
+ * Get current connection state
173
+ */
174
+ getConnectionState(): LiveQueryConnectionState;
175
+ /**
176
+ * Add connection state change listener
177
+ */
178
+ onConnectionStateChange(listener: (state: LiveQueryConnectionState) => void): () => void;
179
+ /**
180
+ * Add error listener
181
+ */
182
+ onError(listener: (error: LiveQueryError) => void): () => void;
183
+ }
184
+ export interface LiveQueryCache {
185
+ /**
186
+ * Get cached result for a query
187
+ */
188
+ get<T>(key: string): T | undefined;
189
+ /**
190
+ * Set cached result for a query
191
+ */
192
+ set<T>(key: string, data: T): void;
193
+ /**
194
+ * Remove cached result
195
+ */
196
+ remove(key: string): void;
197
+ /**
198
+ * Clear all cached results
199
+ */
200
+ clear(): void;
201
+ /**
202
+ * Get cache key for a query
203
+ */
204
+ getKey(query: string, variables?: Record<string, any>): string;
205
+ }
206
+ export interface LiveQueryContextValue {
207
+ /**
208
+ * Live query transport instance
209
+ */
210
+ transport?: LiveQueryTransport;
211
+ /**
212
+ * Live query cache instance
213
+ */
214
+ cache?: LiveQueryCache;
215
+ /**
216
+ * Global live query options
217
+ */
218
+ options?: LiveQueryOptions;
219
+ /**
220
+ * Model schema defining scalar fields for each model
221
+ */
222
+ schema?: ModelSchema;
223
+ /**
224
+ * Query builder instance with schema
225
+ */
226
+ queryBuilder?: any;
227
+ /**
228
+ * Whether the client is connected
229
+ */
230
+ isConnected: boolean;
231
+ /**
232
+ * Current connection state
233
+ */
234
+ connectionState: LiveQueryConnectionState;
235
+ }
236
+ export interface LiveQueryHookResult<T = any> {
237
+ /**
238
+ * Query result data
239
+ */
240
+ data: T | undefined;
241
+ /**
242
+ * Loading state
243
+ */
244
+ loading: boolean;
245
+ /**
246
+ * Error state
247
+ */
248
+ error: LiveQueryError | undefined;
249
+ /**
250
+ * Connection state
251
+ */
252
+ connectionState: LiveQueryConnectionState;
253
+ /**
254
+ * Refetch function
255
+ */
256
+ refetch: () => Promise<void>;
257
+ /**
258
+ * Subscription instance (for advanced usage)
259
+ */
260
+ subscription: LiveQuerySubscription<T> | undefined;
261
+ }
262
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/live/types.ts"],"names":[],"mappings":"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"}
File without changes
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Query parser that converts ORM-style queries to AST representation
3
+ * Handles parsing of Prisma-like query syntax into our internal AST format
4
+ */
5
+ import type { QueryAST, QueryOperation } from '../types/ast.js';
6
+ import type { FindFirstArgs, FindManyArgs, FindUniqueArgs } from '../types/orm.js';
7
+ export declare class QueryParseError extends Error {
8
+ context?: any | undefined;
9
+ constructor(message: string, context?: any | undefined);
10
+ }
11
+ export declare class QueryParser {
12
+ #private;
13
+ /**
14
+ * Parse a complete query from model, operation, and arguments
15
+ */
16
+ parseQuery(model: string, operation: QueryOperation, args?: FindManyArgs<any> | FindUniqueArgs<any> | FindFirstArgs<any>): QueryAST;
17
+ /**
18
+ * Parse comparison operator string
19
+ */
20
+ private parseComparisonOperator;
21
+ /**
22
+ * Validate AST structure
23
+ */
24
+ validateAST(ast: QueryAST): void;
25
+ }
26
+ export declare const queryParser: QueryParser;
27
+ //# sourceMappingURL=queryParser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queryParser.d.ts","sourceRoot":"","sources":["../../src/parser/queryParser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAUV,QAAQ,EACR,cAAc,EAMf,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,cAAc,EAKf,MAAM,iBAAiB,CAAA;AAGxB,qBAAa,eAAgB,SAAQ,KAAK;IAG/B,OAAO,CAAC,EAAE,GAAG;gBADpB,OAAO,EAAE,MAAM,EACR,OAAO,CAAC,EAAE,GAAG,YAAA;CAKvB;AAED,qBAAa,WAAW;;IACtB;;OAEG;IACH,UAAU,CACR,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,cAAc,EACzB,IAAI,CAAC,EAAE,YAAY,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,GAClE,QAAQ;IAoNX;;OAEG;IACH,OAAO,CAAC,uBAAuB;IA2I/B;;OAEG;IACH,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI;CAmBjC;AAGD,eAAO,MAAM,WAAW,aAAoB,CAAA"}
@@ -0,0 +1,312 @@
1
+ import { isLogicalOperator } from "../types/typeUtils.js";
2
+ class QueryParseError extends Error {
3
+ constructor(message, context) {
4
+ super(message);
5
+ this.context = context;
6
+ this.name = "QueryParseError";
7
+ }
8
+ }
9
+ class QueryParser {
10
+ /**
11
+ * Parse a complete query from model, operation, and arguments
12
+ */
13
+ parseQuery(model, operation, args) {
14
+ if (!model || !operation) {
15
+ throw new QueryParseError("Model and operation are required");
16
+ }
17
+ const query = {
18
+ type: "Query",
19
+ model,
20
+ operation
21
+ };
22
+ if (args && Object.keys(args).length > 0) {
23
+ query.args = this.#parseQueryArgs(args);
24
+ }
25
+ return query;
26
+ }
27
+ /**
28
+ * Parse query arguments into AST
29
+ */
30
+ #parseQueryArgs(args) {
31
+ const queryArgs = {
32
+ type: "QueryArgs"
33
+ };
34
+ if (args.where) {
35
+ queryArgs.where = this.#parseWhere(args.where);
36
+ }
37
+ if (args.select) {
38
+ queryArgs.select = this.#parseSelect(args.select);
39
+ }
40
+ if (args.include) {
41
+ queryArgs.include = this.#parseInclude(args.include);
42
+ }
43
+ if (args.orderBy) {
44
+ queryArgs.orderBy = this.#parseOrderBy(args.orderBy);
45
+ }
46
+ if (typeof args.take === "number") {
47
+ queryArgs.take = args.take;
48
+ }
49
+ if (typeof args.skip === "number") {
50
+ queryArgs.skip = args.skip;
51
+ }
52
+ return queryArgs;
53
+ }
54
+ /**
55
+ * Parse WHERE clause into AST
56
+ */
57
+ #parseWhere(where) {
58
+ const conditions = this.#parseWhereConditions(where);
59
+ return {
60
+ type: "Where",
61
+ conditions
62
+ };
63
+ }
64
+ /**
65
+ * Parse where conditions recursively
66
+ */
67
+ // TODO: See if we can type `where` to `Record<any, any>`
68
+ #parseWhereConditions(where) {
69
+ const conditions = Object.entries(where).map(([key, value]) => {
70
+ if (isLogicalOperator(key)) {
71
+ return this.#parseLogicalCondition(key, value);
72
+ }
73
+ if (this.#isRelationCondition(value)) {
74
+ return this.#parseRelationCondition(key, value);
75
+ }
76
+ return this.#parseFieldCondition(key, value);
77
+ });
78
+ return conditions;
79
+ }
80
+ /**
81
+ * Parse logical condition (AND, OR, NOT)
82
+ */
83
+ #parseLogicalCondition(operator, value) {
84
+ let nestedConditions = [];
85
+ if (operator === "NOT") {
86
+ nestedConditions = this.#parseWhereConditions(value);
87
+ } else {
88
+ if (Array.isArray(value)) {
89
+ for (const condition of value) {
90
+ nestedConditions.push(...this.#parseWhereConditions(condition));
91
+ }
92
+ } else {
93
+ throw new QueryParseError(`${operator} operator expects an array`, {
94
+ operator,
95
+ value
96
+ });
97
+ }
98
+ }
99
+ return {
100
+ type: "LogicalCondition",
101
+ operator,
102
+ conditions: nestedConditions
103
+ };
104
+ }
105
+ /**
106
+ * Check if a value represents a relation condition
107
+ */
108
+ #isRelationCondition(value) {
109
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !this.#isFilterObject(value) && !(value instanceof Date);
110
+ }
111
+ /**
112
+ * Parse relation condition
113
+ */
114
+ #parseRelationCondition(field, value) {
115
+ const nestedConditions = this.#parseWhereConditions(value);
116
+ return {
117
+ type: "RelationCondition",
118
+ relation: field,
119
+ condition: {
120
+ type: "Where",
121
+ conditions: nestedConditions
122
+ }
123
+ };
124
+ }
125
+ /**
126
+ * Check if value represents a filter object
127
+ */
128
+ #isFilterObject(value) {
129
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
130
+ return false;
131
+ }
132
+ const filterOperators = /* @__PURE__ */ new Set([
133
+ "equals",
134
+ "not",
135
+ "in",
136
+ "notIn",
137
+ "lt",
138
+ "lte",
139
+ "gt",
140
+ "gte",
141
+ "contains",
142
+ "startsWith",
143
+ "endsWith",
144
+ "isNull",
145
+ "isNotNull"
146
+ ]);
147
+ return Object.keys(value).some((key) => filterOperators.has(key));
148
+ }
149
+ /**
150
+ * Parse field condition
151
+ */
152
+ #parseFieldCondition(field, value) {
153
+ if (!this.#isFilterObject(value)) {
154
+ return {
155
+ type: "FieldCondition",
156
+ field,
157
+ operator: "equals",
158
+ value
159
+ };
160
+ }
161
+ const filterEntries = Object.entries(value);
162
+ if (filterEntries.length !== 1) {
163
+ throw new QueryParseError(
164
+ `Field condition must have exactly one operator`,
165
+ { field, value }
166
+ );
167
+ }
168
+ const [operatorStr, operatorValue] = filterEntries[0];
169
+ const operator = this.parseComparisonOperator(operatorStr);
170
+ return {
171
+ type: "FieldCondition",
172
+ field,
173
+ operator,
174
+ value: operatorValue
175
+ };
176
+ }
177
+ /**
178
+ * Parse comparison operator string
179
+ */
180
+ parseComparisonOperator(operator) {
181
+ const validOperators = [
182
+ "equals",
183
+ "not",
184
+ "in",
185
+ "notIn",
186
+ "lt",
187
+ "lte",
188
+ "gt",
189
+ "gte",
190
+ "contains",
191
+ "startsWith",
192
+ "endsWith",
193
+ "isNull",
194
+ "isNotNull"
195
+ ];
196
+ if (!validOperators.includes(operator)) {
197
+ throw new QueryParseError(`Invalid comparison operator: ${operator}`);
198
+ }
199
+ return operator;
200
+ }
201
+ /**
202
+ * Parse SELECT clause into AST
203
+ */
204
+ #parseSelect(select) {
205
+ const fields = [];
206
+ for (const [field, selected] of Object.entries(select)) {
207
+ if (typeof selected === "boolean") {
208
+ fields.push({
209
+ type: "FieldSelection",
210
+ field,
211
+ selected
212
+ });
213
+ } else if (typeof selected === "object" && selected !== null) {
214
+ const selectedObj = selected;
215
+ const fieldSelection = {
216
+ type: "FieldSelection",
217
+ field,
218
+ selected: true
219
+ };
220
+ if (selectedObj.select) {
221
+ fieldSelection.nested = this.#parseSelect(selectedObj.select);
222
+ } else if (selectedObj.include) {
223
+ fieldSelection.nested = this.#parseInclude(selectedObj.include);
224
+ }
225
+ fields.push(fieldSelection);
226
+ }
227
+ }
228
+ return {
229
+ type: "Select",
230
+ fields
231
+ };
232
+ }
233
+ /**
234
+ * Parse INCLUDE clause into AST
235
+ */
236
+ #parseInclude(include) {
237
+ const relations = [];
238
+ for (const [relation, inclusion] of Object.entries(include)) {
239
+ if (typeof inclusion === "boolean") {
240
+ relations.push({
241
+ type: "RelationInclusion",
242
+ relation,
243
+ included: inclusion
244
+ });
245
+ } else if (typeof inclusion === "object" && inclusion !== null) {
246
+ const relationInclusion = {
247
+ type: "RelationInclusion",
248
+ relation,
249
+ included: true
250
+ };
251
+ if (inclusion.include) {
252
+ relationInclusion.nested = this.#parseInclude(inclusion.include);
253
+ }
254
+ if (inclusion.select || inclusion.where || inclusion.orderBy || inclusion.take || inclusion.skip) {
255
+ relationInclusion.args = this.#parseQueryArgs(inclusion);
256
+ }
257
+ relations.push(relationInclusion);
258
+ }
259
+ }
260
+ return {
261
+ type: "Include",
262
+ relations
263
+ };
264
+ }
265
+ /**
266
+ * Parse ORDER BY clause
267
+ */
268
+ #parseOrderBy(orderBy) {
269
+ const fields = [];
270
+ const orderByArray = Array.isArray(orderBy) ? orderBy : [orderBy];
271
+ for (const orderByItem of orderByArray) {
272
+ for (const [field, direction] of Object.entries(orderByItem)) {
273
+ if (direction === "asc" || direction === "desc") {
274
+ fields.push({
275
+ type: "OrderByField",
276
+ field,
277
+ direction
278
+ });
279
+ } else {
280
+ throw new QueryParseError(
281
+ `Invalid sort direction: ${direction}. Must be 'asc' or 'desc'`
282
+ );
283
+ }
284
+ }
285
+ }
286
+ return {
287
+ type: "OrderBy",
288
+ fields
289
+ };
290
+ }
291
+ /**
292
+ * Validate AST structure
293
+ */
294
+ validateAST(ast) {
295
+ if (!ast.model || !ast.operation) {
296
+ throw new QueryParseError("Invalid AST: missing model or operation");
297
+ }
298
+ if (ast.operation === "findUnique" || ast.operation === "findUniqueOrThrow") {
299
+ if (!ast.args?.where) {
300
+ throw new QueryParseError(
301
+ "findUnique operations require a where clause"
302
+ );
303
+ }
304
+ }
305
+ }
306
+ }
307
+ const queryParser = new QueryParser();
308
+ export {
309
+ QueryParseError,
310
+ QueryParser,
311
+ queryParser
312
+ };