omni-rest 0.3.3 → 0.4.2
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/dist/adapters/express.d.mts +1 -1
- package/dist/adapters/express.d.ts +1 -1
- package/dist/adapters/express.js +51 -9
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +51 -9
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/fastify.d.mts +1 -1
- package/dist/adapters/fastify.d.ts +1 -1
- package/dist/adapters/fastify.js +51 -9
- package/dist/adapters/fastify.js.map +1 -1
- package/dist/adapters/fastify.mjs +51 -9
- package/dist/adapters/fastify.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +1 -1
- package/dist/adapters/nextjs.d.ts +1 -1
- package/dist/adapters/nextjs.js +51 -9
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +51 -9
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/cli.js +75 -2
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +75 -2
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +22 -5
- package/dist/index.d.ts +22 -5
- package/dist/index.js +114 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +114 -10
- package/dist/index.mjs.map +1 -1
- package/dist/{types-jvppYvku.d.mts → types-Dhk8VaWX.d.mts} +12 -0
- package/dist/{types-jvppYvku.d.ts → types-Dhk8VaWX.d.ts} +12 -0
- package/frontend-next/form-generator.tsx +52 -59
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -77,6 +77,19 @@ function buildModelMap(models, allowList) {
|
|
|
77
77
|
const filtered = allowList ? models.filter((m) => allowList.includes(m.routeName)) : models;
|
|
78
78
|
return Object.fromEntries(filtered.map((m) => [m.routeName, m]));
|
|
79
79
|
}
|
|
80
|
+
function detectSoftDeleteField(fields, explicitField) {
|
|
81
|
+
if (explicitField) {
|
|
82
|
+
const f = fields.find((f2) => f2.name === explicitField);
|
|
83
|
+
if (!f) return null;
|
|
84
|
+
const value = f.type === "Boolean" ? false : /* @__PURE__ */ new Date();
|
|
85
|
+
return { field: explicitField, value };
|
|
86
|
+
}
|
|
87
|
+
const deletedAt = fields.find((f) => f.name === "deletedAt" && f.type === "DateTime");
|
|
88
|
+
if (deletedAt) return { field: "deletedAt", value: /* @__PURE__ */ new Date() };
|
|
89
|
+
const isActive = fields.find((f) => f.name === "isActive" && f.type === "Boolean");
|
|
90
|
+
if (isActive) return { field: "isActive", value: false };
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
80
93
|
function getDelegate(prisma, meta) {
|
|
81
94
|
const key = meta.name.charAt(0).toLowerCase() + meta.name.slice(1);
|
|
82
95
|
const delegate = prisma[key];
|
|
@@ -108,9 +121,11 @@ var RESERVED_KEYS = /* @__PURE__ */ new Set([
|
|
|
108
121
|
"limit",
|
|
109
122
|
"sort",
|
|
110
123
|
"include",
|
|
111
|
-
"select"
|
|
124
|
+
"select",
|
|
125
|
+
"fields",
|
|
126
|
+
"search"
|
|
112
127
|
]);
|
|
113
|
-
function buildQuery(searchParams, defaultLimit = 20, maxLimit = 100) {
|
|
128
|
+
function buildQuery(searchParams, defaultLimit = 20, maxLimit = 100, modelFields) {
|
|
114
129
|
const where = {};
|
|
115
130
|
const orderBy = {};
|
|
116
131
|
let include = {};
|
|
@@ -134,13 +149,25 @@ function buildQuery(searchParams, defaultLimit = 20, maxLimit = 100) {
|
|
|
134
149
|
if (rel.trim()) include[rel.trim()] = true;
|
|
135
150
|
}
|
|
136
151
|
}
|
|
137
|
-
const selectParam = searchParams.get("select");
|
|
152
|
+
const selectParam = searchParams.get("select") ?? searchParams.get("fields");
|
|
138
153
|
if (selectParam) {
|
|
139
154
|
select = {};
|
|
140
155
|
for (const field of selectParam.split(",")) {
|
|
141
156
|
if (field.trim()) select[field.trim()] = true;
|
|
142
157
|
}
|
|
143
158
|
}
|
|
159
|
+
const searchValue = searchParams.get("search");
|
|
160
|
+
if (searchValue && modelFields) {
|
|
161
|
+
const stringFields = modelFields.filter(
|
|
162
|
+
(f) => f.type === "String" && !f.isRelation
|
|
163
|
+
);
|
|
164
|
+
if (stringFields.length > 0) {
|
|
165
|
+
const orClauses = stringFields.map((f) => ({
|
|
166
|
+
[f.name]: { contains: searchValue, mode: "insensitive" }
|
|
167
|
+
}));
|
|
168
|
+
where["OR"] = orClauses;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
144
171
|
for (const [key, value] of searchParams.entries()) {
|
|
145
172
|
if (RESERVED_KEYS.has(key)) continue;
|
|
146
173
|
let matched = false;
|
|
@@ -202,7 +229,9 @@ function createRouter(prisma, options = {}) {
|
|
|
202
229
|
beforeOperation,
|
|
203
230
|
afterOperation,
|
|
204
231
|
defaultLimit = 20,
|
|
205
|
-
maxLimit = 100
|
|
232
|
+
maxLimit = 100,
|
|
233
|
+
softDelete = false,
|
|
234
|
+
softDeleteField
|
|
206
235
|
} = options;
|
|
207
236
|
const models = getModels(prisma);
|
|
208
237
|
const modelMap = buildModelMap(models, allow);
|
|
@@ -236,7 +265,9 @@ function createRouter(prisma, options = {}) {
|
|
|
236
265
|
searchParams,
|
|
237
266
|
defaultLimit,
|
|
238
267
|
maxLimit,
|
|
239
|
-
operation
|
|
268
|
+
operation,
|
|
269
|
+
softDelete,
|
|
270
|
+
softDeleteField
|
|
240
271
|
);
|
|
241
272
|
} catch (e) {
|
|
242
273
|
return handlePrismaError(e);
|
|
@@ -252,12 +283,13 @@ function createRouter(prisma, options = {}) {
|
|
|
252
283
|
}
|
|
253
284
|
return { handle, modelMap, models };
|
|
254
285
|
}
|
|
255
|
-
async function executeOperation(prisma, meta, method, id, body, searchParams, defaultLimit, maxLimit, operation) {
|
|
286
|
+
async function executeOperation(prisma, meta, method, id, body, searchParams, defaultLimit, maxLimit, operation, softDelete = false, softDeleteField) {
|
|
256
287
|
const delegate = getDelegate(prisma, meta);
|
|
257
288
|
const { where, orderBy, skip, take, include, select } = buildQuery(
|
|
258
289
|
searchParams,
|
|
259
290
|
defaultLimit,
|
|
260
|
-
maxLimit
|
|
291
|
+
maxLimit,
|
|
292
|
+
meta.fields
|
|
261
293
|
);
|
|
262
294
|
const includeArg = Object.keys(include).length > 0 ? include : void 0;
|
|
263
295
|
const selectArg = select && Object.keys(select).length > 0 ? select : void 0;
|
|
@@ -320,9 +352,11 @@ async function executeOperation(prisma, meta, method, id, body, searchParams, de
|
|
|
320
352
|
};
|
|
321
353
|
}
|
|
322
354
|
if (method === "GET" && !id) {
|
|
355
|
+
const softDeleteInfo = softDelete ? detectSoftDeleteField(meta.fields, softDeleteField) : null;
|
|
356
|
+
const listWhere = softDeleteInfo ? { ...where, [softDeleteInfo.field]: softDeleteInfo.field === "isActive" ? true : null } : where;
|
|
323
357
|
const [data, total] = await prisma.$transaction([
|
|
324
|
-
delegate.findMany({ where, orderBy, skip, take, ...projection }),
|
|
325
|
-
delegate.count({ where })
|
|
358
|
+
delegate.findMany({ where: listWhere, orderBy, skip, take, ...projection }),
|
|
359
|
+
delegate.count({ where: listWhere })
|
|
326
360
|
]);
|
|
327
361
|
return {
|
|
328
362
|
status: 200,
|
|
@@ -359,6 +393,14 @@ async function executeOperation(prisma, meta, method, id, body, searchParams, de
|
|
|
359
393
|
return { status: 200, data: record };
|
|
360
394
|
}
|
|
361
395
|
if (method === "DELETE" && id) {
|
|
396
|
+
const softDeleteInfo = softDelete ? detectSoftDeleteField(meta.fields, softDeleteField) : null;
|
|
397
|
+
if (softDeleteInfo) {
|
|
398
|
+
const record = await delegate.update({
|
|
399
|
+
where: { [meta.idField]: coerceId(id) },
|
|
400
|
+
data: { [softDeleteInfo.field]: softDeleteInfo.value }
|
|
401
|
+
});
|
|
402
|
+
return { status: 200, data: record };
|
|
403
|
+
}
|
|
362
404
|
await delegate.delete({
|
|
363
405
|
where: { [meta.idField]: coerceId(id) }
|
|
364
406
|
});
|
|
@@ -862,6 +904,68 @@ function buildListParameters() {
|
|
|
862
904
|
];
|
|
863
905
|
}
|
|
864
906
|
|
|
907
|
+
// src/config-generator.ts
|
|
908
|
+
function generateConfig(prisma) {
|
|
909
|
+
const models = getModels(prisma);
|
|
910
|
+
const PRISMA_TO_ZOD2 = {
|
|
911
|
+
String: "z.string()",
|
|
912
|
+
Int: "z.number().int()",
|
|
913
|
+
Float: "z.number()",
|
|
914
|
+
Decimal: "z.number()",
|
|
915
|
+
Boolean: "z.boolean()",
|
|
916
|
+
DateTime: "z.coerce.date()",
|
|
917
|
+
Json: "z.any()",
|
|
918
|
+
BigInt: "z.bigint()",
|
|
919
|
+
Bytes: "z.any()"
|
|
920
|
+
};
|
|
921
|
+
function fieldToZod2(field) {
|
|
922
|
+
if (field.isRelation) return null;
|
|
923
|
+
let zod = PRISMA_TO_ZOD2[field.type] ?? "z.any()";
|
|
924
|
+
if (!field.isRequired) zod = `${zod}.optional()`;
|
|
925
|
+
if (field.isList) zod = `z.array(${zod})`;
|
|
926
|
+
return zod;
|
|
927
|
+
}
|
|
928
|
+
const schemaBlocks = models.map((meta) => {
|
|
929
|
+
const createFields = meta.fields.filter((f) => !f.isRelation && !f.isId && !f.hasDefaultValue && !f.isUpdatedAt).map((f) => {
|
|
930
|
+
const z = fieldToZod2(f);
|
|
931
|
+
return z ? ` ${f.name}: ${z},` : null;
|
|
932
|
+
}).filter(Boolean).join("\n");
|
|
933
|
+
const updateFields = meta.fields.filter((f) => !f.isRelation && !f.isId && !f.isUpdatedAt).map((f) => {
|
|
934
|
+
const z = fieldToZod2(f);
|
|
935
|
+
if (!z) return null;
|
|
936
|
+
const opt = z.includes(".optional()") ? z : `${z}.optional()`;
|
|
937
|
+
return ` ${f.name}: ${opt},`;
|
|
938
|
+
}).filter(Boolean).join("\n");
|
|
939
|
+
return `
|
|
940
|
+
// \u2500\u2500\u2500 ${meta.name} \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
941
|
+
|
|
942
|
+
export const ${meta.name}CreateSchema = z.object({
|
|
943
|
+
${createFields}
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
export const ${meta.name}UpdateSchema = z.object({
|
|
947
|
+
${updateFields}
|
|
948
|
+
});
|
|
949
|
+
|
|
950
|
+
export type ${meta.name}Create = z.infer<typeof ${meta.name}CreateSchema>;
|
|
951
|
+
export type ${meta.name}Update = z.infer<typeof ${meta.name}UpdateSchema>;`.trim();
|
|
952
|
+
});
|
|
953
|
+
const zodSchemas = `/**
|
|
954
|
+
* Auto-generated Zod schemas from Prisma schema.
|
|
955
|
+
* Generated by omni-rest \u2014 do not edit manually.
|
|
956
|
+
*/
|
|
957
|
+
import { z } from "zod";
|
|
958
|
+
|
|
959
|
+
${schemaBlocks.join("\n\n")}
|
|
960
|
+
`;
|
|
961
|
+
return {
|
|
962
|
+
version: "1",
|
|
963
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
964
|
+
models,
|
|
965
|
+
zodSchemas
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
|
|
865
969
|
// src/adapters/express.ts
|
|
866
970
|
function expressAdapter(prisma, options = {}) {
|
|
867
971
|
const { Router } = __require("express");
|
|
@@ -1320,6 +1424,6 @@ function nestjsController(prisma, options = {}, prefix = "api") {
|
|
|
1320
1424
|
return OmniRestDynamicController;
|
|
1321
1425
|
}
|
|
1322
1426
|
|
|
1323
|
-
export { buildModelMap, buildQuery, buildRuntimeSchemas, createRouter, expressAdapter, fastifyAdapter, generateOpenApiSpec, generateZodSchemas, getDelegate, getModels, hapiAdapter, koaAdapter, nestjsController, nextjsAdapter, runGuard, runHook, toRouteName, validateBody, withValidation };
|
|
1427
|
+
export { buildModelMap, buildQuery, buildRuntimeSchemas, createRouter, expressAdapter, fastifyAdapter, generateConfig, generateOpenApiSpec, generateZodSchemas, getDelegate, getModels, hapiAdapter, koaAdapter, nestjsController, nextjsAdapter, runGuard, runHook, toRouteName, validateBody, withValidation };
|
|
1324
1428
|
//# sourceMappingURL=index.mjs.map
|
|
1325
1429
|
//# sourceMappingURL=index.mjs.map
|