@visulima/crud 2.0.46 → 3.0.0-alpha.10
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/CHANGELOG.md +162 -0
- package/LICENSE.md +22 -1
- package/README.md +29 -18
- package/dist/adapter/prisma/index.d.cts +39 -0
- package/dist/adapter/prisma/index.d.mts +39 -0
- package/dist/adapter/prisma/index.d.ts +39 -0
- package/dist/adapter/prisma/types.d.cts +31 -0
- package/dist/adapter/prisma/types.d.mts +31 -0
- package/dist/adapter/prisma/types.d.ts +31 -0
- package/dist/adapter/prisma/utils/models-to-route-names.d.cts +5 -0
- package/dist/adapter/prisma/utils/models-to-route-names.d.mts +5 -0
- package/dist/adapter/prisma/utils/models-to-route-names.d.ts +5 -0
- package/dist/adapter/prisma/utils/parse-cursor.d.cts +3 -0
- package/dist/adapter/prisma/utils/parse-cursor.d.mts +3 -0
- package/dist/adapter/prisma/utils/parse-cursor.d.ts +3 -0
- package/dist/adapter/prisma/utils/parse-order-by.d.cts +4 -0
- package/dist/adapter/prisma/utils/parse-order-by.d.mts +4 -0
- package/dist/adapter/prisma/utils/parse-order-by.d.ts +4 -0
- package/dist/adapter/prisma/utils/parse-recursive.d.cts +4 -0
- package/dist/adapter/prisma/utils/parse-recursive.d.mts +4 -0
- package/dist/adapter/prisma/utils/parse-recursive.d.ts +4 -0
- package/dist/adapter/prisma/utils/parse-where.d.cts +4 -0
- package/dist/adapter/prisma/utils/parse-where.d.mts +4 -0
- package/dist/adapter/prisma/utils/parse-where.d.ts +4 -0
- package/dist/base-crud-handler.d.cts +9 -0
- package/dist/base-crud-handler.d.mts +9 -0
- package/dist/base-crud-handler.d.ts +9 -0
- package/dist/handler/create.d.cts +11 -0
- package/dist/handler/create.d.mts +11 -0
- package/dist/handler/create.d.ts +11 -0
- package/dist/handler/delete.d.cts +7 -0
- package/dist/handler/delete.d.mts +7 -0
- package/dist/handler/delete.d.ts +7 -0
- package/dist/handler/list.d.cts +9 -0
- package/dist/handler/list.d.mts +9 -0
- package/dist/handler/list.d.ts +9 -0
- package/dist/handler/read.d.cts +7 -0
- package/dist/handler/read.d.mts +7 -0
- package/dist/handler/read.d.ts +7 -0
- package/dist/handler/update.d.cts +11 -0
- package/dist/handler/update.d.mts +11 -0
- package/dist/handler/update.d.ts +11 -0
- package/dist/index.cjs +13 -0
- package/dist/index.d.cts +6 -0
- package/dist/index.d.mts +6 -127
- package/dist/index.d.ts +6 -127
- package/dist/index.mjs +3 -1085
- package/dist/next/api/edge/index.d.cts +3 -0
- package/dist/next/api/edge/index.d.mts +3 -0
- package/dist/next/api/edge/index.d.ts +3 -0
- package/dist/next/api/node/index.d.cts +4 -0
- package/dist/next/api/node/index.d.mts +4 -0
- package/dist/next/api/node/index.d.ts +4 -0
- package/dist/next/index.cjs +11 -0
- package/dist/next/index.d.cts +2 -0
- package/dist/next/index.d.mts +2 -8
- package/dist/next/index.d.ts +2 -8
- package/dist/next/index.mjs +2 -4832
- package/dist/packem_shared/PrismaAdapter-BTCwgMow.cjs +324 -0
- package/dist/packem_shared/PrismaAdapter-dVBZvBOv.mjs +318 -0
- package/dist/packem_shared/RouteType-Bk3uAK0x.cjs +14 -0
- package/dist/packem_shared/RouteType-CB2xrWdf.mjs +10 -0
- package/dist/packem_shared/base-crud-handler-B3eCO4up.cjs +572 -0
- package/dist/packem_shared/base-crud-handler-DgrOMhoH.mjs +587 -0
- package/dist/packem_shared/edgeHandler-B4JJXPUI.mjs +16 -0
- package/dist/packem_shared/edgeHandler-CDFgDdrG.cjs +18 -0
- package/dist/packem_shared/get-accessible-routes-C6NF9Iry.cjs +16 -0
- package/dist/packem_shared/get-accessible-routes-sV5SDdFn.mjs +14 -0
- package/dist/packem_shared/models-to-route-names-CdwsK0V1.mjs +9 -0
- package/dist/packem_shared/models-to-route-names-Dv94PzhE.cjs +11 -0
- package/dist/{index.js → packem_shared/modelsToOpenApi-BuGL_l3R.cjs} +66 -453
- package/dist/packem_shared/modelsToOpenApi-Bux3khmh.mjs +706 -0
- package/dist/packem_shared/nodeHandler-BUWSKNyo.cjs +16 -0
- package/dist/packem_shared/nodeHandler-DSq7vHzv.mjs +14 -0
- package/dist/query-parser.d.cts +3 -0
- package/dist/query-parser.d.mts +3 -0
- package/dist/query-parser.d.ts +3 -0
- package/dist/swagger/adapter/prisma/index.d.cts +22 -0
- package/dist/swagger/adapter/prisma/index.d.mts +22 -0
- package/dist/swagger/adapter/prisma/index.d.ts +22 -0
- package/dist/swagger/json-schema-parser.d.cts +18 -0
- package/dist/swagger/json-schema-parser.d.mts +18 -0
- package/dist/swagger/json-schema-parser.d.ts +18 -0
- package/dist/swagger/parameters.d.cts +5 -0
- package/dist/swagger/parameters.d.mts +5 -0
- package/dist/swagger/parameters.d.ts +5 -0
- package/dist/swagger/types.d.cts +39 -0
- package/dist/swagger/types.d.mts +39 -0
- package/dist/swagger/types.d.ts +39 -0
- package/dist/swagger/utils/format-example-ref.d.cts +2 -0
- package/dist/swagger/utils/format-example-ref.d.mts +2 -0
- package/dist/swagger/utils/format-example-ref.d.ts +2 -0
- package/dist/swagger/utils/format-schema-ref.d.cts +2 -0
- package/dist/swagger/utils/format-schema-ref.d.mts +2 -0
- package/dist/swagger/utils/format-schema-ref.d.ts +2 -0
- package/dist/swagger/utils/get-models-accessible-routes.d.cts +4 -0
- package/dist/swagger/utils/get-models-accessible-routes.d.mts +4 -0
- package/dist/swagger/utils/get-models-accessible-routes.d.ts +4 -0
- package/dist/swagger/utils/get-swagger-paths.d.cts +12 -0
- package/dist/swagger/utils/get-swagger-paths.d.mts +12 -0
- package/dist/swagger/utils/get-swagger-paths.d.ts +12 -0
- package/dist/swagger/utils/get-swagger-tags.d.cts +4 -0
- package/dist/swagger/utils/get-swagger-tags.d.mts +4 -0
- package/dist/swagger/utils/get-swagger-tags.d.ts +4 -0
- package/dist/types.d.cts +106 -0
- package/dist/types.d.mts +106 -0
- package/dist/types.d.ts +106 -0
- package/dist/utils/format-resource-id.d.cts +2 -0
- package/dist/utils/format-resource-id.d.mts +2 -0
- package/dist/utils/format-resource-id.d.ts +2 -0
- package/dist/utils/get-accessible-routes.d.cts +3 -0
- package/dist/utils/get-accessible-routes.d.mts +3 -0
- package/dist/utils/get-accessible-routes.d.ts +3 -0
- package/dist/utils/get-resource-name-from-url.d.cts +5 -0
- package/dist/utils/get-resource-name-from-url.d.mts +5 -0
- package/dist/utils/get-resource-name-from-url.d.ts +5 -0
- package/dist/utils/get-route-type.d.cts +7 -0
- package/dist/utils/get-route-type.d.mts +7 -0
- package/dist/utils/get-route-type.d.ts +7 -0
- package/dist/utils/is-primitive.d.cts +2 -0
- package/dist/utils/is-primitive.d.mts +2 -0
- package/dist/utils/is-primitive.d.ts +2 -0
- package/dist/utils/validate-adapter-methods.d.cts +3 -0
- package/dist/utils/validate-adapter-methods.d.mts +3 -0
- package/dist/utils/validate-adapter-methods.d.ts +3 -0
- package/package.json +38 -19
- package/dist/chunk-5I2B5KQG.js +0 -77
- package/dist/chunk-5I2B5KQG.js.map +0 -1
- package/dist/chunk-LBXJKEOF.mjs +0 -73
- package/dist/chunk-LBXJKEOF.mjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/next/index.js +0 -4839
- package/dist/next/index.js.map +0 -1
- package/dist/next/index.mjs.map +0 -1
- package/dist/types-C5c2M01-.d.mts +0 -138
- package/dist/types-C5c2M01-.d.ts +0 -138
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const createHttpError = require('http-errors');
|
|
4
|
+
const index_js = require('next/dist/server/api-utils/index.js');
|
|
5
|
+
const pagination = require('@visulima/pagination');
|
|
6
|
+
const node_url = require('node:url');
|
|
7
|
+
const RouteType = require('./RouteType-Bk3uAK0x.cjs');
|
|
8
|
+
const getAccessibleRoutes = require('./get-accessible-routes-C6NF9Iry.cjs');
|
|
9
|
+
const pathToRegexp = require('path-to-regexp');
|
|
10
|
+
|
|
11
|
+
const _interopDefaultCompat = e => e && typeof e === 'object' && 'default' in e ? e.default : e;
|
|
12
|
+
|
|
13
|
+
const createHttpError__default = /*#__PURE__*/_interopDefaultCompat(createHttpError);
|
|
14
|
+
|
|
15
|
+
const createHandler = async ({ adapter, query, request, resourceName }) => {
|
|
16
|
+
const resources = await adapter.create(resourceName, request.body, query);
|
|
17
|
+
return {
|
|
18
|
+
data: resources,
|
|
19
|
+
status: 201
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const deleteHandler = async ({ adapter, query, resourceId, resourceName }) => {
|
|
24
|
+
const resource = await adapter.getOne(resourceName, resourceId, query);
|
|
25
|
+
if (typeof resource === "object") {
|
|
26
|
+
const deletedResource = await adapter.delete(resourceName, resourceId, query);
|
|
27
|
+
return {
|
|
28
|
+
data: deletedResource,
|
|
29
|
+
status: 200
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
throw createHttpError__default(404, `${resourceName} ${resourceId} not found`);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const listHandler = async ({ adapter, pagination: pagination$1, query, resourceName }) => {
|
|
36
|
+
let isPaginated = false;
|
|
37
|
+
let paginationOptions;
|
|
38
|
+
if (query.page !== void 0) {
|
|
39
|
+
if (query.page <= 0) {
|
|
40
|
+
throw new Error("page query must be a strictly positive number");
|
|
41
|
+
}
|
|
42
|
+
paginationOptions = {
|
|
43
|
+
page: query.page,
|
|
44
|
+
perPage: query.limit ?? pagination$1.perPage
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (paginationOptions) {
|
|
48
|
+
isPaginated = true;
|
|
49
|
+
query.skip = (paginationOptions.page - 1) * paginationOptions.perPage;
|
|
50
|
+
query.limit = paginationOptions.perPage;
|
|
51
|
+
}
|
|
52
|
+
const resources = await adapter.getAll(resourceName, query);
|
|
53
|
+
if (isPaginated) {
|
|
54
|
+
const { page, total } = await adapter.getPaginationData(resourceName, query);
|
|
55
|
+
const paginator = pagination.paginate(page, paginationOptions.perPage, total, resources);
|
|
56
|
+
return {
|
|
57
|
+
data: paginator.toJSON(),
|
|
58
|
+
status: 200
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
data: resources,
|
|
63
|
+
status: 200
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const readHandler = async ({ adapter, query, resourceId, resourceName }) => {
|
|
68
|
+
const resource = await adapter.getOne(resourceName, resourceId, query);
|
|
69
|
+
if (typeof resource !== "object") {
|
|
70
|
+
throw createHttpError__default(404, `${resourceName} ${resourceId} not found`);
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
data: resource,
|
|
74
|
+
status: 200
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const updateHandler = async ({ adapter, query, request, resourceId, resourceName }) => {
|
|
79
|
+
const resource = await adapter.getOne(resourceName, resourceId, query);
|
|
80
|
+
if (typeof resource === "object") {
|
|
81
|
+
const updatedResource = await adapter.update(resourceName, resourceId, request.body, query);
|
|
82
|
+
return {
|
|
83
|
+
data: updatedResource,
|
|
84
|
+
status: 201
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
throw createHttpError__default(404, `${resourceName} ${resourceId} not found`);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const isObject = (value) => {
|
|
91
|
+
const type = typeof value;
|
|
92
|
+
return value !== null && (type === "object" || type === "function");
|
|
93
|
+
};
|
|
94
|
+
const disallowedKeys = /* @__PURE__ */ new Set([
|
|
95
|
+
"__proto__",
|
|
96
|
+
"prototype",
|
|
97
|
+
"constructor"
|
|
98
|
+
]);
|
|
99
|
+
const MAX_ARRAY_INDEX = 1e6;
|
|
100
|
+
const isDigit = (character) => character >= "0" && character <= "9";
|
|
101
|
+
function shouldCoerceToNumber(segment) {
|
|
102
|
+
if (segment === "0") {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
if (/^[1-9]\d*$/.test(segment)) {
|
|
106
|
+
const parsedNumber = Number.parseInt(segment, 10);
|
|
107
|
+
return parsedNumber <= Number.MAX_SAFE_INTEGER && parsedNumber <= MAX_ARRAY_INDEX;
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
function processSegment(segment, parts) {
|
|
112
|
+
if (disallowedKeys.has(segment)) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
if (segment && shouldCoerceToNumber(segment)) {
|
|
116
|
+
parts.push(Number.parseInt(segment, 10));
|
|
117
|
+
} else {
|
|
118
|
+
parts.push(segment);
|
|
119
|
+
}
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
function parsePath(path) {
|
|
123
|
+
if (typeof path !== "string") {
|
|
124
|
+
throw new TypeError(`Expected a string, got ${typeof path}`);
|
|
125
|
+
}
|
|
126
|
+
const parts = [];
|
|
127
|
+
let currentSegment = "";
|
|
128
|
+
let currentPart = "start";
|
|
129
|
+
let isEscaping = false;
|
|
130
|
+
let position = 0;
|
|
131
|
+
for (const character of path) {
|
|
132
|
+
position++;
|
|
133
|
+
if (isEscaping) {
|
|
134
|
+
currentSegment += character;
|
|
135
|
+
isEscaping = false;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (character === "\\") {
|
|
139
|
+
if (currentPart === "index") {
|
|
140
|
+
throw new Error(`Invalid character '${character}' in an index at position ${position}`);
|
|
141
|
+
}
|
|
142
|
+
if (currentPart === "indexEnd") {
|
|
143
|
+
throw new Error(`Invalid character '${character}' after an index at position ${position}`);
|
|
144
|
+
}
|
|
145
|
+
isEscaping = true;
|
|
146
|
+
currentPart = currentPart === "start" ? "property" : currentPart;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
switch (character) {
|
|
150
|
+
case ".": {
|
|
151
|
+
if (currentPart === "index") {
|
|
152
|
+
throw new Error(`Invalid character '${character}' in an index at position ${position}`);
|
|
153
|
+
}
|
|
154
|
+
if (currentPart === "indexEnd") {
|
|
155
|
+
currentPart = "property";
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
if (!processSegment(currentSegment, parts)) {
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
currentSegment = "";
|
|
162
|
+
currentPart = "property";
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
case "[": {
|
|
166
|
+
if (currentPart === "index") {
|
|
167
|
+
throw new Error(`Invalid character '${character}' in an index at position ${position}`);
|
|
168
|
+
}
|
|
169
|
+
if (currentPart === "indexEnd") {
|
|
170
|
+
currentPart = "index";
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
if (currentPart === "property" || currentPart === "start") {
|
|
174
|
+
if ((currentSegment || currentPart === "property") && !processSegment(currentSegment, parts)) {
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
currentSegment = "";
|
|
178
|
+
}
|
|
179
|
+
currentPart = "index";
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
case "]": {
|
|
183
|
+
if (currentPart === "index") {
|
|
184
|
+
if (currentSegment === "") {
|
|
185
|
+
const lastSegment = parts.pop() || "";
|
|
186
|
+
currentSegment = lastSegment + "[]";
|
|
187
|
+
currentPart = "property";
|
|
188
|
+
} else {
|
|
189
|
+
const parsedNumber = Number.parseInt(currentSegment, 10);
|
|
190
|
+
const isValidInteger = !Number.isNaN(parsedNumber) && Number.isFinite(parsedNumber) && parsedNumber >= 0 && parsedNumber <= Number.MAX_SAFE_INTEGER && parsedNumber <= MAX_ARRAY_INDEX && currentSegment === String(parsedNumber);
|
|
191
|
+
if (isValidInteger) {
|
|
192
|
+
parts.push(parsedNumber);
|
|
193
|
+
} else {
|
|
194
|
+
parts.push(currentSegment);
|
|
195
|
+
}
|
|
196
|
+
currentSegment = "";
|
|
197
|
+
currentPart = "indexEnd";
|
|
198
|
+
}
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
if (currentPart === "indexEnd") {
|
|
202
|
+
throw new Error(`Invalid character '${character}' after an index at position ${position}`);
|
|
203
|
+
}
|
|
204
|
+
currentSegment += character;
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
default: {
|
|
208
|
+
if (currentPart === "index" && !isDigit(character)) {
|
|
209
|
+
throw new Error(`Invalid character '${character}' in an index at position ${position}`);
|
|
210
|
+
}
|
|
211
|
+
if (currentPart === "indexEnd") {
|
|
212
|
+
throw new Error(`Invalid character '${character}' after an index at position ${position}`);
|
|
213
|
+
}
|
|
214
|
+
if (currentPart === "start") {
|
|
215
|
+
currentPart = "property";
|
|
216
|
+
}
|
|
217
|
+
currentSegment += character;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (isEscaping) {
|
|
222
|
+
currentSegment += "\\";
|
|
223
|
+
}
|
|
224
|
+
switch (currentPart) {
|
|
225
|
+
case "property": {
|
|
226
|
+
if (!processSegment(currentSegment, parts)) {
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
case "index": {
|
|
232
|
+
throw new Error("Index was not closed");
|
|
233
|
+
}
|
|
234
|
+
case "start": {
|
|
235
|
+
parts.push("");
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return parts;
|
|
240
|
+
}
|
|
241
|
+
function normalizePath(path) {
|
|
242
|
+
if (typeof path === "string") {
|
|
243
|
+
return parsePath(path);
|
|
244
|
+
}
|
|
245
|
+
if (Array.isArray(path)) {
|
|
246
|
+
const normalized = [];
|
|
247
|
+
for (const [index, segment] of path.entries()) {
|
|
248
|
+
if (typeof segment !== "string" && typeof segment !== "number") {
|
|
249
|
+
throw new TypeError(`Expected a string or number for path segment at index ${index}, got ${typeof segment}`);
|
|
250
|
+
}
|
|
251
|
+
if (typeof segment === "number" && !Number.isFinite(segment)) {
|
|
252
|
+
throw new TypeError(`Path segment at index ${index} must be a finite number, got ${segment}`);
|
|
253
|
+
}
|
|
254
|
+
if (disallowedKeys.has(segment)) {
|
|
255
|
+
return [];
|
|
256
|
+
}
|
|
257
|
+
if (typeof segment === "string" && shouldCoerceToNumber(segment)) {
|
|
258
|
+
normalized.push(Number.parseInt(segment, 10));
|
|
259
|
+
} else {
|
|
260
|
+
normalized.push(segment);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return normalized;
|
|
264
|
+
}
|
|
265
|
+
return [];
|
|
266
|
+
}
|
|
267
|
+
function setProperty(object, path, value) {
|
|
268
|
+
if (!isObject(object) || typeof path !== "string" && !Array.isArray(path)) {
|
|
269
|
+
return object;
|
|
270
|
+
}
|
|
271
|
+
const root = object;
|
|
272
|
+
const pathArray = normalizePath(path);
|
|
273
|
+
if (pathArray.length === 0) {
|
|
274
|
+
return object;
|
|
275
|
+
}
|
|
276
|
+
for (let index = 0; index < pathArray.length; index++) {
|
|
277
|
+
const key = pathArray[index];
|
|
278
|
+
if (index === pathArray.length - 1) {
|
|
279
|
+
object[key] = value;
|
|
280
|
+
} else if (!isObject(object[key])) {
|
|
281
|
+
const nextKey = pathArray[index + 1];
|
|
282
|
+
const shouldCreateArray = typeof nextKey === "number";
|
|
283
|
+
object[key] = shouldCreateArray ? [] : {};
|
|
284
|
+
}
|
|
285
|
+
object = object[key];
|
|
286
|
+
}
|
|
287
|
+
return root;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const parseRecursive = (select) => {
|
|
291
|
+
const selectFields = {};
|
|
292
|
+
const fields = select.split(",");
|
|
293
|
+
fields.forEach((field) => {
|
|
294
|
+
setProperty(selectFields, field, true);
|
|
295
|
+
});
|
|
296
|
+
return selectFields;
|
|
297
|
+
};
|
|
298
|
+
const parseWhere = (where) => {
|
|
299
|
+
const whereObject = JSON.parse(where);
|
|
300
|
+
const parsed = {};
|
|
301
|
+
Object.keys(whereObject).forEach((key) => {
|
|
302
|
+
setProperty(parsed, key, whereObject[key]);
|
|
303
|
+
});
|
|
304
|
+
return parsed;
|
|
305
|
+
};
|
|
306
|
+
const parseOrderBy = (orderBy) => {
|
|
307
|
+
const parsed = {};
|
|
308
|
+
const orderByObject = JSON.parse(orderBy);
|
|
309
|
+
if (Object.keys(orderByObject).length > 0) {
|
|
310
|
+
const key = Object.keys(orderByObject)[0];
|
|
311
|
+
if (orderByObject[key] === "$asc" || orderByObject[key] === "$desc") {
|
|
312
|
+
parsed[key] = orderByObject[key];
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (Object.keys(parsed).length !== 1) {
|
|
316
|
+
throw new Error("orderBy needs to be an object with exactly 1 property with either $asc or $desc value");
|
|
317
|
+
}
|
|
318
|
+
return parsed;
|
|
319
|
+
};
|
|
320
|
+
const parseQuery = (url) => {
|
|
321
|
+
if (url) {
|
|
322
|
+
const { searchParams } = new node_url.URL(url);
|
|
323
|
+
const parsedQuery = {};
|
|
324
|
+
if (searchParams.get("select")) {
|
|
325
|
+
parsedQuery.select = parseRecursive(searchParams.get("select"));
|
|
326
|
+
}
|
|
327
|
+
if (searchParams.get("include")) {
|
|
328
|
+
parsedQuery.include = parseRecursive(searchParams.get("include"));
|
|
329
|
+
}
|
|
330
|
+
if (searchParams.get("where")) {
|
|
331
|
+
parsedQuery.where = parseWhere(searchParams.get("where"));
|
|
332
|
+
}
|
|
333
|
+
if (searchParams.get("orderBy")) {
|
|
334
|
+
parsedQuery.orderBy = parseOrderBy(searchParams.get("orderBy"));
|
|
335
|
+
}
|
|
336
|
+
if (searchParams.has("limit")) {
|
|
337
|
+
parsedQuery.limit = Number.isFinite(+searchParams.get("limit")) ? +searchParams.get("limit") : void 0;
|
|
338
|
+
}
|
|
339
|
+
if (searchParams.has("skip")) {
|
|
340
|
+
parsedQuery.skip = Number.isFinite(+searchParams.get("skip")) ? +searchParams.get("skip") : void 0;
|
|
341
|
+
}
|
|
342
|
+
if (searchParams.get("distinct")) {
|
|
343
|
+
parsedQuery.distinct = searchParams.get("distinct");
|
|
344
|
+
}
|
|
345
|
+
if (searchParams.get("page")) {
|
|
346
|
+
parsedQuery.page = Number.isFinite(+searchParams.get("page")) ? +searchParams.get("page") : void 0;
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
originalQuery: Object.fromEntries(searchParams.entries()),
|
|
350
|
+
...parsedQuery
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
return {};
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const formatResourceId = (resourceId) => Number.isSafeInteger(+resourceId) ? +resourceId : resourceId;
|
|
357
|
+
|
|
358
|
+
const ensureCamelCase = (string_) => `${string_.charAt(0).toLowerCase()}${string_.slice(1)}`;
|
|
359
|
+
const getResourceNameFromUrl = (url, models) => {
|
|
360
|
+
const realPath = url.split("?")[0];
|
|
361
|
+
if (realPath === void 0) {
|
|
362
|
+
throw new TypeError("Path is undefined");
|
|
363
|
+
}
|
|
364
|
+
const modelName = Object.keys(models).find((name) => {
|
|
365
|
+
const routeName = models[name];
|
|
366
|
+
const camelCaseModel = ensureCamelCase(routeName);
|
|
367
|
+
return new RegExp(`(${routeName}|${camelCaseModel}$)|(${routeName}|${camelCaseModel}/)`, "g").test(realPath);
|
|
368
|
+
});
|
|
369
|
+
if (modelName === void 0) {
|
|
370
|
+
throw new Error(`Couldn't find model ${modelName} name for url ${url}`);
|
|
371
|
+
}
|
|
372
|
+
return {
|
|
373
|
+
modelName,
|
|
374
|
+
resourceName: models[modelName]
|
|
375
|
+
};
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
const getRouteType = (method, url, resourceName) => {
|
|
379
|
+
const realPath = url.split("?")[0];
|
|
380
|
+
if (realPath === void 0) {
|
|
381
|
+
throw new TypeError("Path is undefined");
|
|
382
|
+
}
|
|
383
|
+
if (!realPath.includes(`/${resourceName}`)) {
|
|
384
|
+
throw new Error(`invalid resource name '${resourceName}' for route '${realPath}'`);
|
|
385
|
+
}
|
|
386
|
+
const entityMatcher = pathToRegexp.match(`/*placeholder/${resourceName}{/:id}`, { decode: decodeURIComponent });
|
|
387
|
+
const simpleMatcher = pathToRegexp.match(`/*placeholder/${resourceName}`, {
|
|
388
|
+
decode: decodeURIComponent
|
|
389
|
+
});
|
|
390
|
+
switch (method) {
|
|
391
|
+
case "DELETE": {
|
|
392
|
+
const pathMatch = entityMatcher(realPath);
|
|
393
|
+
if (typeof pathMatch === "object" && pathMatch.params.id) {
|
|
394
|
+
return {
|
|
395
|
+
resourceId: pathMatch.params.id,
|
|
396
|
+
routeType: RouteType.RouteType.DELETE
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
routeType: null
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
case "GET": {
|
|
404
|
+
const pathMatch = entityMatcher(realPath);
|
|
405
|
+
console.log(pathMatch);
|
|
406
|
+
if (typeof pathMatch === "object" && pathMatch.params?.id) {
|
|
407
|
+
return {
|
|
408
|
+
resourceId: pathMatch.params.id,
|
|
409
|
+
routeType: RouteType.RouteType.READ_ONE
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
return {
|
|
413
|
+
routeType: RouteType.RouteType.READ_ALL
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
case "PATCH":
|
|
417
|
+
case "PUT": {
|
|
418
|
+
const pathMatch = entityMatcher(realPath);
|
|
419
|
+
if (typeof pathMatch === "object" && pathMatch.params.id) {
|
|
420
|
+
return {
|
|
421
|
+
resourceId: pathMatch.params.id,
|
|
422
|
+
routeType: RouteType.RouteType.UPDATE
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
return {
|
|
426
|
+
routeType: null
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
case "POST": {
|
|
430
|
+
const pathMatch = simpleMatcher(realPath);
|
|
431
|
+
if (pathMatch) {
|
|
432
|
+
return {
|
|
433
|
+
routeType: RouteType.RouteType.CREATE
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
return {
|
|
437
|
+
routeType: null
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
default: {
|
|
441
|
+
return {
|
|
442
|
+
routeType: null
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
const adapterMethods = ["create", "delete", "getAll", "getOne", "parseQuery", "update", "getPaginationData", "getModels"];
|
|
449
|
+
const validateAdapterMethods = (adapter) => {
|
|
450
|
+
adapterMethods.forEach((method) => {
|
|
451
|
+
if (!adapter[method]) {
|
|
452
|
+
throw createHttpError__default(500, `Adapter must implement the "${method}" method.`);
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
async function baseHandler(responseExecutor, finalExecutor, adapter, options) {
|
|
458
|
+
try {
|
|
459
|
+
validateAdapterMethods(adapter);
|
|
460
|
+
} catch (error_) {
|
|
461
|
+
const error = error_;
|
|
462
|
+
throw new index_js.ApiError(error.statusCode, error.message);
|
|
463
|
+
}
|
|
464
|
+
await adapter.init?.();
|
|
465
|
+
const config = {
|
|
466
|
+
formatResourceId,
|
|
467
|
+
pagination: {
|
|
468
|
+
perPage: 20
|
|
469
|
+
},
|
|
470
|
+
...options
|
|
471
|
+
};
|
|
472
|
+
const routeNames = await adapter.mapModelsToRouteNames?.();
|
|
473
|
+
const modelRoutes = {};
|
|
474
|
+
adapter.getModels().forEach((modelName) => {
|
|
475
|
+
modelRoutes[modelName] = config.models?.[modelName]?.name ?? routeNames?.[modelName] ?? modelName;
|
|
476
|
+
});
|
|
477
|
+
return async (request, responseOrContext) => {
|
|
478
|
+
const { modelName, resourceName } = getResourceNameFromUrl(request.url, modelRoutes);
|
|
479
|
+
if (!resourceName) {
|
|
480
|
+
{
|
|
481
|
+
const mappedModels = await adapter.mapModelsToRouteNames?.();
|
|
482
|
+
if (typeof mappedModels === "object") {
|
|
483
|
+
throw createHttpError__default(404, `Resource not found, possible models: ${Object.values(mappedModels).join(", ")}`);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
throw createHttpError__default(404, `Resource not found: ${request.url}`);
|
|
487
|
+
}
|
|
488
|
+
const { resourceId, routeType } = getRouteType(request.method, request.url, resourceName);
|
|
489
|
+
if (routeType === null) {
|
|
490
|
+
throw createHttpError__default(404, `Route not found: ${request.url}`);
|
|
491
|
+
}
|
|
492
|
+
const modelConfig = options?.models?.[modelName];
|
|
493
|
+
const accessibleRoutes = getAccessibleRoutes.getAccessibleRoutes(modelConfig?.only, modelConfig?.exclude, options?.exposeStrategy ?? "all");
|
|
494
|
+
if (!accessibleRoutes.includes(routeType)) {
|
|
495
|
+
throw createHttpError__default(404, `Route not found: ${request.url}`);
|
|
496
|
+
}
|
|
497
|
+
try {
|
|
498
|
+
const resourceIdFormatted = modelConfig?.formatResourceId?.(resourceId) ?? config.formatResourceId(resourceId);
|
|
499
|
+
await adapter.connect?.();
|
|
500
|
+
const parsedQuery = parseQuery(`https://${request.headers.host?.replace(/\/$/u, "")}/${request.url}`);
|
|
501
|
+
const parameters = {
|
|
502
|
+
adapter,
|
|
503
|
+
query: adapter.parseQuery(modelName, parsedQuery),
|
|
504
|
+
resourceName: modelName
|
|
505
|
+
};
|
|
506
|
+
try {
|
|
507
|
+
let responseConfig;
|
|
508
|
+
switch (routeType) {
|
|
509
|
+
case RouteType.RouteType.CREATE: {
|
|
510
|
+
responseConfig = await (config.handlers?.create ?? createHandler)({
|
|
511
|
+
...parameters,
|
|
512
|
+
request
|
|
513
|
+
});
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
case RouteType.RouteType.DELETE: {
|
|
517
|
+
responseConfig = await (config.handlers?.delete ?? deleteHandler)({
|
|
518
|
+
...parameters,
|
|
519
|
+
resourceId: resourceIdFormatted
|
|
520
|
+
});
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
case RouteType.RouteType.READ_ALL: {
|
|
524
|
+
responseConfig = await (config.handlers?.list ?? listHandler)({
|
|
525
|
+
...parameters,
|
|
526
|
+
pagination: config.pagination,
|
|
527
|
+
query: {
|
|
528
|
+
...parameters.query,
|
|
529
|
+
limit: parsedQuery.limit ? Number(parsedQuery.limit) : void 0,
|
|
530
|
+
page: parsedQuery.page ? Number(parsedQuery.page) : void 0
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
case RouteType.RouteType.READ_ONE: {
|
|
536
|
+
responseConfig = await (config.handlers?.get ?? readHandler)({
|
|
537
|
+
...parameters,
|
|
538
|
+
resourceId: resourceIdFormatted
|
|
539
|
+
});
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
case RouteType.RouteType.UPDATE: {
|
|
543
|
+
responseConfig = await (config.handlers?.update ?? updateHandler)({
|
|
544
|
+
...parameters,
|
|
545
|
+
request,
|
|
546
|
+
resourceId: resourceIdFormatted
|
|
547
|
+
});
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
550
|
+
default: {
|
|
551
|
+
responseConfig = {
|
|
552
|
+
data: "Method not found",
|
|
553
|
+
status: 404
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
await responseExecutor(responseOrContext, responseConfig);
|
|
558
|
+
} catch (error) {
|
|
559
|
+
if (adapter.handleError && !(error instanceof index_js.ApiError)) {
|
|
560
|
+
adapter.handleError(error);
|
|
561
|
+
} else {
|
|
562
|
+
throw error;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
} finally {
|
|
566
|
+
await adapter.disconnect?.();
|
|
567
|
+
await finalExecutor(responseOrContext);
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
exports.baseHandler = baseHandler;
|