prisma-generator-express 1.18.0 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +399 -194
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +1 -1
- package/dist/bin.js.map +1 -1
- package/dist/client/encodeQueryParams.d.ts +1 -0
- package/dist/client/encodeQueryParams.js +33 -0
- package/dist/client/encodeQueryParams.js.map +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/copy/misc.d.ts +5 -0
- package/dist/copy/misc.js +52 -0
- package/dist/copy/misc.js.map +1 -0
- package/dist/generators/generateImportPrismaStatement.d.ts +3 -0
- package/dist/generators/generateImportPrismaStatement.js +55 -0
- package/dist/generators/generateImportPrismaStatement.js.map +1 -0
- package/dist/generators/generateQueryBuilderHelper.d.ts +2 -0
- package/dist/generators/generateQueryBuilderHelper.js +139 -0
- package/dist/generators/generateQueryBuilderHelper.js.map +1 -0
- package/dist/generators/generateRouter.d.ts +6 -0
- package/dist/generators/generateRouter.js +340 -0
- package/dist/generators/generateRouter.js.map +1 -0
- package/dist/generators/generateUnifiedDocs.d.ts +1 -0
- package/dist/generators/generateUnifiedDocs.js +171 -0
- package/dist/generators/generateUnifiedDocs.js.map +1 -0
- package/dist/generators/generateUnifiedHandler.d.ts +6 -0
- package/dist/generators/generateUnifiedHandler.js +444 -0
- package/dist/generators/generateUnifiedHandler.js.map +1 -0
- package/dist/generators/generateUnifiedScalarUI.d.ts +5 -0
- package/dist/generators/generateUnifiedScalarUI.js +1390 -0
- package/dist/generators/generateUnifiedScalarUI.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +80 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/copyFiles.d.ts +6 -0
- package/dist/utils/copyFiles.js +123 -21
- package/dist/utils/copyFiles.js.map +1 -1
- package/dist/utils/strings.d.ts +2 -0
- package/dist/utils/writeFileSafely.d.ts +10 -0
- package/dist/utils/writeFileSafely.js +86 -14
- package/dist/utils/writeFileSafely.js.map +1 -1
- package/package.json +64 -31
- package/src/client/encodeQueryParams.ts +56 -0
- package/src/copy/buildModelOpenApi.ts +1569 -0
- package/src/copy/misc.ts +21 -0
- package/src/copy/operationDefinitions.ts +96 -0
- package/src/copy/parseQueryParams.ts +36 -21
- package/src/copy/routeConfig.ts +68 -28
- package/dist/generator.js +0 -47
- package/dist/generator.js.map +0 -1
- package/dist/helpers/generateImportPrismaStatement.js +0 -25
- package/dist/helpers/generateImportPrismaStatement.js.map +0 -1
- package/dist/helpers/generateOperation.js +0 -471
- package/dist/helpers/generateOperation.js.map +0 -1
- package/dist/helpers/generateRouteFile.js +0 -210
- package/dist/helpers/generateRouteFile.js.map +0 -1
- package/dist/utils/formatFile.js +0 -26
- package/dist/utils/formatFile.js.map +0 -1
- package/src/bin.ts +0 -2
- package/src/constants.ts +0 -1
- package/src/copy/encodeQueryParams.spec.ts +0 -303
- package/src/copy/encodeQueryParams.ts +0 -44
- package/src/copy/misc.spec.ts +0 -62
- package/src/copy/parseQueryParams.spec.ts +0 -187
- package/src/copy/transformZod.spec.ts +0 -763
- package/src/generator.ts +0 -54
- package/src/helpers/generateImportPrismaStatement.ts +0 -38
- package/src/helpers/generateOperation.ts +0 -515
- package/src/helpers/generateRouteFile.ts +0 -213
- package/src/utils/copyFiles.ts +0 -27
- package/src/utils/formatFile.ts +0 -22
- package/src/utils/strings.ts +0 -7
- package/src/utils/writeFileSafely.ts +0 -29
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generateRouterFunction = generateRouterFunction;
|
|
4
|
-
function generateRouterFunction({ model, }) {
|
|
5
|
-
const modelName = model.name;
|
|
6
|
-
const routerFunctionName = `${modelName}Router`;
|
|
7
|
-
return `import express, {
|
|
8
|
-
RequestHandler
|
|
9
|
-
} from 'express'
|
|
10
|
-
import { ParsedQs } from 'qs'
|
|
11
|
-
import { ${modelName}FindFirst } from './${modelName}FindFirst';
|
|
12
|
-
import { ${modelName}FindMany } from './${modelName}FindMany';
|
|
13
|
-
import { ${modelName}FindUnique } from './${modelName}FindUnique';
|
|
14
|
-
import { ${modelName}Create } from './${modelName}Create';
|
|
15
|
-
import { ${modelName}CreateMany } from './${modelName}CreateMany';
|
|
16
|
-
import { ${modelName}CreateManyAndReturn } from './${modelName}CreateManyAndReturn';
|
|
17
|
-
import { ${modelName}Update } from './${modelName}Update';
|
|
18
|
-
import { ${modelName}UpdateMany } from './${modelName}UpdateMany';
|
|
19
|
-
import { ${modelName}UpdateManyAndReturn } from './${modelName}UpdateManyAndReturn';
|
|
20
|
-
import { ${modelName}Upsert } from './${modelName}Upsert';
|
|
21
|
-
import { ${modelName}Delete } from './${modelName}Delete';
|
|
22
|
-
import { ${modelName}DeleteMany } from './${modelName}DeleteMany';
|
|
23
|
-
import { ${modelName}Aggregate } from './${modelName}Aggregate';
|
|
24
|
-
import { ${modelName}Count } from './${modelName}Count';
|
|
25
|
-
import { ${modelName}GroupBy } from './${modelName}GroupBy';
|
|
26
|
-
import { createValidatorMiddleware, removeTrailingSlash } from '../createValidatorMiddleware'
|
|
27
|
-
import { RouteConfig, ValidatorConfig } from '../routeConfig'
|
|
28
|
-
import { parseQueryParams } from "../parseQueryParams";
|
|
29
|
-
|
|
30
|
-
const defaultBeforeAfter = {
|
|
31
|
-
before: [] as RequestHandler[],
|
|
32
|
-
after: [] as RequestHandler[],
|
|
33
|
-
inputValidator: undefined,
|
|
34
|
-
outputValidator: undefined,
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Generates an Express router for ${modelName} model.
|
|
39
|
-
* @param config Contains optional middleware to enable routes.
|
|
40
|
-
* @param customUrlPrefix Optional custom URL prefix for the routes.
|
|
41
|
-
* @returns {express.Router}
|
|
42
|
-
*/
|
|
43
|
-
export function ${routerFunctionName}(config: RouteConfig<RequestHandler>) {
|
|
44
|
-
const router = express.Router();
|
|
45
|
-
const basePath = removeTrailingSlash(config.customUrlPrefix || '') +
|
|
46
|
-
removeTrailingSlash(config.addModelPrefix !== false ? '/${modelName.toLowerCase()}' : '');
|
|
47
|
-
|
|
48
|
-
const setupRoute = (
|
|
49
|
-
path: string,
|
|
50
|
-
method: "all" | "get" | "post" | "put" | "delete" | "patch" | "options" | "head",
|
|
51
|
-
middlewares: RequestHandler[],
|
|
52
|
-
handler: RequestHandler,
|
|
53
|
-
inputValidator?: ValidatorConfig,
|
|
54
|
-
outputValidator?: ValidatorConfig
|
|
55
|
-
) => {
|
|
56
|
-
const middlewaresWithValidators: RequestHandler[] = [
|
|
57
|
-
(req, res, next) => {
|
|
58
|
-
if (req.query) {
|
|
59
|
-
req.query = parseQueryParams(req.query as Record<string, string>) as ParsedQs;
|
|
60
|
-
}
|
|
61
|
-
next();
|
|
62
|
-
},
|
|
63
|
-
...middlewares
|
|
64
|
-
];
|
|
65
|
-
|
|
66
|
-
if (inputValidator) {
|
|
67
|
-
middlewaresWithValidators.push(createValidatorMiddleware({
|
|
68
|
-
schema: inputValidator.schema,
|
|
69
|
-
allowedPaths: inputValidator.allow,
|
|
70
|
-
forbiddenPaths: inputValidator.forbid,
|
|
71
|
-
target: method === 'get' ? 'query' : 'body',
|
|
72
|
-
}));
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
middlewaresWithValidators.push((req, res, next) => {
|
|
76
|
-
res.locals.outputValidator = outputValidator;
|
|
77
|
-
next();
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
middlewaresWithValidators.push(handler);
|
|
81
|
-
|
|
82
|
-
router[method](removeTrailingSlash(basePath + path), ...middlewaresWithValidators);
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (config.enableAll || config?.findFirst) {
|
|
87
|
-
const { before = [], after = [], inputValidator, outputValidator } = config.findFirst || defaultBeforeAfter;
|
|
88
|
-
setupRoute('/first', 'get', before, ${modelName}FindFirst as RequestHandler, inputValidator, outputValidator);
|
|
89
|
-
if (after.length) {
|
|
90
|
-
router.use(removeTrailingSlash(basePath) + '/first', ...after);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (config.enableAll || config?.findMany) {
|
|
95
|
-
const { before = [], after = [], inputValidator, outputValidator } = config.findMany || defaultBeforeAfter;
|
|
96
|
-
setupRoute('/', 'get', before, ${modelName}FindMany as RequestHandler, inputValidator, outputValidator);
|
|
97
|
-
if (after.length) {
|
|
98
|
-
router.use(removeTrailingSlash(basePath + '/'), ...after);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (config.enableAll || config?.findUnique) {
|
|
103
|
-
const { before = [], after = [], inputValidator, outputValidator } = config.findUnique || defaultBeforeAfter;
|
|
104
|
-
setupRoute('/:id', 'get', before, ${modelName}FindUnique as any, inputValidator, outputValidator);
|
|
105
|
-
if (after.length) {
|
|
106
|
-
router.use(removeTrailingSlash(basePath) + '/:id', ...after);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (config.enableAll || config?.create) {
|
|
111
|
-
const { before = [], after = [], inputValidator, outputValidator } = config.create || defaultBeforeAfter;
|
|
112
|
-
setupRoute('/', 'post', before, ${modelName}Create as RequestHandler, inputValidator, outputValidator);
|
|
113
|
-
if (after.length) {
|
|
114
|
-
router.use(removeTrailingSlash(basePath + '/'), ...after);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (config.enableAll || config?.createMany) {
|
|
119
|
-
const { before = [], after = [], inputValidator, outputValidator } = config.createMany || defaultBeforeAfter;
|
|
120
|
-
setupRoute('/many', 'post', before, ${modelName}CreateMany as RequestHandler, inputValidator, outputValidator);
|
|
121
|
-
if (after.length) {
|
|
122
|
-
router.use(removeTrailingSlash(basePath) + '/many', ...after);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (config.enableAll || config?.createManyAndReturn) {
|
|
127
|
-
const { before = [], after = [], inputValidator, outputValidator } = config.createManyAndReturn || defaultBeforeAfter;
|
|
128
|
-
setupRoute('/many/return', 'post', before, ${modelName}CreateManyAndReturn as RequestHandler, inputValidator, outputValidator);
|
|
129
|
-
if (after.length) {
|
|
130
|
-
router.use(removeTrailingSlash(basePath) + '/many/return', ...after);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (config.enableAll || config?.update) {
|
|
135
|
-
const { before = [], after = [], inputValidator, outputValidator } = config.update || defaultBeforeAfter;
|
|
136
|
-
setupRoute('/', 'put', before, ${modelName}Update as RequestHandler, inputValidator, outputValidator);
|
|
137
|
-
if (after.length) {
|
|
138
|
-
router.use(removeTrailingSlash(basePath + '/'), ...after);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (config.enableAll || config?.updateMany) {
|
|
143
|
-
const { before = [], after = [], inputValidator, outputValidator } = config.updateMany || defaultBeforeAfter;
|
|
144
|
-
setupRoute('/many', 'put', before, ${modelName}UpdateMany as RequestHandler, inputValidator, outputValidator);
|
|
145
|
-
if (after.length) {
|
|
146
|
-
router.use(removeTrailingSlash(basePath) + '/many', ...after);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (config.enableAll || config?.updateManyAndReturn) {
|
|
151
|
-
const { before = [], after = [], inputValidator, outputValidator } = config.updateManyAndReturn || defaultBeforeAfter;
|
|
152
|
-
setupRoute('/many/return', 'put', before, ${modelName}UpdateManyAndReturn as RequestHandler, inputValidator, outputValidator);
|
|
153
|
-
if (after.length) {
|
|
154
|
-
router.use(removeTrailingSlash(basePath) + '/many/return', ...after);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (config.enableAll || config?.upsert) {
|
|
159
|
-
const { before = [], after = [], inputValidator, outputValidator } = config.upsert || defaultBeforeAfter;
|
|
160
|
-
setupRoute('/', 'patch', before, ${modelName}Upsert as RequestHandler, inputValidator, outputValidator);
|
|
161
|
-
if (after.length) {
|
|
162
|
-
router.use(removeTrailingSlash(basePath + '/'), ...after);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (config.enableAll || config?.delete) {
|
|
167
|
-
const { before = [], after = [], inputValidator, outputValidator } = config.delete || defaultBeforeAfter;
|
|
168
|
-
setupRoute('/', 'delete', before, ${modelName}Delete as RequestHandler, inputValidator, outputValidator);
|
|
169
|
-
if (after.length) {
|
|
170
|
-
router.use(removeTrailingSlash(basePath + '/'), ...after);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (config.enableAll || config?.deleteMany) {
|
|
175
|
-
const { before = [], after = [], inputValidator, outputValidator } = config.deleteMany || defaultBeforeAfter;
|
|
176
|
-
setupRoute('/many', 'delete', before, ${modelName}DeleteMany as RequestHandler, inputValidator, outputValidator);
|
|
177
|
-
if (after.length) {
|
|
178
|
-
router.use(removeTrailingSlash(basePath) + '/many', ...after);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (config.enableAll || config?.aggregate) {
|
|
183
|
-
const { before = [], after = [], inputValidator, outputValidator } = config.aggregate || defaultBeforeAfter;
|
|
184
|
-
setupRoute('/aggregate', 'get', before, ${modelName}Aggregate as RequestHandler, inputValidator, outputValidator);
|
|
185
|
-
if (after.length) {
|
|
186
|
-
router.use(removeTrailingSlash(basePath) + '/aggregate', ...after);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (config.enableAll || config?.count) {
|
|
191
|
-
const { before = [], after = [], inputValidator, outputValidator } = config.count || defaultBeforeAfter;
|
|
192
|
-
setupRoute('/count', 'get', before, ${modelName}Count as RequestHandler, inputValidator, outputValidator);
|
|
193
|
-
if (after.length) {
|
|
194
|
-
router.use(removeTrailingSlash(basePath) + '/count', ...after);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (config.enableAll || config?.groupBy) {
|
|
199
|
-
const { before = [], after = [], inputValidator, outputValidator } = config.groupBy || defaultBeforeAfter;
|
|
200
|
-
setupRoute('/groupby', 'get', before, ${modelName}GroupBy as RequestHandler, inputValidator, outputValidator);
|
|
201
|
-
if (after.length) {
|
|
202
|
-
router.use(removeTrailingSlash(basePath) + '/groupby', ...after);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
return router;
|
|
207
|
-
}
|
|
208
|
-
`;
|
|
209
|
-
}
|
|
210
|
-
//# sourceMappingURL=generateRouteFile.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"generateRouteFile.js","sourceRoot":"","sources":["../../src/helpers/generateRouteFile.ts"],"names":[],"mappings":";;AAEA,wDAkNC;AAlND,SAAgB,sBAAsB,CAAC,EACrC,KAAK,GAGN;IACC,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAA;IAC5B,MAAM,kBAAkB,GAAG,GAAG,SAAS,QAAQ,CAAA;IAE/C,OAAO;;;;WAIE,SAAS,uBAAuB,SAAS;WACzC,SAAS,sBAAsB,SAAS;WACxC,SAAS,wBAAwB,SAAS;WAC1C,SAAS,oBAAoB,SAAS;WACtC,SAAS,wBAAwB,SAAS;WAC1C,SAAS,iCAAiC,SAAS;WACnD,SAAS,oBAAoB,SAAS;WACtC,SAAS,wBAAwB,SAAS;WAC1C,SAAS,iCAAiC,SAAS;WACnD,SAAS,oBAAoB,SAAS;WACtC,SAAS,oBAAoB,SAAS;WACtC,SAAS,wBAAwB,SAAS;WAC1C,SAAS,uBAAuB,SAAS;WACzC,SAAS,mBAAmB,SAAS;WACrC,SAAS,qBAAqB,SAAS;;;;;;;;;;;;;qCAab,SAAS;;;;;kBAK5B,kBAAkB;;;4EAGwC,SAAS,CAAC,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0CA0CzD,SAAS;;;;;;;;qCAQd,SAAS;;;;;;;;wCAQN,SAAS;;;;;;;;sCAQX,SAAS;;;;;;;;0CAQL,SAAS;;;;;;;;iDAQF,SAAS;;;;;;;;qCAQrB,SAAS;;;;;;;;yCAQL,SAAS;;;;;;;;gDAQF,SAAS;;;;;;;;uCAQlB,SAAS;;;;;;;;wCAQR,SAAS;;;;;;;;4CAQL,SAAS;;;;;;;;8CAQP,SAAS;;;;;;;;0CAQb,SAAS;;;;;;;;4CAQP,SAAS;;;;;;;;CAQpD,CAAA;AACD,CAAC"}
|
package/dist/utils/formatFile.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.formatFile = void 0;
|
|
7
|
-
const prettier_1 = __importDefault(require("prettier"));
|
|
8
|
-
const formatFile = (content) => {
|
|
9
|
-
return new Promise((res, rej) => prettier_1.default.resolveConfig(process.cwd()).then((options) => {
|
|
10
|
-
if (!options) {
|
|
11
|
-
res(content);
|
|
12
|
-
}
|
|
13
|
-
try {
|
|
14
|
-
const formatted = prettier_1.default.format(content, {
|
|
15
|
-
...options,
|
|
16
|
-
parser: 'typescript',
|
|
17
|
-
});
|
|
18
|
-
res(formatted);
|
|
19
|
-
}
|
|
20
|
-
catch (error) {
|
|
21
|
-
rej(error);
|
|
22
|
-
}
|
|
23
|
-
}));
|
|
24
|
-
};
|
|
25
|
-
exports.formatFile = formatFile;
|
|
26
|
-
//# sourceMappingURL=formatFile.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"formatFile.js","sourceRoot":"","sources":["../../src/utils/formatFile.ts"],"names":[],"mappings":";;;;;;AAAA,wDAA+B;AAExB,MAAM,UAAU,GAAG,CAAC,OAAe,EAAmB,EAAE;IAC7D,OAAO,IAAI,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAC9B,kBAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;QACrD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,GAAG,CAAC,OAAO,CAAC,CAAA;QACd,CAAC;QAED,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,kBAAQ,CAAC,MAAM,CAAC,OAAO,EAAE;gBACzC,GAAG,OAAO;gBACV,MAAM,EAAE,YAAY;aACrB,CAAC,CAAA;YAEF,GAAG,CAAC,SAAS,CAAC,CAAA;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,KAAK,CAAC,CAAA;QACZ,CAAC;IACH,CAAC,CAAC,CACH,CAAA;AACH,CAAC,CAAA;AAnBY,QAAA,UAAU,cAmBtB"}
|
package/src/bin.ts
DELETED
package/src/constants.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const GENERATOR_NAME = 'prisma-generator-express'
|
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
import { encodeQueryParams } from './encodeQueryParams'
|
|
2
|
-
|
|
3
|
-
describe('Query Params Encoding', () => {
|
|
4
|
-
it('should encode simple key-value pairs correctly', () => {
|
|
5
|
-
const params = {
|
|
6
|
-
id: 1,
|
|
7
|
-
name: 'test',
|
|
8
|
-
}
|
|
9
|
-
const result = encodeQueryParams(params)
|
|
10
|
-
expect(result).toBe('id=1&name=test')
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
it('should encode nested objects correctly', () => {
|
|
14
|
-
const params = {
|
|
15
|
-
select: {
|
|
16
|
-
id: true,
|
|
17
|
-
name: true,
|
|
18
|
-
},
|
|
19
|
-
}
|
|
20
|
-
const result = encodeQueryParams(params)
|
|
21
|
-
expect(result).toBe('select%5Bid%5D=true&select%5Bname%5D=true')
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
it('should encode arrays correctly', () => {
|
|
25
|
-
const params = {
|
|
26
|
-
ids: [1, 2, 3],
|
|
27
|
-
}
|
|
28
|
-
const result = encodeQueryParams(params)
|
|
29
|
-
expect(result).toBe('ids%5B0%5D=1&ids%5B1%5D=2&ids%5B2%5D=3')
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it('should encode complex nested objects correctly', () => {
|
|
33
|
-
const params = {
|
|
34
|
-
select: {
|
|
35
|
-
id: true,
|
|
36
|
-
project_id: true,
|
|
37
|
-
list_id: true,
|
|
38
|
-
user_assignments: {
|
|
39
|
-
select: {
|
|
40
|
-
user: true,
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
tags_mappings: {
|
|
44
|
-
select: {
|
|
45
|
-
tag: true,
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
attachments: {
|
|
49
|
-
select: {
|
|
50
|
-
attachment: true,
|
|
51
|
-
},
|
|
52
|
-
where: {
|
|
53
|
-
is_image: true,
|
|
54
|
-
},
|
|
55
|
-
take: 100,
|
|
56
|
-
orderBy: {
|
|
57
|
-
created_at: 'desc',
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
rendered_description: true,
|
|
61
|
-
description: true,
|
|
62
|
-
created_at: true,
|
|
63
|
-
start_date: true,
|
|
64
|
-
reactions: true,
|
|
65
|
-
intervals: true,
|
|
66
|
-
column_id: true,
|
|
67
|
-
priority: true,
|
|
68
|
-
due_date: true,
|
|
69
|
-
column: true,
|
|
70
|
-
title: true,
|
|
71
|
-
order: true,
|
|
72
|
-
color: true,
|
|
73
|
-
},
|
|
74
|
-
where: {
|
|
75
|
-
id: 'task_id',
|
|
76
|
-
AND: [
|
|
77
|
-
{
|
|
78
|
-
OR: [{ to_delete: false }, { to_delete: null }],
|
|
79
|
-
},
|
|
80
|
-
],
|
|
81
|
-
},
|
|
82
|
-
}
|
|
83
|
-
const result = encodeQueryParams(params)
|
|
84
|
-
expect(result).toBe(
|
|
85
|
-
'select%5Bid%5D=true&select%5Bproject_id%5D=true&select%5Blist_id%5D=true&select%5Buser_assignments%5D%5Bselect%5D%5Buser%5D=true&select%5Btags_mappings%5D%5Bselect%5D%5Btag%5D=true&select%5Battachments%5D%5Bselect%5D%5Battachment%5D=true&select%5Battachments%5D%5Bwhere%5D%5Bis_image%5D=true&select%5Battachments%5D%5Btake%5D=100&select%5Battachments%5D%5BorderBy%5D%5Bcreated_at%5D=desc&select%5Brendered_description%5D=true&select%5Bdescription%5D=true&select%5Bcreated_at%5D=true&select%5Bstart_date%5D=true&select%5Breactions%5D=true&select%5Bintervals%5D=true&select%5Bcolumn_id%5D=true&select%5Bpriority%5D=true&select%5Bdue_date%5D=true&select%5Bcolumn%5D=true&select%5Btitle%5D=true&select%5Border%5D=true&select%5Bcolor%5D=true&where%5Bid%5D=task_id&where%5BAND%5D%5B0%5D%5BOR%5D%5B0%5D%5Bto_delete%5D=false&where%5BAND%5D%5B0%5D%5BOR%5D%5B1%5D%5Bto_delete%5D=null',
|
|
86
|
-
)
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it('should handle empty objects correctly', () => {
|
|
90
|
-
const params = {}
|
|
91
|
-
const result = encodeQueryParams(params)
|
|
92
|
-
expect(result).toBe('')
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
it('should handle null values correctly', () => {
|
|
96
|
-
const params = {
|
|
97
|
-
id: null,
|
|
98
|
-
name: 'test',
|
|
99
|
-
}
|
|
100
|
-
const result = encodeQueryParams(params)
|
|
101
|
-
expect(result).toBe('id=null&name=test')
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('should handle boolean values correctly', () => {
|
|
105
|
-
const params = {
|
|
106
|
-
isActive: true,
|
|
107
|
-
isDeleted: false,
|
|
108
|
-
}
|
|
109
|
-
const result = encodeQueryParams(params)
|
|
110
|
-
expect(result).toBe('isActive=true&isDeleted=false')
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
it('should handle nested arrays correctly', () => {
|
|
114
|
-
const params = {
|
|
115
|
-
filters: [
|
|
116
|
-
{
|
|
117
|
-
key: 'age',
|
|
118
|
-
values: [20, 30, 40],
|
|
119
|
-
},
|
|
120
|
-
],
|
|
121
|
-
}
|
|
122
|
-
const result = encodeQueryParams(params)
|
|
123
|
-
expect(result).toBe(
|
|
124
|
-
'filters%5B0%5D%5Bkey%5D=age&filters%5B0%5D%5Bvalues%5D%5B0%5D=20&filters%5B0%5D%5Bvalues%5D%5B1%5D=30&filters%5B0%5D%5Bvalues%5D%5B2%5D=40',
|
|
125
|
-
)
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
it('should handle complex nested arrays and objects', () => {
|
|
129
|
-
const params = {
|
|
130
|
-
select: {
|
|
131
|
-
id: true,
|
|
132
|
-
name: true,
|
|
133
|
-
details: {
|
|
134
|
-
address: {
|
|
135
|
-
street: 'Main St',
|
|
136
|
-
city: 'Metropolis',
|
|
137
|
-
},
|
|
138
|
-
phoneNumbers: [
|
|
139
|
-
{
|
|
140
|
-
type: 'home',
|
|
141
|
-
number: '123-456-7890',
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
type: 'work',
|
|
145
|
-
number: '098-765-4321',
|
|
146
|
-
},
|
|
147
|
-
],
|
|
148
|
-
},
|
|
149
|
-
},
|
|
150
|
-
}
|
|
151
|
-
const result = encodeQueryParams(params)
|
|
152
|
-
expect(result).toBe(
|
|
153
|
-
'select%5Bid%5D=true&select%5Bname%5D=true&select%5Bdetails%5D%5Baddress%5D%5Bstreet%5D=Main%20St&select%5Bdetails%5D%5Baddress%5D%5Bcity%5D=Metropolis&select%5Bdetails%5D%5BphoneNumbers%5D%5B0%5D%5Btype%5D=home&select%5Bdetails%5D%5BphoneNumbers%5D%5B0%5D%5Bnumber%5D=123-456-7890&select%5Bdetails%5D%5BphoneNumbers%5D%5B1%5D%5Btype%5D=work&select%5Bdetails%5D%5BphoneNumbers%5D%5B1%5D%5Bnumber%5D=098-765-4321',
|
|
154
|
-
)
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
it('should handle edge cases correctly', () => {
|
|
158
|
-
const params = {
|
|
159
|
-
select: {
|
|
160
|
-
id: '',
|
|
161
|
-
name: null,
|
|
162
|
-
details: {
|
|
163
|
-
address: {
|
|
164
|
-
street: undefined,
|
|
165
|
-
city: 'Metropolis',
|
|
166
|
-
},
|
|
167
|
-
},
|
|
168
|
-
},
|
|
169
|
-
}
|
|
170
|
-
const result = encodeQueryParams(params)
|
|
171
|
-
expect(result).toBe(
|
|
172
|
-
'select%5Bid%5D=&select%5Bname%5D=null&select%5Bdetails%5D%5Baddress%5D%5Bcity%5D=Metropolis',
|
|
173
|
-
)
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
it('should handle multiple nested levels correctly', () => {
|
|
177
|
-
const params = {
|
|
178
|
-
level1: {
|
|
179
|
-
level2: {
|
|
180
|
-
level3: {
|
|
181
|
-
level4: {
|
|
182
|
-
key: 'value',
|
|
183
|
-
},
|
|
184
|
-
},
|
|
185
|
-
},
|
|
186
|
-
},
|
|
187
|
-
}
|
|
188
|
-
const result = encodeQueryParams(params)
|
|
189
|
-
expect(result).toBe(
|
|
190
|
-
'level1%5Blevel2%5D%5Blevel3%5D%5Blevel4%5D%5Bkey%5D=value',
|
|
191
|
-
)
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
it('should handle empty strings and null values correctly', () => {
|
|
195
|
-
const params = {
|
|
196
|
-
key1: '',
|
|
197
|
-
key2: null,
|
|
198
|
-
key3: 'value',
|
|
199
|
-
}
|
|
200
|
-
const result = encodeQueryParams(params)
|
|
201
|
-
expect(result).toBe('key1=&key2=null&key3=value')
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
it('should encode special characters correctly', () => {
|
|
205
|
-
const params = {
|
|
206
|
-
name: "O'Reilly & Sons",
|
|
207
|
-
description: '20% discount!',
|
|
208
|
-
}
|
|
209
|
-
const result = encodeQueryParams(params)
|
|
210
|
-
expect(result).toBe(
|
|
211
|
-
'name=O%27Reilly%20%26%20Sons&description=20%25%20discount%21',
|
|
212
|
-
)
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
it('should handle empty arrays correctly', () => {
|
|
216
|
-
const params = {
|
|
217
|
-
filters: [],
|
|
218
|
-
}
|
|
219
|
-
const result = encodeQueryParams(params)
|
|
220
|
-
expect(result).toBe('filters%5B%5D=')
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
it('should encode only special characters correctly', () => {
|
|
224
|
-
const params = {
|
|
225
|
-
special: '!@#$%^&*()_+{}|:"<>?[]\\;\',./`~',
|
|
226
|
-
}
|
|
227
|
-
const result = encodeQueryParams(params)
|
|
228
|
-
expect(result).toBe(
|
|
229
|
-
'special=%21%40%23%24%25%5E%26%2A%28%29_%2B%7B%7D%7C%3A%22%3C%3E%3F%5B%5D%5C%3B%27%2C.%2F%60%7E',
|
|
230
|
-
)
|
|
231
|
-
})
|
|
232
|
-
|
|
233
|
-
it('should encode repeated special characters correctly', () => {
|
|
234
|
-
const params = {
|
|
235
|
-
special: '!!!@@@###$$$',
|
|
236
|
-
}
|
|
237
|
-
const result = encodeQueryParams(params)
|
|
238
|
-
expect(result).toBe('special=%21%21%21%40%40%40%23%23%23%24%24%24')
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
it('should encode special characters in keys correctly', () => {
|
|
242
|
-
const params = {
|
|
243
|
-
'sp!ci@l': 'value',
|
|
244
|
-
'key&with=special?chars': 'another value',
|
|
245
|
-
}
|
|
246
|
-
const result = encodeQueryParams(params)
|
|
247
|
-
expect(result).toBe(
|
|
248
|
-
'sp%21ci%40l=value&key%26with%3Dspecial%3Fchars=another%20value',
|
|
249
|
-
)
|
|
250
|
-
})
|
|
251
|
-
|
|
252
|
-
it('should handle arrays with mixed types correctly', () => {
|
|
253
|
-
const params = {
|
|
254
|
-
mixedArray: [1, 'two', { three: 3 }],
|
|
255
|
-
}
|
|
256
|
-
const result = encodeQueryParams(params)
|
|
257
|
-
expect(result).toBe(
|
|
258
|
-
'mixedArray%5B0%5D=1&mixedArray%5B1%5D=two&mixedArray%5B2%5D%5Bthree%5D=3',
|
|
259
|
-
)
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
it('should handle boolean and null values in nested objects correctly', () => {
|
|
263
|
-
const params = {
|
|
264
|
-
nested: {
|
|
265
|
-
flag: true,
|
|
266
|
-
notSet: null,
|
|
267
|
-
},
|
|
268
|
-
}
|
|
269
|
-
const result = encodeQueryParams(params)
|
|
270
|
-
expect(result).toBe('nested%5Bflag%5D=true&nested%5BnotSet%5D=null')
|
|
271
|
-
})
|
|
272
|
-
|
|
273
|
-
it('should handle empty strings in nested objects correctly', () => {
|
|
274
|
-
const params = {
|
|
275
|
-
nested: {
|
|
276
|
-
emptyString: '',
|
|
277
|
-
validString: 'value',
|
|
278
|
-
},
|
|
279
|
-
}
|
|
280
|
-
const result = encodeQueryParams(params)
|
|
281
|
-
expect(result).toBe(
|
|
282
|
-
'nested%5BemptyString%5D=&nested%5BvalidString%5D=value',
|
|
283
|
-
)
|
|
284
|
-
})
|
|
285
|
-
|
|
286
|
-
it('should handle deeply nested structures correctly', () => {
|
|
287
|
-
const params = {
|
|
288
|
-
level1: {
|
|
289
|
-
level2: {
|
|
290
|
-
level3: {
|
|
291
|
-
level4: {
|
|
292
|
-
level5: 'value',
|
|
293
|
-
},
|
|
294
|
-
},
|
|
295
|
-
},
|
|
296
|
-
},
|
|
297
|
-
}
|
|
298
|
-
const result = encodeQueryParams(params)
|
|
299
|
-
expect(result).toBe(
|
|
300
|
-
'level1%5Blevel2%5D%5Blevel3%5D%5Blevel4%5D%5Blevel5%5D=value',
|
|
301
|
-
)
|
|
302
|
-
})
|
|
303
|
-
})
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { isObject } from './misc'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Encodes query parameters recursively, skipping undefined values.
|
|
5
|
-
* @param {Record<string, unknown>} params - The query parameters to encode.
|
|
6
|
-
* @returns {string} The encoded query string.
|
|
7
|
-
*/
|
|
8
|
-
export const encodeQueryParams = (params: Record<string, unknown>): string => {
|
|
9
|
-
const customEncodeURIComponent = (str: string): string => {
|
|
10
|
-
return encodeURIComponent(str)
|
|
11
|
-
.replace(
|
|
12
|
-
/[!'()*~]/g,
|
|
13
|
-
(c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,
|
|
14
|
-
)
|
|
15
|
-
.replace(/%20/g, '%20') // Encode spaces as %20
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const encode = (key: string, value: unknown): string => {
|
|
19
|
-
if (value === undefined) {
|
|
20
|
-
return ''
|
|
21
|
-
}
|
|
22
|
-
if (Array.isArray(value)) {
|
|
23
|
-
if (value.length === 0) {
|
|
24
|
-
return `${customEncodeURIComponent(key)}%5B%5D=`
|
|
25
|
-
}
|
|
26
|
-
return value
|
|
27
|
-
.map((v, i) => encode(`${key}[${i}]`, v))
|
|
28
|
-
.filter(Boolean)
|
|
29
|
-
.join('&')
|
|
30
|
-
}
|
|
31
|
-
if (isObject(value)) {
|
|
32
|
-
return Object.entries(value)
|
|
33
|
-
.map(([k, v]) => encode(`${key}[${k}]`, v))
|
|
34
|
-
.filter(Boolean)
|
|
35
|
-
.join('&')
|
|
36
|
-
}
|
|
37
|
-
return `${customEncodeURIComponent(key)}=${customEncodeURIComponent(String(value))}`
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return Object.entries(params)
|
|
41
|
-
.map(([k, v]) => encode(k, v))
|
|
42
|
-
.filter(Boolean)
|
|
43
|
-
.join('&')
|
|
44
|
-
}
|
package/src/copy/misc.spec.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { isJsonString, safeJSONparse, isObject } from './misc'
|
|
2
|
-
|
|
3
|
-
describe('misc.ts', () => {
|
|
4
|
-
describe('isJsonString', () => {
|
|
5
|
-
it('should return true for valid JSON string', () => {
|
|
6
|
-
expect(isJsonString('{"name":"John"}')).toBe(true)
|
|
7
|
-
})
|
|
8
|
-
|
|
9
|
-
it('should return false for invalid JSON string', () => {
|
|
10
|
-
expect(isJsonString('{"name": "John"')).toBe(false) // Missing closing brace
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
it('should return false for non-string input', () => {
|
|
14
|
-
expect(isJsonString(123)).toBe(false)
|
|
15
|
-
expect(isJsonString({ name: 'John' })).toBe(false)
|
|
16
|
-
expect(isJsonString(null)).toBe(false)
|
|
17
|
-
expect(isJsonString(undefined)).toBe(false)
|
|
18
|
-
})
|
|
19
|
-
})
|
|
20
|
-
describe('safeJSONparse', () => {
|
|
21
|
-
it('should parse valid JSON string', () => {
|
|
22
|
-
expect(safeJSONparse('{"name":"John"}')).toEqual({ name: 'John' })
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
it('should return false for string "false"', () => {
|
|
26
|
-
expect(safeJSONparse('false')).toBe(false)
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('should return undefined for string "undefined"', () => {
|
|
30
|
-
expect(safeJSONparse('undefined')).toBeUndefined()
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('should return null for string "null"', () => {
|
|
34
|
-
expect(safeJSONparse('null')).toBeNull()
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
it('should return original data for non-JSON string', () => {
|
|
38
|
-
expect(safeJSONparse('Hello World')).toBe('Hello World')
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('should return original data for non-string input', () => {
|
|
42
|
-
expect(safeJSONparse(123)).toBe(123)
|
|
43
|
-
expect(safeJSONparse({ name: 'John' })).toEqual({ name: 'John' })
|
|
44
|
-
expect(safeJSONparse(null)).toBe(null)
|
|
45
|
-
expect(safeJSONparse(undefined)).toBe(undefined)
|
|
46
|
-
})
|
|
47
|
-
})
|
|
48
|
-
describe('isObject', () => {
|
|
49
|
-
it('should return true for objects', () => {
|
|
50
|
-
expect(isObject({})).toBe(true)
|
|
51
|
-
expect(isObject({ name: 'John' })).toBe(true)
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('should return false for non-objects', () => {
|
|
55
|
-
expect(isObject('Hello World')).toBe(false)
|
|
56
|
-
expect(isObject(123)).toBe(false)
|
|
57
|
-
expect(isObject(null)).toBe(false)
|
|
58
|
-
expect(isObject(undefined)).toBe(false)
|
|
59
|
-
expect(isObject([])).toBe(false) // Arrays are not considered objects in this context
|
|
60
|
-
})
|
|
61
|
-
})
|
|
62
|
-
})
|