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