sequelize-api-query-parser 0.0.0-stage → 0.1.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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +234 -2
  3. package/dist/cjs/index.js +6 -0
  4. package/dist/cjs/index.js.map +1 -0
  5. package/dist/cjs/package.json +3 -0
  6. package/dist/cjs/parsers/include.js +111 -0
  7. package/dist/cjs/parsers/include.js.map +1 -0
  8. package/dist/cjs/parsers/order.js +49 -0
  9. package/dist/cjs/parsers/order.js.map +1 -0
  10. package/dist/cjs/parsers/search.js +85 -0
  11. package/dist/cjs/parsers/search.js.map +1 -0
  12. package/dist/cjs/parsers/select.js +29 -0
  13. package/dist/cjs/parsers/select.js.map +1 -0
  14. package/dist/cjs/parsers/where.js +173 -0
  15. package/dist/cjs/parsers/where.js.map +1 -0
  16. package/dist/cjs/query-parser.js +81 -0
  17. package/dist/cjs/query-parser.js.map +1 -0
  18. package/dist/cjs/types.js +3 -0
  19. package/dist/cjs/types.js.map +1 -0
  20. package/dist/cjs/utils/fields.js +76 -0
  21. package/dist/cjs/utils/fields.js.map +1 -0
  22. package/dist/cjs/utils/values.js +149 -0
  23. package/dist/cjs/utils/values.js.map +1 -0
  24. package/dist/esm/index.js +2 -0
  25. package/dist/esm/index.js.map +1 -0
  26. package/dist/esm/parsers/include.js +107 -0
  27. package/dist/esm/parsers/include.js.map +1 -0
  28. package/dist/esm/parsers/order.js +45 -0
  29. package/dist/esm/parsers/order.js.map +1 -0
  30. package/dist/esm/parsers/search.js +80 -0
  31. package/dist/esm/parsers/search.js.map +1 -0
  32. package/dist/esm/parsers/select.js +25 -0
  33. package/dist/esm/parsers/select.js.map +1 -0
  34. package/dist/esm/parsers/where.js +169 -0
  35. package/dist/esm/parsers/where.js.map +1 -0
  36. package/dist/esm/query-parser.js +77 -0
  37. package/dist/esm/query-parser.js.map +1 -0
  38. package/dist/esm/types.js +2 -0
  39. package/dist/esm/types.js.map +1 -0
  40. package/dist/esm/utils/fields.js +68 -0
  41. package/dist/esm/utils/fields.js.map +1 -0
  42. package/dist/esm/utils/values.js +138 -0
  43. package/dist/esm/utils/values.js.map +1 -0
  44. package/dist/types/index.d.ts +2 -0
  45. package/dist/types/parsers/include.d.ts +12 -0
  46. package/dist/types/parsers/order.d.ts +2 -0
  47. package/dist/types/parsers/search.d.ts +13 -0
  48. package/dist/types/parsers/select.d.ts +2 -0
  49. package/dist/types/parsers/where.d.ts +13 -0
  50. package/dist/types/query-parser.d.ts +7 -0
  51. package/dist/types/types.d.ts +52 -0
  52. package/dist/types/utils/fields.d.ts +12 -0
  53. package/dist/types/utils/values.d.ts +12 -0
  54. package/package.json +59 -4
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseWhere = void 0;
4
+ const sequelize_1 = require("sequelize");
5
+ const fields_js_1 = require("../utils/fields.js");
6
+ const values_js_1 = require("../utils/values.js");
7
+ const MAX_LOGICAL_DEPTH = 5;
8
+ const OPERATOR_MAP = {
9
+ eq: sequelize_1.Op.eq,
10
+ ne: sequelize_1.Op.ne,
11
+ gt: sequelize_1.Op.gt,
12
+ gte: sequelize_1.Op.gte,
13
+ lt: sequelize_1.Op.lt,
14
+ lte: sequelize_1.Op.lte,
15
+ like: sequelize_1.Op.like,
16
+ notLike: sequelize_1.Op.notLike,
17
+ iLike: sequelize_1.Op.iLike,
18
+ notILike: sequelize_1.Op.notILike,
19
+ iEq: sequelize_1.Op.iLike,
20
+ startsWith: sequelize_1.Op.startsWith,
21
+ endsWith: sequelize_1.Op.endsWith,
22
+ contains: sequelize_1.Op.contains,
23
+ regexp: sequelize_1.Op.regexp,
24
+ notRegexp: sequelize_1.Op.notRegexp,
25
+ iRegexp: sequelize_1.Op.iRegexp,
26
+ notIRegexp: sequelize_1.Op.notIRegexp,
27
+ between: sequelize_1.Op.between,
28
+ notBetween: sequelize_1.Op.notBetween,
29
+ in: sequelize_1.Op.in,
30
+ notIn: sequelize_1.Op.notIn,
31
+ };
32
+ const LIST_OPERATORS = new Set(['between', 'notBetween', 'in', 'notIn']);
33
+ const JSON_TYPES = new Set(['JSON', 'JSONB', 'JSONTYPE']);
34
+ const getOperator = (name) => Object.prototype.hasOwnProperty.call(OPERATOR_MAP, name) ? OPERATOR_MAP[name] : undefined;
35
+ const hasEntries = (value) => Reflect.ownKeys(value).length > 0;
36
+ const fieldOutputKey = (path) => path.length > 1 ? `$${path.join('.')}$` : path[0] ?? '';
37
+ const convertOperatorValue = (operator, value, typeName) => {
38
+ const parsed = LIST_OPERATORS.has(operator) ? (0, values_js_1.parseTypedList)(value, typeName) : (0, values_js_1.parseTypedValue)(value, typeName);
39
+ if (!parsed.valid) {
40
+ return parsed;
41
+ }
42
+ if ((operator === 'iLike' || operator === 'notILike') && typeof parsed.value === 'string') {
43
+ return { valid: true, value: `%${parsed.value}%` };
44
+ }
45
+ return parsed;
46
+ };
47
+ const mergeAndExpressions = (result, expressions) => {
48
+ if (expressions.length === 0) {
49
+ return;
50
+ }
51
+ const existing = result[sequelize_1.Op.and];
52
+ result[sequelize_1.Op.and] = Array.isArray(existing) ? [...existing, ...expressions] : expressions;
53
+ };
54
+ const parseJsonOperators = (reference, input) => {
55
+ const expression = `${reference}::text`;
56
+ const clauses = [];
57
+ for (const [operator, rawValue] of Object.entries(input)) {
58
+ const sequelizeOperator = getOperator(operator);
59
+ if (!sequelizeOperator || LIST_OPERATORS.has(operator) || !(0, values_js_1.isScalarValue)(rawValue)) {
60
+ continue;
61
+ }
62
+ const parsed = convertOperatorValue(operator, rawValue);
63
+ if (!parsed.valid) {
64
+ continue;
65
+ }
66
+ const value = operator === 'eq' || operator === 'ne' ? JSON.stringify(parsed.value) : parsed.value;
67
+ clauses.push((0, sequelize_1.where)((0, sequelize_1.literal)(expression), sequelizeOperator, value));
68
+ }
69
+ return clauses;
70
+ };
71
+ const parseArrayField = (outputKey, input, expressions) => {
72
+ const result = {};
73
+ const conditions = {};
74
+ for (const [operator, rawValue] of Object.entries(input)) {
75
+ if (!['contains', 'eq', 'ne'].includes(operator)) {
76
+ continue;
77
+ }
78
+ const parsed = Array.isArray(rawValue) ? (0, values_js_1.parseTypedList)(rawValue) : (0, values_js_1.parseTypedValue)(rawValue);
79
+ if (!parsed.valid) {
80
+ continue;
81
+ }
82
+ const values = Array.isArray(parsed.value) ? parsed.value : [parsed.value];
83
+ if (operator === 'ne') {
84
+ expressions.push({ [sequelize_1.Op.not]: { [outputKey]: { [sequelize_1.Op.contains]: values } } });
85
+ }
86
+ else {
87
+ conditions[sequelize_1.Op.contains] = values;
88
+ }
89
+ }
90
+ if (Reflect.ownKeys(conditions).length > 0) {
91
+ result[outputKey] = conditions;
92
+ }
93
+ return result;
94
+ };
95
+ const parseRegularOperators = (input, typeName) => {
96
+ const result = {};
97
+ for (const [operator, rawValue] of Object.entries(input)) {
98
+ const sequelizeOperator = getOperator(operator);
99
+ if (!sequelizeOperator) {
100
+ continue;
101
+ }
102
+ const parsed = convertOperatorValue(operator, rawValue, typeName);
103
+ if (parsed.valid) {
104
+ result[sequelizeOperator] = parsed.value;
105
+ }
106
+ }
107
+ return result;
108
+ };
109
+ const parseObject = (input, context, depth) => {
110
+ if (!(0, values_js_1.isRecord)(input) || depth > MAX_LOGICAL_DEPTH) {
111
+ return {};
112
+ }
113
+ const result = {};
114
+ const expressions = [];
115
+ for (const key of Object.keys(input)) {
116
+ const rawValue = input[key];
117
+ if (key === 'and' || key === 'or' || key === 'not') {
118
+ const rawBranches = Array.isArray(rawValue) ? rawValue : [rawValue];
119
+ const branches = rawBranches
120
+ .map((branch) => parseObject(branch, context, depth + 1))
121
+ .filter(hasEntries);
122
+ if (branches.length === 0) {
123
+ continue;
124
+ }
125
+ if (key === 'not') {
126
+ result[sequelize_1.Op.not] = branches.length === 1 ? branches[0] : { [sequelize_1.Op.or]: branches };
127
+ }
128
+ else {
129
+ result[key === 'and' ? sequelize_1.Op.and : sequelize_1.Op.or] = branches;
130
+ }
131
+ continue;
132
+ }
133
+ const resolved = (0, fields_js_1.resolveField)(context.model, key);
134
+ if (!resolved) {
135
+ continue;
136
+ }
137
+ if (Object.prototype.hasOwnProperty.call(context.columns, key)) {
138
+ context.fields.add(key);
139
+ }
140
+ const outputKey = fieldOutputKey(resolved.path);
141
+ if ((0, values_js_1.isRecord)(rawValue)) {
142
+ if (JSON_TYPES.has(resolved.typeName ?? '')) {
143
+ const reference = (0, fields_js_1.getSqlFieldReference)(context.model, resolved.path, context.associationPath);
144
+ if (reference) {
145
+ expressions.push(...parseJsonOperators(reference, rawValue));
146
+ }
147
+ }
148
+ else if (resolved.typeName === 'ARRAY') {
149
+ Object.assign(result, parseArrayField(outputKey, rawValue, expressions));
150
+ }
151
+ else {
152
+ const operators = parseRegularOperators(rawValue, resolved.typeName);
153
+ if (Reflect.ownKeys(operators).length > 0) {
154
+ result[outputKey] = operators;
155
+ }
156
+ }
157
+ continue;
158
+ }
159
+ const parsed = (0, values_js_1.parseTypedValue)(rawValue, resolved.typeName);
160
+ if (parsed.valid) {
161
+ result[outputKey] = parsed.value;
162
+ }
163
+ }
164
+ mergeAndExpressions(result, expressions);
165
+ return result;
166
+ };
167
+ const parseWhere = ({ input, model, columns = {}, associationPath = [] }) => {
168
+ const fields = new Set();
169
+ const where = parseObject(input, { model, columns, fields, associationPath }, 0);
170
+ return { where: where, whereFields: [...fields] };
171
+ };
172
+ exports.parseWhere = parseWhere;
173
+ //# sourceMappingURL=where.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"where.js","sourceRoot":"","sources":["../../../src/parsers/where.ts"],"names":[],"mappings":"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","sourcesContent":["import { Op, literal, where as sequelizeWhere } from 'sequelize';\nimport type { Model, ModelStatic, WhereOptions } from 'sequelize';\n\nimport type { ColumnDetail } from '../types.js';\nimport { getSqlFieldReference, resolveField } from '../utils/fields.js';\nimport { isRecord, isScalarValue, parseTypedList, parseTypedValue } from '../utils/values.js';\n\nconst MAX_LOGICAL_DEPTH = 5;\n\nconst OPERATOR_MAP: Readonly<Record<string, symbol>> = {\n eq: Op.eq,\n ne: Op.ne,\n gt: Op.gt,\n gte: Op.gte,\n lt: Op.lt,\n lte: Op.lte,\n like: Op.like,\n notLike: Op.notLike,\n iLike: Op.iLike,\n notILike: Op.notILike,\n iEq: Op.iLike,\n startsWith: Op.startsWith,\n endsWith: Op.endsWith,\n contains: Op.contains,\n regexp: Op.regexp,\n notRegexp: Op.notRegexp,\n iRegexp: Op.iRegexp,\n notIRegexp: Op.notIRegexp,\n between: Op.between,\n notBetween: Op.notBetween,\n in: Op.in,\n notIn: Op.notIn,\n};\n\nconst LIST_OPERATORS = new Set(['between', 'notBetween', 'in', 'notIn']);\nconst JSON_TYPES = new Set(['JSON', 'JSONB', 'JSONTYPE']);\n\nconst getOperator = (name: string): symbol | undefined =>\n Object.prototype.hasOwnProperty.call(OPERATOR_MAP, name) ? OPERATOR_MAP[name] : undefined;\n\ninterface ParseWhereOptions {\n readonly input: unknown;\n readonly model: ModelStatic<Model>;\n readonly columns?: Readonly<Partial<Record<string, ColumnDetail>>>;\n readonly associationPath?: readonly string[];\n}\n\ninterface ParserContext {\n readonly model: ModelStatic<Model>;\n readonly columns: Readonly<Partial<Record<string, ColumnDetail>>>;\n readonly fields: Set<string>;\n readonly associationPath: readonly string[];\n}\n\nconst hasEntries = (value: Record<PropertyKey, unknown>): boolean => Reflect.ownKeys(value).length > 0;\n\nconst fieldOutputKey = (path: readonly string[]): string =>\n path.length > 1 ? `$${path.join('.')}$` : path[0] ?? '';\n\nconst convertOperatorValue = (operator: string, value: unknown, typeName?: string) => {\n const parsed = LIST_OPERATORS.has(operator) ? parseTypedList(value, typeName) : parseTypedValue(value, typeName);\n if (!parsed.valid) {\n return parsed;\n }\n\n if ((operator === 'iLike' || operator === 'notILike') && typeof parsed.value === 'string') {\n return { valid: true, value: `%${parsed.value}%` };\n }\n\n return parsed;\n};\n\nconst mergeAndExpressions = (result: Record<PropertyKey, unknown>, expressions: unknown[]) => {\n if (expressions.length === 0) {\n return;\n }\n\n const existing = result[Op.and];\n result[Op.and] = Array.isArray(existing) ? [...existing, ...expressions] : expressions;\n};\n\nconst parseJsonOperators = (\n reference: string,\n input: Record<string, unknown>,\n): unknown[] => {\n const expression = `${reference}::text`;\n const clauses: unknown[] = [];\n for (const [operator, rawValue] of Object.entries(input)) {\n const sequelizeOperator = getOperator(operator);\n if (!sequelizeOperator || LIST_OPERATORS.has(operator) || !isScalarValue(rawValue)) {\n continue;\n }\n\n const parsed = convertOperatorValue(operator, rawValue);\n if (!parsed.valid) {\n continue;\n }\n\n const value = operator === 'eq' || operator === 'ne' ? JSON.stringify(parsed.value) : parsed.value;\n clauses.push(sequelizeWhere(literal(expression), sequelizeOperator as any, value));\n }\n\n return clauses;\n};\n\nconst parseArrayField = (\n outputKey: string,\n input: Record<string, unknown>,\n expressions: unknown[],\n): Record<PropertyKey, unknown> => {\n const result: Record<PropertyKey, unknown> = {};\n const conditions: Record<symbol, unknown> = {};\n\n for (const [operator, rawValue] of Object.entries(input)) {\n if (!['contains', 'eq', 'ne'].includes(operator)) {\n continue;\n }\n\n const parsed = Array.isArray(rawValue) ? parseTypedList(rawValue) : parseTypedValue(rawValue);\n if (!parsed.valid) {\n continue;\n }\n const values = Array.isArray(parsed.value) ? parsed.value : [parsed.value];\n\n if (operator === 'ne') {\n expressions.push({ [Op.not]: { [outputKey]: { [Op.contains]: values } } });\n } else {\n conditions[Op.contains] = values;\n }\n }\n\n if (Reflect.ownKeys(conditions).length > 0) {\n result[outputKey] = conditions;\n }\n return result;\n};\n\nconst parseRegularOperators = (\n input: Record<string, unknown>,\n typeName?: string,\n): Record<symbol, unknown> => {\n const result: Record<symbol, unknown> = {};\n for (const [operator, rawValue] of Object.entries(input)) {\n const sequelizeOperator = getOperator(operator);\n if (!sequelizeOperator) {\n continue;\n }\n\n const parsed = convertOperatorValue(operator, rawValue, typeName);\n if (parsed.valid) {\n result[sequelizeOperator] = parsed.value;\n }\n }\n return result;\n};\n\nconst parseObject = (\n input: unknown,\n context: ParserContext,\n depth: number,\n): Record<PropertyKey, unknown> => {\n if (!isRecord(input) || depth > MAX_LOGICAL_DEPTH) {\n return {};\n }\n\n const result: Record<PropertyKey, unknown> = {};\n const expressions: unknown[] = [];\n\n for (const key of Object.keys(input)) {\n const rawValue = input[key];\n if (key === 'and' || key === 'or' || key === 'not') {\n const rawBranches = Array.isArray(rawValue) ? rawValue : [rawValue];\n const branches = rawBranches\n .map((branch) => parseObject(branch, context, depth + 1))\n .filter(hasEntries);\n\n if (branches.length === 0) {\n continue;\n }\n\n if (key === 'not') {\n result[Op.not] = branches.length === 1 ? branches[0] : { [Op.or]: branches };\n } else {\n result[key === 'and' ? Op.and : Op.or] = branches;\n }\n continue;\n }\n\n const resolved = resolveField(context.model, key);\n if (!resolved) {\n continue;\n }\n\n if (Object.prototype.hasOwnProperty.call(context.columns, key)) {\n context.fields.add(key);\n }\n\n const outputKey = fieldOutputKey(resolved.path);\n if (isRecord(rawValue)) {\n if (JSON_TYPES.has(resolved.typeName ?? '')) {\n const reference = getSqlFieldReference(context.model, resolved.path, context.associationPath);\n if (reference) {\n expressions.push(...parseJsonOperators(reference, rawValue));\n }\n } else if (resolved.typeName === 'ARRAY') {\n Object.assign(result, parseArrayField(outputKey, rawValue, expressions));\n } else {\n const operators = parseRegularOperators(rawValue, resolved.typeName);\n if (Reflect.ownKeys(operators).length > 0) {\n result[outputKey] = operators;\n }\n }\n continue;\n }\n\n const parsed = parseTypedValue(rawValue, resolved.typeName);\n if (parsed.valid) {\n result[outputKey] = parsed.value;\n }\n }\n\n mergeAndExpressions(result, expressions);\n return result;\n};\n\nexport const parseWhere = ({ input, model, columns = {}, associationPath = [] }: ParseWhereOptions) => {\n const fields = new Set<string>();\n const where = parseObject(input, { model, columns, fields, associationPath }, 0);\n return { where: where as WhereOptions, whereFields: [...fields] };\n};\n"]}
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QueryParser = void 0;
4
+ const include_js_1 = require("./parsers/include.js");
5
+ const order_js_1 = require("./parsers/order.js");
6
+ const search_js_1 = require("./parsers/search.js");
7
+ const select_js_1 = require("./parsers/select.js");
8
+ const where_js_1 = require("./parsers/where.js");
9
+ const values_js_1 = require("./utils/values.js");
10
+ const DEFAULT_LIMIT = 10;
11
+ const DEFAULT_MAX_LIMIT = 100;
12
+ const isSequelizeModel = (value) => {
13
+ if ((typeof value !== 'object' || value === null) && typeof value !== 'function') {
14
+ return false;
15
+ }
16
+ return typeof value.getAttributes === 'function';
17
+ };
18
+ class QueryParser {
19
+ #options;
20
+ #maxLimit;
21
+ constructor(options) {
22
+ if (!options || !(0, values_js_1.isRecord)(options.request)) {
23
+ throw new TypeError('QueryParser requires a request object');
24
+ }
25
+ if (!isSequelizeModel(options.model)) {
26
+ throw new TypeError('QueryParser requires a Sequelize model');
27
+ }
28
+ this.#options = options;
29
+ this.#maxLimit = (0, values_js_1.parsePositiveInteger)(options.maxLimit, DEFAULT_MAX_LIMIT, Number.MAX_SAFE_INTEGER);
30
+ }
31
+ getFullQuery(target = {}) {
32
+ const query = this.#getQuery();
33
+ const limit = (0, values_js_1.parsePositiveInteger)(query.limit, DEFAULT_LIMIT, this.#maxLimit);
34
+ const page = (0, values_js_1.parsePositiveInteger)(query.page, 1, Number.MAX_SAFE_INTEGER);
35
+ const offset = Math.min(limit * (page - 1), Number.MAX_SAFE_INTEGER);
36
+ const parsedWhere = (0, where_js_1.parseWhere)({
37
+ input: query.q,
38
+ model: this.#options.model,
39
+ columns: this.#options.columns,
40
+ });
41
+ const searchWhere = (0, search_js_1.parseSearch)({
42
+ searchText: query.searchText,
43
+ searchFields: query.searchFields,
44
+ checkYesNo: query.checkYesNo,
45
+ timezone: this.#getHeader('client-tz'),
46
+ model: this.#options.model,
47
+ columns: this.#options.columns,
48
+ });
49
+ return {
50
+ ...target,
51
+ where: (0, search_js_1.mergeSearchWhere)(parsedWhere.where, searchWhere),
52
+ order: (0, order_js_1.parseOrder)(this.#options.model, query.sort),
53
+ limit,
54
+ offset,
55
+ include: (0, include_js_1.parseIncludes)({
56
+ input: query.include,
57
+ model: this.#options.model,
58
+ isGroupBy: this.#options.isGroupBy,
59
+ maxLimit: this.#maxLimit,
60
+ }),
61
+ attributes: (0, select_js_1.parseSelection)(this.#options.model, query.select),
62
+ subQuery: (0, values_js_1.parseBoolean)(query.subQuery),
63
+ paranoid: (0, values_js_1.parseBoolean)(query.paranoid),
64
+ whereFields: parsedWhere.whereFields,
65
+ };
66
+ }
67
+ #getQuery() {
68
+ return (0, values_js_1.isRecord)(this.#options.request.query) ? this.#options.request.query : {};
69
+ }
70
+ #getHeader(name) {
71
+ const headers = this.#options.request.headers;
72
+ if (!headers) {
73
+ return undefined;
74
+ }
75
+ const entry = Object.entries(headers).find(([key]) => key.toLowerCase() === name);
76
+ const value = entry?.[1];
77
+ return typeof value === 'string' ? value : value?.[0];
78
+ }
79
+ }
80
+ exports.QueryParser = QueryParser;
81
+ //# sourceMappingURL=query-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-parser.js","sourceRoot":"","sources":["../../src/query-parser.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Model } from 'sequelize';\n\nimport { parseIncludes } from './parsers/include.js';\nimport { parseOrder } from './parsers/order.js';\nimport { mergeSearchWhere, parseSearch } from './parsers/search.js';\nimport { parseSelection } from './parsers/select.js';\nimport { parseWhere } from './parsers/where.js';\nimport type { ParsedQuery, QueryParserOptions, QueryParserQuery } from './types.js';\nimport { isRecord, parseBoolean, parsePositiveInteger } from './utils/values.js';\n\nconst DEFAULT_LIMIT = 10;\nconst DEFAULT_MAX_LIMIT = 100;\n\nconst isSequelizeModel = (value: unknown): boolean => {\n if ((typeof value !== 'object' || value === null) && typeof value !== 'function') {\n return false;\n }\n\n return typeof (value as { getAttributes?: unknown }).getAttributes === 'function';\n};\n\nexport class QueryParser<M extends Model> {\n readonly #options: QueryParserOptions<M>;\n readonly #maxLimit: number;\n\n constructor(options: QueryParserOptions<M>) {\n if (!options || !isRecord(options.request)) {\n throw new TypeError('QueryParser requires a request object');\n }\n\n if (!isSequelizeModel(options.model)) {\n throw new TypeError('QueryParser requires a Sequelize model');\n }\n\n this.#options = options;\n this.#maxLimit = parsePositiveInteger(options.maxLimit, DEFAULT_MAX_LIMIT, Number.MAX_SAFE_INTEGER);\n }\n\n getFullQuery(target: Record<string, unknown> = {}): ParsedQuery {\n const query = this.#getQuery();\n const limit = parsePositiveInteger(query.limit, DEFAULT_LIMIT, this.#maxLimit);\n const page = parsePositiveInteger(query.page, 1, Number.MAX_SAFE_INTEGER);\n const offset = Math.min(limit * (page - 1), Number.MAX_SAFE_INTEGER);\n const parsedWhere = parseWhere({\n input: query.q,\n model: this.#options.model,\n columns: this.#options.columns,\n });\n const searchWhere = parseSearch({\n searchText: query.searchText,\n searchFields: query.searchFields,\n checkYesNo: query.checkYesNo,\n timezone: this.#getHeader('client-tz'),\n model: this.#options.model,\n columns: this.#options.columns,\n });\n\n return {\n ...target,\n where: mergeSearchWhere(parsedWhere.where, searchWhere),\n order: parseOrder(this.#options.model, query.sort),\n limit,\n offset,\n include: parseIncludes({\n input: query.include,\n model: this.#options.model,\n isGroupBy: this.#options.isGroupBy,\n maxLimit: this.#maxLimit,\n }),\n attributes: parseSelection(this.#options.model, query.select),\n subQuery: parseBoolean(query.subQuery),\n paranoid: parseBoolean(query.paranoid),\n whereFields: parsedWhere.whereFields,\n };\n }\n\n #getQuery(): QueryParserQuery {\n return isRecord(this.#options.request.query) ? this.#options.request.query : {};\n }\n\n #getHeader(name: string): string | undefined {\n const headers = this.#options.request.headers;\n if (!headers) {\n return undefined;\n }\n\n const entry = Object.entries(headers).find(([key]) => key.toLowerCase() === name);\n const value = entry?.[1];\n return typeof value === 'string' ? value : value?.[0];\n }\n}\n"]}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Includeable, Model, ModelStatic, Order, WhereOptions } from 'sequelize';\n\nexport type QueryParserQuery = Record<string, unknown> & {\n checkYesNo?: unknown;\n include?: unknown;\n limit?: unknown;\n page?: unknown;\n paranoid?: unknown;\n q?: unknown;\n searchFields?: unknown;\n searchText?: unknown;\n select?: unknown;\n sort?: unknown;\n subQuery?: unknown;\n};\n\nexport interface QueryParserRequest {\n readonly query?: QueryParserQuery;\n readonly headers?: Readonly<Record<string, string | readonly string[] | undefined>>;\n}\n\nexport interface ColumnDetail {\n readonly displayName?: string;\n readonly fieldName: string;\n readonly type: string;\n readonly show?: boolean;\n readonly custom?: boolean;\n readonly default?: boolean;\n readonly width?: number;\n readonly minWidth?: number;\n readonly isPinned?: boolean;\n readonly include?: Readonly<Record<string, unknown>>;\n readonly searchKeys?: readonly string[];\n readonly tableName?: string;\n readonly foreignKey?: string;\n readonly fieldType?: 'column' | 'association' | 'filter';\n}\n\nexport interface QueryParserOptions<M extends Model = Model> {\n readonly request: QueryParserRequest;\n readonly model: ModelStatic<M>;\n readonly columns?: Readonly<Partial<Record<string, ColumnDetail>>>;\n readonly isGroupBy?: boolean;\n readonly maxLimit?: number;\n}\n\nexport type ParsedQuery = Record<string, unknown> & {\n readonly where: WhereOptions;\n readonly order: Order;\n readonly limit: number;\n readonly offset: number;\n readonly include: Includeable[];\n readonly attributes: string[];\n readonly subQuery: boolean | undefined;\n readonly paranoid: boolean | undefined;\n readonly whereFields: string[];\n};\n"]}
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveField = exports.getSqlFieldReference = exports.getAttributeNames = exports.getModelAttributes = exports.isSafeIdentifier = void 0;
4
+ const IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
5
+ const BLOCKED_KEYS = new Set(['__proto__', 'prototype', 'constructor']);
6
+ const isSafeIdentifier = (value) => IDENTIFIER_PATTERN.test(value) && !BLOCKED_KEYS.has(value);
7
+ exports.isSafeIdentifier = isSafeIdentifier;
8
+ const getModelAttributes = (model) => {
9
+ try {
10
+ const attributes = model.getAttributes();
11
+ return attributes && typeof attributes === 'object' ? attributes : {};
12
+ }
13
+ catch {
14
+ return {};
15
+ }
16
+ };
17
+ exports.getModelAttributes = getModelAttributes;
18
+ const getAttributeNames = (model) => Object.keys((0, exports.getModelAttributes)(model)).filter(exports.isSafeIdentifier);
19
+ exports.getAttributeNames = getAttributeNames;
20
+ const getSqlFieldReference = (rootModel, path, baseAssociationPath = []) => {
21
+ if (path.length === 0 ||
22
+ path.some((segment) => !(0, exports.isSafeIdentifier)(segment)) ||
23
+ baseAssociationPath.some((segment) => !(0, exports.isSafeIdentifier)(segment))) {
24
+ return null;
25
+ }
26
+ const attribute = path.at(-1);
27
+ if (!attribute) {
28
+ return null;
29
+ }
30
+ const associationPath = [...baseAssociationPath, ...path.slice(0, -1)];
31
+ if (associationPath.length === 0) {
32
+ return (0, exports.isSafeIdentifier)(rootModel.name) ? `"${rootModel.name}"."${attribute}"` : null;
33
+ }
34
+ const associationAlias = associationPath.join('->');
35
+ return `"${associationAlias}"."${attribute}"`;
36
+ };
37
+ exports.getSqlFieldReference = getSqlFieldReference;
38
+ const normalizeFieldPath = (path) => {
39
+ const trimmed = path.trim();
40
+ return trimmed.startsWith('$') && trimmed.endsWith('$') ? trimmed.slice(1, -1) : trimmed;
41
+ };
42
+ const resolveField = (model, path) => {
43
+ const segments = normalizeFieldPath(path).split('.');
44
+ if (segments.length === 0 || segments.some((segment) => !(0, exports.isSafeIdentifier)(segment))) {
45
+ return null;
46
+ }
47
+ let currentModel = model;
48
+ for (const associationName of segments.slice(0, -1)) {
49
+ const associations = currentModel.associations;
50
+ if (!associations || !Object.prototype.hasOwnProperty.call(associations, associationName)) {
51
+ return null;
52
+ }
53
+ const target = associations[associationName]?.target;
54
+ if (!target || typeof target.getAttributes !== 'function') {
55
+ return null;
56
+ }
57
+ currentModel = target;
58
+ }
59
+ const attribute = segments.at(-1);
60
+ if (!attribute) {
61
+ return null;
62
+ }
63
+ const attributes = (0, exports.getModelAttributes)(currentModel);
64
+ if (!Object.prototype.hasOwnProperty.call(attributes, attribute)) {
65
+ return null;
66
+ }
67
+ const typeName = attributes[attribute]?.type?.constructor?.name;
68
+ return {
69
+ path: segments,
70
+ model: currentModel,
71
+ attribute,
72
+ typeName: typeof typeName === 'string' ? typeName : undefined,
73
+ };
74
+ };
75
+ exports.resolveField = resolveField;
76
+ //# sourceMappingURL=fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fields.js","sourceRoot":"","sources":["../../../src/utils/fields.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Model, ModelStatic } from 'sequelize';\n\nconst IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;\nconst BLOCKED_KEYS = new Set(['__proto__', 'prototype', 'constructor']);\n\nexport interface ResolvedField {\n readonly path: string[];\n readonly model: ModelStatic<Model>;\n readonly attribute: string;\n readonly typeName: string | undefined;\n}\n\nexport const isSafeIdentifier = (value: string): boolean =>\n IDENTIFIER_PATTERN.test(value) && !BLOCKED_KEYS.has(value);\n\nexport const getModelAttributes = (model: ModelStatic<Model>): Record<string, any> => {\n try {\n const attributes = model.getAttributes();\n return attributes && typeof attributes === 'object' ? (attributes as Record<string, any>) : {};\n } catch {\n return {};\n }\n};\n\nexport const getAttributeNames = (model: ModelStatic<Model>): string[] =>\n Object.keys(getModelAttributes(model)).filter(isSafeIdentifier);\n\nexport const getSqlFieldReference = (\n rootModel: ModelStatic<Model>,\n path: readonly string[],\n baseAssociationPath: readonly string[] = [],\n): string | null => {\n if (\n path.length === 0 ||\n path.some((segment) => !isSafeIdentifier(segment)) ||\n baseAssociationPath.some((segment) => !isSafeIdentifier(segment))\n ) {\n return null;\n }\n\n const attribute = path.at(-1);\n if (!attribute) {\n return null;\n }\n\n const associationPath = [...baseAssociationPath, ...path.slice(0, -1)];\n if (associationPath.length === 0) {\n return isSafeIdentifier(rootModel.name) ? `\"${rootModel.name}\".\"${attribute}\"` : null;\n }\n\n const associationAlias = associationPath.join('->');\n return `\"${associationAlias}\".\"${attribute}\"`;\n};\n\nconst normalizeFieldPath = (path: string): string => {\n const trimmed = path.trim();\n return trimmed.startsWith('$') && trimmed.endsWith('$') ? trimmed.slice(1, -1) : trimmed;\n};\n\nexport const resolveField = (model: ModelStatic<Model>, path: string): ResolvedField | null => {\n const segments = normalizeFieldPath(path).split('.');\n if (segments.length === 0 || segments.some((segment) => !isSafeIdentifier(segment))) {\n return null;\n }\n\n let currentModel = model;\n for (const associationName of segments.slice(0, -1)) {\n const associations = currentModel.associations as Record<string, { target?: ModelStatic<Model> }> | undefined;\n if (!associations || !Object.prototype.hasOwnProperty.call(associations, associationName)) {\n return null;\n }\n\n const target = associations[associationName]?.target;\n if (!target || typeof target.getAttributes !== 'function') {\n return null;\n }\n currentModel = target;\n }\n\n const attribute = segments.at(-1);\n if (!attribute) {\n return null;\n }\n\n const attributes = getModelAttributes(currentModel);\n if (!Object.prototype.hasOwnProperty.call(attributes, attribute)) {\n return null;\n }\n\n const typeName = attributes[attribute]?.type?.constructor?.name;\n return {\n path: segments,\n model: currentModel,\n attribute,\n typeName: typeof typeName === 'string' ? typeName : undefined,\n };\n};\n"]}
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isValidTimeZone = exports.isValidDateText = exports.parseTypedList = exports.parseTypedValue = exports.isScalarValue = exports.parsePositiveInteger = exports.parseBoolean = exports.isRecord = void 0;
4
+ const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date);
5
+ exports.isRecord = isRecord;
6
+ const parseBoolean = (value, fallback) => {
7
+ if (typeof value === 'boolean') {
8
+ return value;
9
+ }
10
+ if (typeof value === 'string') {
11
+ const normalized = value.trim().toLowerCase();
12
+ if (normalized === 'true') {
13
+ return true;
14
+ }
15
+ if (normalized === 'false') {
16
+ return false;
17
+ }
18
+ }
19
+ return fallback;
20
+ };
21
+ exports.parseBoolean = parseBoolean;
22
+ const parsePositiveInteger = (value, fallback, maximum) => {
23
+ const parsed = typeof value === 'number' ? value : typeof value === 'string' ? Number(value.trim()) : Number.NaN;
24
+ if (!Number.isFinite(parsed) || parsed <= 0) {
25
+ return fallback;
26
+ }
27
+ return Math.min(Math.floor(parsed), maximum);
28
+ };
29
+ exports.parsePositiveInteger = parsePositiveInteger;
30
+ const isScalarValue = (value) => value === null || ['string', 'number', 'boolean'].includes(typeof value) || value instanceof Date;
31
+ exports.isScalarValue = isScalarValue;
32
+ const BIGINT_PATTERN = /^[+-]?\d+$/;
33
+ const DECIMAL_PATTERN = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
34
+ const fromTaggedString = (tag, value) => {
35
+ switch (tag) {
36
+ case 'n':
37
+ case 'f': {
38
+ if (!value.trim()) {
39
+ return { valid: false };
40
+ }
41
+ const parsed = Number(value);
42
+ return Number.isFinite(parsed) ? { valid: true, value: parsed } : { valid: false };
43
+ }
44
+ case 'b':
45
+ return value === 'true' || value === 'false' ? { valid: true, value: value === 'true' } : { valid: false };
46
+ case 'd': {
47
+ const parsed = new Date(value);
48
+ return Number.isNaN(parsed.getTime()) ? { valid: false } : { valid: true, value: parsed };
49
+ }
50
+ case 's':
51
+ return { valid: true, value };
52
+ default:
53
+ return { valid: false };
54
+ }
55
+ };
56
+ const parseTypedValue = (value, typeName) => {
57
+ if (!(0, exports.isScalarValue)(value)) {
58
+ return { valid: false };
59
+ }
60
+ if (value instanceof Date) {
61
+ return Number.isNaN(value.getTime()) ? { valid: false } : { valid: true, value };
62
+ }
63
+ if (value === null || typeof value !== 'string') {
64
+ return { valid: true, value };
65
+ }
66
+ if (value === '') {
67
+ return { valid: true, value: null };
68
+ }
69
+ const tagged = /^([nfbsd])\|(.*)$/s.exec(value);
70
+ if (tagged) {
71
+ return fromTaggedString(tagged[1] ?? '', tagged[2] ?? '');
72
+ }
73
+ switch (typeName) {
74
+ case 'BIGINT': {
75
+ const normalized = value.trim();
76
+ return BIGINT_PATTERN.test(normalized)
77
+ ? { valid: true, value: normalized }
78
+ : { valid: false };
79
+ }
80
+ case 'INTEGER': {
81
+ const parsed = Number(value);
82
+ return Number.isInteger(parsed) ? { valid: true, value: parsed } : { valid: false };
83
+ }
84
+ case 'DECIMAL': {
85
+ const normalized = value.trim();
86
+ return DECIMAL_PATTERN.test(normalized)
87
+ ? { valid: true, value: normalized }
88
+ : { valid: false };
89
+ }
90
+ case 'FLOAT':
91
+ case 'DOUBLE':
92
+ case 'REAL': {
93
+ const parsed = Number(value);
94
+ return Number.isFinite(parsed) ? { valid: true, value: parsed } : { valid: false };
95
+ }
96
+ case 'BOOLEAN':
97
+ return value === 'true' || value === 'false' ? { valid: true, value: value === 'true' } : { valid: false };
98
+ case 'DATE':
99
+ case 'DATEONLY': {
100
+ const parsed = new Date(value);
101
+ return Number.isNaN(parsed.getTime()) ? { valid: false } : { valid: true, value: parsed };
102
+ }
103
+ default:
104
+ return { valid: true, value };
105
+ }
106
+ };
107
+ exports.parseTypedValue = parseTypedValue;
108
+ const parseTypedList = (value, typeName) => {
109
+ let values;
110
+ if (Array.isArray(value)) {
111
+ values = value;
112
+ }
113
+ else if (typeof value === 'string') {
114
+ const tagged = /^([nfbsd])\|(.*)$/s.exec(value);
115
+ values = tagged
116
+ ? (tagged[2] ?? '').split(',').map((item) => `${tagged[1]}|${item}`)
117
+ : value.split(',');
118
+ }
119
+ else {
120
+ return { valid: false };
121
+ }
122
+ const parsed = values.map((item) => (0, exports.parseTypedValue)(item, typeName));
123
+ return parsed.every((item) => item.valid)
124
+ ? { valid: true, value: parsed.map((item) => item.value) }
125
+ : { valid: false };
126
+ };
127
+ exports.parseTypedList = parseTypedList;
128
+ const isValidDateText = (value) => {
129
+ const normalized = value.trim();
130
+ if (!normalized) {
131
+ return false;
132
+ }
133
+ return !Number.isNaN(new Date(normalized).getTime());
134
+ };
135
+ exports.isValidDateText = isValidDateText;
136
+ const isValidTimeZone = (value) => {
137
+ if (typeof value !== 'string' || value.length === 0 || value.length > 100) {
138
+ return false;
139
+ }
140
+ try {
141
+ new Intl.DateTimeFormat('en-US', { timeZone: value }).format();
142
+ return true;
143
+ }
144
+ catch {
145
+ return false;
146
+ }
147
+ };
148
+ exports.isValidTimeZone = isValidTimeZone;
149
+ //# sourceMappingURL=values.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"values.js","sourceRoot":"","sources":["../../../src/utils/values.ts"],"names":[],"mappings":";;;AAAO,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAoC,EAAE,CAC3E,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,YAAY,IAAI,CAAC,CAAC;AADtF,QAAA,QAAQ,YAC8E;AAE5F,MAAM,YAAY,GAAG,CAAC,KAAc,EAAE,QAAkB,EAAuB,EAAE;IACtF,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC9C,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,UAAU,KAAK,OAAO,EAAE,CAAC;YAC3B,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAhBW,QAAA,YAAY,gBAgBvB;AAEK,MAAM,oBAAoB,GAAG,CAAC,KAAc,EAAE,QAAgB,EAAE,OAAe,EAAU,EAAE;IAChG,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;IAEjH,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QAC5C,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC,CAAC;AARW,QAAA,oBAAoB,wBAQ/B;AAOK,MAAM,aAAa,GAAG,CAAC,KAAc,EAAoD,EAAE,CAChG,KAAK,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,OAAO,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC;AADvF,QAAA,aAAa,iBAC0E;AAEpG,MAAM,cAAc,GAAG,YAAY,CAAC;AACpC,MAAM,eAAe,GAAG,+CAA+C,CAAC;AAExE,MAAM,gBAAgB,GAAG,CAAC,GAAW,EAAE,KAAa,EAAe,EAAE;IACnE,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,GAAG,CAAC;QACT,KAAK,GAAG,CAAC,CAAC,CAAC;YACT,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;gBAClB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC1B,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC7B,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QACrF,CAAC;QACD,KAAK,GAAG;YACN,OAAO,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAC7G,KAAK,GAAG,CAAC,CAAC,CAAC;YACT,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;YAC/B,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAC5F,CAAC;QACD,KAAK,GAAG;YACN,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QAChC;YACE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAC5B,CAAC;AACH,CAAC,CAAC;AAEK,MAAM,eAAe,GAAG,CAAC,KAAc,EAAE,QAAiB,EAAe,EAAE;IAChF,IAAI,CAAC,IAAA,qBAAa,EAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAC1B,CAAC;IAED,IAAI,KAAK,YAAY,IAAI,EAAE,CAAC;QAC1B,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACnF,CAAC;IAED,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAChC,CAAC;IAED,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACjB,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACtC,CAAC;IAED,MAAM,MAAM,GAAG,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChD,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;YAChC,OAAO,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC;gBACpC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE;gBACpC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QACvB,CAAC;QACD,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC7B,OAAO,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QACtF,CAAC;QACD,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;YAChC,OAAO,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC;gBACrC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE;gBACpC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QACvB,CAAC;QACD,KAAK,OAAO,CAAC;QACb,KAAK,QAAQ,CAAC;QACd,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC7B,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QACrF,CAAC;QACD,KAAK,SAAS;YACZ,OAAO,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAC7G,KAAK,MAAM,CAAC;QACZ,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;YAC/B,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAC5F,CAAC;QACD;YACE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAClC,CAAC;AACH,CAAC,CAAC;AAvDW,QAAA,eAAe,mBAuD1B;AAEK,MAAM,cAAc,GAAG,CAAC,KAAc,EAAE,QAAiB,EAAe,EAAE;IAC/E,IAAI,MAAiB,CAAC;IAEtB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,GAAG,KAAK,CAAC;IACjB,CAAC;SAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChD,MAAM,GAAG,MAAM;YACb,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;YACpE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;SAAM,CAAC;QACN,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAC1B,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAA,uBAAe,EAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IACrE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;QACvC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;QAC1D,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AACvB,CAAC,CAAC;AAlBW,QAAA,cAAc,kBAkBzB;AAEK,MAAM,eAAe,GAAG,CAAC,KAAa,EAAW,EAAE;IACxD,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAChC,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;AACvD,CAAC,CAAC;AAPW,QAAA,eAAe,mBAO1B;AAEK,MAAM,eAAe,GAAG,CAAC,KAAc,EAAmB,EAAE;IACjE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QAC1E,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;QAC/D,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC;AAXW,QAAA,eAAe,mBAW1B","sourcesContent":["export const isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date);\n\nexport const parseBoolean = (value: unknown, fallback?: boolean): boolean | undefined => {\n if (typeof value === 'boolean') {\n return value;\n }\n\n if (typeof value === 'string') {\n const normalized = value.trim().toLowerCase();\n if (normalized === 'true') {\n return true;\n }\n if (normalized === 'false') {\n return false;\n }\n }\n\n return fallback;\n};\n\nexport const parsePositiveInteger = (value: unknown, fallback: number, maximum: number): number => {\n const parsed = typeof value === 'number' ? value : typeof value === 'string' ? Number(value.trim()) : Number.NaN;\n\n if (!Number.isFinite(parsed) || parsed <= 0) {\n return fallback;\n }\n\n return Math.min(Math.floor(parsed), maximum);\n};\n\nexport interface ParsedValue {\n readonly valid: boolean;\n readonly value?: unknown;\n}\n\nexport const isScalarValue = (value: unknown): value is string | number | boolean | Date | null =>\n value === null || ['string', 'number', 'boolean'].includes(typeof value) || value instanceof Date;\n\nconst BIGINT_PATTERN = /^[+-]?\\d+$/;\nconst DECIMAL_PATTERN = /^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:e[+-]?\\d+)?$/i;\n\nconst fromTaggedString = (tag: string, value: string): ParsedValue => {\n switch (tag) {\n case 'n':\n case 'f': {\n if (!value.trim()) {\n return { valid: false };\n }\n const parsed = Number(value);\n return Number.isFinite(parsed) ? { valid: true, value: parsed } : { valid: false };\n }\n case 'b':\n return value === 'true' || value === 'false' ? { valid: true, value: value === 'true' } : { valid: false };\n case 'd': {\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? { valid: false } : { valid: true, value: parsed };\n }\n case 's':\n return { valid: true, value };\n default:\n return { valid: false };\n }\n};\n\nexport const parseTypedValue = (value: unknown, typeName?: string): ParsedValue => {\n if (!isScalarValue(value)) {\n return { valid: false };\n }\n\n if (value instanceof Date) {\n return Number.isNaN(value.getTime()) ? { valid: false } : { valid: true, value };\n }\n\n if (value === null || typeof value !== 'string') {\n return { valid: true, value };\n }\n\n if (value === '') {\n return { valid: true, value: null };\n }\n\n const tagged = /^([nfbsd])\\|(.*)$/s.exec(value);\n if (tagged) {\n return fromTaggedString(tagged[1] ?? '', tagged[2] ?? '');\n }\n\n switch (typeName) {\n case 'BIGINT': {\n const normalized = value.trim();\n return BIGINT_PATTERN.test(normalized)\n ? { valid: true, value: normalized }\n : { valid: false };\n }\n case 'INTEGER': {\n const parsed = Number(value);\n return Number.isInteger(parsed) ? { valid: true, value: parsed } : { valid: false };\n }\n case 'DECIMAL': {\n const normalized = value.trim();\n return DECIMAL_PATTERN.test(normalized)\n ? { valid: true, value: normalized }\n : { valid: false };\n }\n case 'FLOAT':\n case 'DOUBLE':\n case 'REAL': {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? { valid: true, value: parsed } : { valid: false };\n }\n case 'BOOLEAN':\n return value === 'true' || value === 'false' ? { valid: true, value: value === 'true' } : { valid: false };\n case 'DATE':\n case 'DATEONLY': {\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? { valid: false } : { valid: true, value: parsed };\n }\n default:\n return { valid: true, value };\n }\n};\n\nexport const parseTypedList = (value: unknown, typeName?: string): ParsedValue => {\n let values: unknown[];\n\n if (Array.isArray(value)) {\n values = value;\n } else if (typeof value === 'string') {\n const tagged = /^([nfbsd])\\|(.*)$/s.exec(value);\n values = tagged\n ? (tagged[2] ?? '').split(',').map((item) => `${tagged[1]}|${item}`)\n : value.split(',');\n } else {\n return { valid: false };\n }\n\n const parsed = values.map((item) => parseTypedValue(item, typeName));\n return parsed.every((item) => item.valid)\n ? { valid: true, value: parsed.map((item) => item.value) }\n : { valid: false };\n};\n\nexport const isValidDateText = (value: string): boolean => {\n const normalized = value.trim();\n if (!normalized) {\n return false;\n }\n\n return !Number.isNaN(new Date(normalized).getTime());\n};\n\nexport const isValidTimeZone = (value: unknown): value is string => {\n if (typeof value !== 'string' || value.length === 0 || value.length > 100) {\n return false;\n }\n\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: value }).format();\n return true;\n } catch {\n return false;\n }\n};\n"]}
@@ -0,0 +1,2 @@
1
+ export { QueryParser } from './query-parser.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC","sourcesContent":["export { QueryParser } from './query-parser.js';\nexport type {\n ColumnDetail,\n ParsedQuery,\n QueryParserOptions,\n QueryParserQuery,\n QueryParserRequest,\n} from './types.js';\n"]}