sequelize-api-query-parser 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +234 -2
- package/dist/cjs/index.js +6 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/parsers/include.js +111 -0
- package/dist/cjs/parsers/include.js.map +1 -0
- package/dist/cjs/parsers/order.js +49 -0
- package/dist/cjs/parsers/order.js.map +1 -0
- package/dist/cjs/parsers/search.js +85 -0
- package/dist/cjs/parsers/search.js.map +1 -0
- package/dist/cjs/parsers/select.js +29 -0
- package/dist/cjs/parsers/select.js.map +1 -0
- package/dist/cjs/parsers/where.js +173 -0
- package/dist/cjs/parsers/where.js.map +1 -0
- package/dist/cjs/query-parser.js +81 -0
- package/dist/cjs/query-parser.js.map +1 -0
- package/dist/cjs/types.js +3 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/utils/fields.js +76 -0
- package/dist/cjs/utils/fields.js.map +1 -0
- package/dist/cjs/utils/values.js +149 -0
- package/dist/cjs/utils/values.js.map +1 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/parsers/include.js +107 -0
- package/dist/esm/parsers/include.js.map +1 -0
- package/dist/esm/parsers/order.js +45 -0
- package/dist/esm/parsers/order.js.map +1 -0
- package/dist/esm/parsers/search.js +80 -0
- package/dist/esm/parsers/search.js.map +1 -0
- package/dist/esm/parsers/select.js +25 -0
- package/dist/esm/parsers/select.js.map +1 -0
- package/dist/esm/parsers/where.js +169 -0
- package/dist/esm/parsers/where.js.map +1 -0
- package/dist/esm/query-parser.js +77 -0
- package/dist/esm/query-parser.js.map +1 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/utils/fields.js +68 -0
- package/dist/esm/utils/fields.js.map +1 -0
- package/dist/esm/utils/values.js +138 -0
- package/dist/esm/utils/values.js.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/parsers/include.d.ts +12 -0
- package/dist/types/parsers/order.d.ts +2 -0
- package/dist/types/parsers/search.d.ts +13 -0
- package/dist/types/parsers/select.d.ts +2 -0
- package/dist/types/parsers/where.d.ts +13 -0
- package/dist/types/query-parser.d.ts +7 -0
- package/dist/types/types.d.ts +52 -0
- package/dist/types/utils/fields.d.ts +12 -0
- package/dist/types/utils/values.d.ts +12 -0
- package/package.json +60 -4
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
2
|
+
const BLOCKED_KEYS = new Set(['__proto__', 'prototype', 'constructor']);
|
|
3
|
+
export const isSafeIdentifier = (value) => IDENTIFIER_PATTERN.test(value) && !BLOCKED_KEYS.has(value);
|
|
4
|
+
export const getModelAttributes = (model) => {
|
|
5
|
+
try {
|
|
6
|
+
const attributes = model.getAttributes();
|
|
7
|
+
return attributes && typeof attributes === 'object' ? attributes : {};
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
export const getAttributeNames = (model) => Object.keys(getModelAttributes(model)).filter(isSafeIdentifier);
|
|
14
|
+
export const getSqlFieldReference = (rootModel, path, baseAssociationPath = []) => {
|
|
15
|
+
if (path.length === 0 ||
|
|
16
|
+
path.some((segment) => !isSafeIdentifier(segment)) ||
|
|
17
|
+
baseAssociationPath.some((segment) => !isSafeIdentifier(segment))) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const attribute = path.at(-1);
|
|
21
|
+
if (!attribute) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const associationPath = [...baseAssociationPath, ...path.slice(0, -1)];
|
|
25
|
+
if (associationPath.length === 0) {
|
|
26
|
+
return isSafeIdentifier(rootModel.name) ? `"${rootModel.name}"."${attribute}"` : null;
|
|
27
|
+
}
|
|
28
|
+
const associationAlias = associationPath.join('->');
|
|
29
|
+
return `"${associationAlias}"."${attribute}"`;
|
|
30
|
+
};
|
|
31
|
+
const normalizeFieldPath = (path) => {
|
|
32
|
+
const trimmed = path.trim();
|
|
33
|
+
return trimmed.startsWith('$') && trimmed.endsWith('$') ? trimmed.slice(1, -1) : trimmed;
|
|
34
|
+
};
|
|
35
|
+
export const resolveField = (model, path) => {
|
|
36
|
+
const segments = normalizeFieldPath(path).split('.');
|
|
37
|
+
if (segments.length === 0 || segments.some((segment) => !isSafeIdentifier(segment))) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
let currentModel = model;
|
|
41
|
+
for (const associationName of segments.slice(0, -1)) {
|
|
42
|
+
const associations = currentModel.associations;
|
|
43
|
+
if (!associations || !Object.prototype.hasOwnProperty.call(associations, associationName)) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const target = associations[associationName]?.target;
|
|
47
|
+
if (!target || typeof target.getAttributes !== 'function') {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
currentModel = target;
|
|
51
|
+
}
|
|
52
|
+
const attribute = segments.at(-1);
|
|
53
|
+
if (!attribute) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const attributes = getModelAttributes(currentModel);
|
|
57
|
+
if (!Object.prototype.hasOwnProperty.call(attributes, attribute)) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const typeName = attributes[attribute]?.type?.constructor?.name;
|
|
61
|
+
return {
|
|
62
|
+
path: segments,
|
|
63
|
+
model: currentModel,
|
|
64
|
+
attribute,
|
|
65
|
+
typeName: typeof typeName === 'string' ? typeName : undefined,
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
//# 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,138 @@
|
|
|
1
|
+
export const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date);
|
|
2
|
+
export const parseBoolean = (value, fallback) => {
|
|
3
|
+
if (typeof value === 'boolean') {
|
|
4
|
+
return value;
|
|
5
|
+
}
|
|
6
|
+
if (typeof value === 'string') {
|
|
7
|
+
const normalized = value.trim().toLowerCase();
|
|
8
|
+
if (normalized === 'true') {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
if (normalized === 'false') {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return fallback;
|
|
16
|
+
};
|
|
17
|
+
export const parsePositiveInteger = (value, fallback, maximum) => {
|
|
18
|
+
const parsed = typeof value === 'number' ? value : typeof value === 'string' ? Number(value.trim()) : Number.NaN;
|
|
19
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
20
|
+
return fallback;
|
|
21
|
+
}
|
|
22
|
+
return Math.min(Math.floor(parsed), maximum);
|
|
23
|
+
};
|
|
24
|
+
export const isScalarValue = (value) => value === null || ['string', 'number', 'boolean'].includes(typeof value) || value instanceof Date;
|
|
25
|
+
const BIGINT_PATTERN = /^[+-]?\d+$/;
|
|
26
|
+
const DECIMAL_PATTERN = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
|
|
27
|
+
const fromTaggedString = (tag, value) => {
|
|
28
|
+
switch (tag) {
|
|
29
|
+
case 'n':
|
|
30
|
+
case 'f': {
|
|
31
|
+
if (!value.trim()) {
|
|
32
|
+
return { valid: false };
|
|
33
|
+
}
|
|
34
|
+
const parsed = Number(value);
|
|
35
|
+
return Number.isFinite(parsed) ? { valid: true, value: parsed } : { valid: false };
|
|
36
|
+
}
|
|
37
|
+
case 'b':
|
|
38
|
+
return value === 'true' || value === 'false' ? { valid: true, value: value === 'true' } : { valid: false };
|
|
39
|
+
case 'd': {
|
|
40
|
+
const parsed = new Date(value);
|
|
41
|
+
return Number.isNaN(parsed.getTime()) ? { valid: false } : { valid: true, value: parsed };
|
|
42
|
+
}
|
|
43
|
+
case 's':
|
|
44
|
+
return { valid: true, value };
|
|
45
|
+
default:
|
|
46
|
+
return { valid: false };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
export const parseTypedValue = (value, typeName) => {
|
|
50
|
+
if (!isScalarValue(value)) {
|
|
51
|
+
return { valid: false };
|
|
52
|
+
}
|
|
53
|
+
if (value instanceof Date) {
|
|
54
|
+
return Number.isNaN(value.getTime()) ? { valid: false } : { valid: true, value };
|
|
55
|
+
}
|
|
56
|
+
if (value === null || typeof value !== 'string') {
|
|
57
|
+
return { valid: true, value };
|
|
58
|
+
}
|
|
59
|
+
if (value === '') {
|
|
60
|
+
return { valid: true, value: null };
|
|
61
|
+
}
|
|
62
|
+
const tagged = /^([nfbsd])\|(.*)$/s.exec(value);
|
|
63
|
+
if (tagged) {
|
|
64
|
+
return fromTaggedString(tagged[1] ?? '', tagged[2] ?? '');
|
|
65
|
+
}
|
|
66
|
+
switch (typeName) {
|
|
67
|
+
case 'BIGINT': {
|
|
68
|
+
const normalized = value.trim();
|
|
69
|
+
return BIGINT_PATTERN.test(normalized)
|
|
70
|
+
? { valid: true, value: normalized }
|
|
71
|
+
: { valid: false };
|
|
72
|
+
}
|
|
73
|
+
case 'INTEGER': {
|
|
74
|
+
const parsed = Number(value);
|
|
75
|
+
return Number.isInteger(parsed) ? { valid: true, value: parsed } : { valid: false };
|
|
76
|
+
}
|
|
77
|
+
case 'DECIMAL': {
|
|
78
|
+
const normalized = value.trim();
|
|
79
|
+
return DECIMAL_PATTERN.test(normalized)
|
|
80
|
+
? { valid: true, value: normalized }
|
|
81
|
+
: { valid: false };
|
|
82
|
+
}
|
|
83
|
+
case 'FLOAT':
|
|
84
|
+
case 'DOUBLE':
|
|
85
|
+
case 'REAL': {
|
|
86
|
+
const parsed = Number(value);
|
|
87
|
+
return Number.isFinite(parsed) ? { valid: true, value: parsed } : { valid: false };
|
|
88
|
+
}
|
|
89
|
+
case 'BOOLEAN':
|
|
90
|
+
return value === 'true' || value === 'false' ? { valid: true, value: value === 'true' } : { valid: false };
|
|
91
|
+
case 'DATE':
|
|
92
|
+
case 'DATEONLY': {
|
|
93
|
+
const parsed = new Date(value);
|
|
94
|
+
return Number.isNaN(parsed.getTime()) ? { valid: false } : { valid: true, value: parsed };
|
|
95
|
+
}
|
|
96
|
+
default:
|
|
97
|
+
return { valid: true, value };
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
export const parseTypedList = (value, typeName) => {
|
|
101
|
+
let values;
|
|
102
|
+
if (Array.isArray(value)) {
|
|
103
|
+
values = value;
|
|
104
|
+
}
|
|
105
|
+
else if (typeof value === 'string') {
|
|
106
|
+
const tagged = /^([nfbsd])\|(.*)$/s.exec(value);
|
|
107
|
+
values = tagged
|
|
108
|
+
? (tagged[2] ?? '').split(',').map((item) => `${tagged[1]}|${item}`)
|
|
109
|
+
: value.split(',');
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
return { valid: false };
|
|
113
|
+
}
|
|
114
|
+
const parsed = values.map((item) => parseTypedValue(item, typeName));
|
|
115
|
+
return parsed.every((item) => item.valid)
|
|
116
|
+
? { valid: true, value: parsed.map((item) => item.value) }
|
|
117
|
+
: { valid: false };
|
|
118
|
+
};
|
|
119
|
+
export const isValidDateText = (value) => {
|
|
120
|
+
const normalized = value.trim();
|
|
121
|
+
if (!normalized) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
return !Number.isNaN(new Date(normalized).getTime());
|
|
125
|
+
};
|
|
126
|
+
export const isValidTimeZone = (value) => {
|
|
127
|
+
if (typeof value !== 'string' || value.length === 0 || value.length > 100) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
new Intl.DateTimeFormat('en-US', { timeZone: value }).format();
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
//# sourceMappingURL=values.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"values.js","sourceRoot":"","sources":["../../../src/utils/values.ts"],"names":[],"mappings":"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","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,12 @@
|
|
|
1
|
+
import type { Includeable, Model, ModelStatic } from 'sequelize';
|
|
2
|
+
interface ParseIncludesOptions {
|
|
3
|
+
readonly input: unknown;
|
|
4
|
+
readonly model: ModelStatic<Model>;
|
|
5
|
+
readonly isGroupBy?: boolean;
|
|
6
|
+
readonly depth?: number;
|
|
7
|
+
readonly seen?: WeakSet<object>;
|
|
8
|
+
readonly associationPath?: readonly string[];
|
|
9
|
+
readonly maxLimit?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare const parseIncludes: ({ input, model, isGroupBy, depth, seen, associationPath, maxLimit, }: ParseIncludesOptions) => Includeable[];
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Model, ModelStatic, WhereOptions } from 'sequelize';
|
|
2
|
+
import type { ColumnDetail } from '../types.js';
|
|
3
|
+
interface ParseSearchOptions {
|
|
4
|
+
readonly searchText: unknown;
|
|
5
|
+
readonly searchFields: unknown;
|
|
6
|
+
readonly checkYesNo?: unknown;
|
|
7
|
+
readonly timezone?: unknown;
|
|
8
|
+
readonly model: ModelStatic<Model>;
|
|
9
|
+
readonly columns?: Readonly<Partial<Record<string, ColumnDetail>>>;
|
|
10
|
+
}
|
|
11
|
+
export declare const parseSearch: ({ searchText, searchFields, checkYesNo, timezone, model, columns, }: ParseSearchOptions) => WhereOptions | null;
|
|
12
|
+
export declare const mergeSearchWhere: (filterWhere: WhereOptions, searchWhere: WhereOptions | null) => WhereOptions;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Model, ModelStatic, WhereOptions } from 'sequelize';
|
|
2
|
+
import type { ColumnDetail } from '../types.js';
|
|
3
|
+
interface ParseWhereOptions {
|
|
4
|
+
readonly input: unknown;
|
|
5
|
+
readonly model: ModelStatic<Model>;
|
|
6
|
+
readonly columns?: Readonly<Partial<Record<string, ColumnDetail>>>;
|
|
7
|
+
readonly associationPath?: readonly string[];
|
|
8
|
+
}
|
|
9
|
+
export declare const parseWhere: ({ input, model, columns, associationPath }: ParseWhereOptions) => {
|
|
10
|
+
where: WhereOptions;
|
|
11
|
+
whereFields: string[];
|
|
12
|
+
};
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Model } from 'sequelize';
|
|
2
|
+
import type { ParsedQuery, QueryParserOptions } from './types.js';
|
|
3
|
+
export declare class QueryParser<M extends Model> {
|
|
4
|
+
#private;
|
|
5
|
+
constructor(options: QueryParserOptions<M>);
|
|
6
|
+
getFullQuery(target?: Record<string, unknown>): ParsedQuery;
|
|
7
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Includeable, Model, ModelStatic, Order, WhereOptions } from 'sequelize';
|
|
2
|
+
export type QueryParserQuery = Record<string, unknown> & {
|
|
3
|
+
checkYesNo?: unknown;
|
|
4
|
+
include?: unknown;
|
|
5
|
+
limit?: unknown;
|
|
6
|
+
page?: unknown;
|
|
7
|
+
paranoid?: unknown;
|
|
8
|
+
q?: unknown;
|
|
9
|
+
searchFields?: unknown;
|
|
10
|
+
searchText?: unknown;
|
|
11
|
+
select?: unknown;
|
|
12
|
+
sort?: unknown;
|
|
13
|
+
subQuery?: unknown;
|
|
14
|
+
};
|
|
15
|
+
export interface QueryParserRequest {
|
|
16
|
+
readonly query?: QueryParserQuery;
|
|
17
|
+
readonly headers?: Readonly<Record<string, string | readonly string[] | undefined>>;
|
|
18
|
+
}
|
|
19
|
+
export interface ColumnDetail {
|
|
20
|
+
readonly displayName?: string;
|
|
21
|
+
readonly fieldName: string;
|
|
22
|
+
readonly type: string;
|
|
23
|
+
readonly show?: boolean;
|
|
24
|
+
readonly custom?: boolean;
|
|
25
|
+
readonly default?: boolean;
|
|
26
|
+
readonly width?: number;
|
|
27
|
+
readonly minWidth?: number;
|
|
28
|
+
readonly isPinned?: boolean;
|
|
29
|
+
readonly include?: Readonly<Record<string, unknown>>;
|
|
30
|
+
readonly searchKeys?: readonly string[];
|
|
31
|
+
readonly tableName?: string;
|
|
32
|
+
readonly foreignKey?: string;
|
|
33
|
+
readonly fieldType?: 'column' | 'association' | 'filter';
|
|
34
|
+
}
|
|
35
|
+
export interface QueryParserOptions<M extends Model = Model> {
|
|
36
|
+
readonly request: QueryParserRequest;
|
|
37
|
+
readonly model: ModelStatic<M>;
|
|
38
|
+
readonly columns?: Readonly<Partial<Record<string, ColumnDetail>>>;
|
|
39
|
+
readonly isGroupBy?: boolean;
|
|
40
|
+
readonly maxLimit?: number;
|
|
41
|
+
}
|
|
42
|
+
export type ParsedQuery = Record<string, unknown> & {
|
|
43
|
+
readonly where: WhereOptions;
|
|
44
|
+
readonly order: Order;
|
|
45
|
+
readonly limit: number;
|
|
46
|
+
readonly offset: number;
|
|
47
|
+
readonly include: Includeable[];
|
|
48
|
+
readonly attributes: string[];
|
|
49
|
+
readonly subQuery: boolean | undefined;
|
|
50
|
+
readonly paranoid: boolean | undefined;
|
|
51
|
+
readonly whereFields: string[];
|
|
52
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Model, ModelStatic } from 'sequelize';
|
|
2
|
+
export interface ResolvedField {
|
|
3
|
+
readonly path: string[];
|
|
4
|
+
readonly model: ModelStatic<Model>;
|
|
5
|
+
readonly attribute: string;
|
|
6
|
+
readonly typeName: string | undefined;
|
|
7
|
+
}
|
|
8
|
+
export declare const isSafeIdentifier: (value: string) => boolean;
|
|
9
|
+
export declare const getModelAttributes: (model: ModelStatic<Model>) => Record<string, any>;
|
|
10
|
+
export declare const getAttributeNames: (model: ModelStatic<Model>) => string[];
|
|
11
|
+
export declare const getSqlFieldReference: (rootModel: ModelStatic<Model>, path: readonly string[], baseAssociationPath?: readonly string[]) => string | null;
|
|
12
|
+
export declare const resolveField: (model: ModelStatic<Model>, path: string) => ResolvedField | null;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
|
|
2
|
+
export declare const parseBoolean: (value: unknown, fallback?: boolean) => boolean | undefined;
|
|
3
|
+
export declare const parsePositiveInteger: (value: unknown, fallback: number, maximum: number) => number;
|
|
4
|
+
export interface ParsedValue {
|
|
5
|
+
readonly valid: boolean;
|
|
6
|
+
readonly value?: unknown;
|
|
7
|
+
}
|
|
8
|
+
export declare const isScalarValue: (value: unknown) => value is string | number | boolean | Date | null;
|
|
9
|
+
export declare const parseTypedValue: (value: unknown, typeName?: string) => ParsedValue;
|
|
10
|
+
export declare const parseTypedList: (value: unknown, typeName?: string) => ParsedValue;
|
|
11
|
+
export declare const isValidDateText: (value: string) => boolean;
|
|
12
|
+
export declare const isValidTimeZone: (value: unknown) => value is string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,62 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sequelize-api-query-parser",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Safely parse HTTP query parameters into Sequelize query options.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/cjs/index.js",
|
|
7
|
+
"module": "./dist/esm/index.js",
|
|
8
|
+
"types": "./dist/types/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/types/index.d.ts",
|
|
12
|
+
"import": "./dist/esm/index.js",
|
|
13
|
+
"require": "./dist/cjs/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"clean": "node scripts/clean.mjs",
|
|
30
|
+
"build": "npm run clean && tsc -p tsconfig.build.esm.json && tsc -p tsconfig.build.cjs.json && node scripts/prepare-dist.mjs",
|
|
31
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:coverage": "vitest run --coverage",
|
|
34
|
+
"lint": "eslint src test --max-warnings=0",
|
|
35
|
+
"smoke": "node scripts/smoke-esm.mjs && node scripts/smoke-cjs.cjs && tsc -p test/consumer/tsconfig.json",
|
|
36
|
+
"check": "npm run lint && npm run typecheck && npm test && npm run build && npm run smoke",
|
|
37
|
+
"prepack": "npm run check"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"sequelize",
|
|
41
|
+
"query-parser",
|
|
42
|
+
"typescript",
|
|
43
|
+
"express",
|
|
44
|
+
"filtering",
|
|
45
|
+
"pagination"
|
|
46
|
+
],
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"sequelize": ">=6.37.8 <7"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/node": "20.19.9",
|
|
53
|
+
"@vitest/coverage-v8": "4.1.11",
|
|
54
|
+
"eslint": "9.39.5",
|
|
55
|
+
"globals": "15.15.0",
|
|
56
|
+
"semver": "7.8.5",
|
|
57
|
+
"sequelize": "6.37.8",
|
|
58
|
+
"typescript": "5.9.3",
|
|
59
|
+
"typescript-eslint": "8.69.0",
|
|
60
|
+
"vitest": "4.1.11"
|
|
61
|
+
}
|
|
62
|
+
}
|