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.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PrismaClient } from '@prisma/client';
|
|
2
|
-
import { P as PrismaRestOptions, R as RouterInstance, M as ModelMeta, a as ParsedQuery, G as GuardMap, H as HookFn, b as HookContext } from './types-
|
|
3
|
-
export {
|
|
2
|
+
import { P as PrismaRestOptions, R as RouterInstance, M as ModelMeta, F as FieldMeta, a as ParsedQuery, G as GuardMap, H as HookFn, b as HookContext } from './types-Dhk8VaWX.mjs';
|
|
3
|
+
export { c as GuardFn, d as HandlerResult } from './types-Dhk8VaWX.mjs';
|
|
4
4
|
export { expressAdapter } from './adapters/express.mjs';
|
|
5
5
|
export { nextjsAdapter } from './adapters/nextjs.mjs';
|
|
6
6
|
export { fastifyAdapter } from './adapters/fastify.mjs';
|
|
@@ -43,9 +43,10 @@ declare function getDelegate(prisma: any, meta: ModelMeta): any;
|
|
|
43
43
|
* Sorting → ?sort=createdAt:desc or ?sort=name:asc
|
|
44
44
|
* Pagination → ?page=2&limit=10
|
|
45
45
|
* Relations → ?include=posts,profile
|
|
46
|
-
* Fields → ?select=id,name,email
|
|
46
|
+
* Fields → ?select=id,name,email or ?fields=id,name,email
|
|
47
|
+
* Search → ?search=eng (queries all String fields with OR)
|
|
47
48
|
*/
|
|
48
|
-
declare function buildQuery(searchParams: URLSearchParams, defaultLimit?: number, maxLimit?: number): ParsedQuery;
|
|
49
|
+
declare function buildQuery(searchParams: URLSearchParams, defaultLimit?: number, maxLimit?: number, modelFields?: FieldMeta[]): ParsedQuery;
|
|
49
50
|
|
|
50
51
|
/**
|
|
51
52
|
* Runs the guard for the given model+method combo.
|
|
@@ -129,6 +130,22 @@ declare function generateOpenApiSpec(prisma: any, options?: {
|
|
|
129
130
|
}[];
|
|
130
131
|
}): object;
|
|
131
132
|
|
|
133
|
+
/**
|
|
134
|
+
* The shared config format written by the backend CLI and consumed by omni-rest-client.
|
|
135
|
+
* No Prisma dependency on the consumer side — just plain JSON.
|
|
136
|
+
*/
|
|
137
|
+
interface OmniRestConfig {
|
|
138
|
+
version: string;
|
|
139
|
+
generatedAt: string;
|
|
140
|
+
models: ModelMeta[];
|
|
141
|
+
zodSchemas: string;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Generates the omni-rest.config.json content from a Prisma client instance.
|
|
145
|
+
* This is the bridge between the backend (Prisma) and the client CLI (no Prisma).
|
|
146
|
+
*/
|
|
147
|
+
declare function generateConfig(prisma: any): OmniRestConfig;
|
|
148
|
+
|
|
132
149
|
/**
|
|
133
150
|
* Koa adapter for omni-rest.
|
|
134
151
|
* Allows using omni-rest within a Koa application using @koa/router.
|
|
@@ -213,4 +230,4 @@ declare const hapiAdapter: {
|
|
|
213
230
|
*/
|
|
214
231
|
declare function nestjsController(prisma: PrismaClient, options?: PrismaRestOptions, prefix?: string): any;
|
|
215
232
|
|
|
216
|
-
export { GuardMap, HookContext, HookFn, ModelMeta, ParsedQuery, PrismaRestOptions, RouterInstance, buildModelMap, buildQuery, buildRuntimeSchemas, createRouter, generateOpenApiSpec, generateZodSchemas, getDelegate, getModels, hapiAdapter, koaAdapter, nestjsController, runGuard, runHook, toRouteName, validateBody, withValidation };
|
|
233
|
+
export { FieldMeta, GuardMap, HookContext, HookFn, ModelMeta, type OmniRestConfig, ParsedQuery, PrismaRestOptions, RouterInstance, buildModelMap, buildQuery, buildRuntimeSchemas, createRouter, generateConfig, generateOpenApiSpec, generateZodSchemas, getDelegate, getModels, hapiAdapter, koaAdapter, nestjsController, runGuard, runHook, toRouteName, validateBody, withValidation };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PrismaClient } from '@prisma/client';
|
|
2
|
-
import { P as PrismaRestOptions, R as RouterInstance, M as ModelMeta, a as ParsedQuery, G as GuardMap, H as HookFn, b as HookContext } from './types-
|
|
3
|
-
export {
|
|
2
|
+
import { P as PrismaRestOptions, R as RouterInstance, M as ModelMeta, F as FieldMeta, a as ParsedQuery, G as GuardMap, H as HookFn, b as HookContext } from './types-Dhk8VaWX.js';
|
|
3
|
+
export { c as GuardFn, d as HandlerResult } from './types-Dhk8VaWX.js';
|
|
4
4
|
export { expressAdapter } from './adapters/express.js';
|
|
5
5
|
export { nextjsAdapter } from './adapters/nextjs.js';
|
|
6
6
|
export { fastifyAdapter } from './adapters/fastify.js';
|
|
@@ -43,9 +43,10 @@ declare function getDelegate(prisma: any, meta: ModelMeta): any;
|
|
|
43
43
|
* Sorting → ?sort=createdAt:desc or ?sort=name:asc
|
|
44
44
|
* Pagination → ?page=2&limit=10
|
|
45
45
|
* Relations → ?include=posts,profile
|
|
46
|
-
* Fields → ?select=id,name,email
|
|
46
|
+
* Fields → ?select=id,name,email or ?fields=id,name,email
|
|
47
|
+
* Search → ?search=eng (queries all String fields with OR)
|
|
47
48
|
*/
|
|
48
|
-
declare function buildQuery(searchParams: URLSearchParams, defaultLimit?: number, maxLimit?: number): ParsedQuery;
|
|
49
|
+
declare function buildQuery(searchParams: URLSearchParams, defaultLimit?: number, maxLimit?: number, modelFields?: FieldMeta[]): ParsedQuery;
|
|
49
50
|
|
|
50
51
|
/**
|
|
51
52
|
* Runs the guard for the given model+method combo.
|
|
@@ -129,6 +130,22 @@ declare function generateOpenApiSpec(prisma: any, options?: {
|
|
|
129
130
|
}[];
|
|
130
131
|
}): object;
|
|
131
132
|
|
|
133
|
+
/**
|
|
134
|
+
* The shared config format written by the backend CLI and consumed by omni-rest-client.
|
|
135
|
+
* No Prisma dependency on the consumer side — just plain JSON.
|
|
136
|
+
*/
|
|
137
|
+
interface OmniRestConfig {
|
|
138
|
+
version: string;
|
|
139
|
+
generatedAt: string;
|
|
140
|
+
models: ModelMeta[];
|
|
141
|
+
zodSchemas: string;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Generates the omni-rest.config.json content from a Prisma client instance.
|
|
145
|
+
* This is the bridge between the backend (Prisma) and the client CLI (no Prisma).
|
|
146
|
+
*/
|
|
147
|
+
declare function generateConfig(prisma: any): OmniRestConfig;
|
|
148
|
+
|
|
132
149
|
/**
|
|
133
150
|
* Koa adapter for omni-rest.
|
|
134
151
|
* Allows using omni-rest within a Koa application using @koa/router.
|
|
@@ -213,4 +230,4 @@ declare const hapiAdapter: {
|
|
|
213
230
|
*/
|
|
214
231
|
declare function nestjsController(prisma: PrismaClient, options?: PrismaRestOptions, prefix?: string): any;
|
|
215
232
|
|
|
216
|
-
export { GuardMap, HookContext, HookFn, ModelMeta, ParsedQuery, PrismaRestOptions, RouterInstance, buildModelMap, buildQuery, buildRuntimeSchemas, createRouter, generateOpenApiSpec, generateZodSchemas, getDelegate, getModels, hapiAdapter, koaAdapter, nestjsController, runGuard, runHook, toRouteName, validateBody, withValidation };
|
|
233
|
+
export { FieldMeta, GuardMap, HookContext, HookFn, ModelMeta, type OmniRestConfig, ParsedQuery, PrismaRestOptions, RouterInstance, buildModelMap, buildQuery, buildRuntimeSchemas, createRouter, generateConfig, generateOpenApiSpec, generateZodSchemas, getDelegate, getModels, hapiAdapter, koaAdapter, nestjsController, runGuard, runHook, toRouteName, validateBody, withValidation };
|
package/dist/index.js
CHANGED
|
@@ -79,6 +79,19 @@ function buildModelMap(models, allowList) {
|
|
|
79
79
|
const filtered = allowList ? models.filter((m) => allowList.includes(m.routeName)) : models;
|
|
80
80
|
return Object.fromEntries(filtered.map((m) => [m.routeName, m]));
|
|
81
81
|
}
|
|
82
|
+
function detectSoftDeleteField(fields, explicitField) {
|
|
83
|
+
if (explicitField) {
|
|
84
|
+
const f = fields.find((f2) => f2.name === explicitField);
|
|
85
|
+
if (!f) return null;
|
|
86
|
+
const value = f.type === "Boolean" ? false : /* @__PURE__ */ new Date();
|
|
87
|
+
return { field: explicitField, value };
|
|
88
|
+
}
|
|
89
|
+
const deletedAt = fields.find((f) => f.name === "deletedAt" && f.type === "DateTime");
|
|
90
|
+
if (deletedAt) return { field: "deletedAt", value: /* @__PURE__ */ new Date() };
|
|
91
|
+
const isActive = fields.find((f) => f.name === "isActive" && f.type === "Boolean");
|
|
92
|
+
if (isActive) return { field: "isActive", value: false };
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
82
95
|
function getDelegate(prisma, meta) {
|
|
83
96
|
const key = meta.name.charAt(0).toLowerCase() + meta.name.slice(1);
|
|
84
97
|
const delegate = prisma[key];
|
|
@@ -110,9 +123,11 @@ var RESERVED_KEYS = /* @__PURE__ */ new Set([
|
|
|
110
123
|
"limit",
|
|
111
124
|
"sort",
|
|
112
125
|
"include",
|
|
113
|
-
"select"
|
|
126
|
+
"select",
|
|
127
|
+
"fields",
|
|
128
|
+
"search"
|
|
114
129
|
]);
|
|
115
|
-
function buildQuery(searchParams, defaultLimit = 20, maxLimit = 100) {
|
|
130
|
+
function buildQuery(searchParams, defaultLimit = 20, maxLimit = 100, modelFields) {
|
|
116
131
|
const where = {};
|
|
117
132
|
const orderBy = {};
|
|
118
133
|
let include = {};
|
|
@@ -136,13 +151,25 @@ function buildQuery(searchParams, defaultLimit = 20, maxLimit = 100) {
|
|
|
136
151
|
if (rel.trim()) include[rel.trim()] = true;
|
|
137
152
|
}
|
|
138
153
|
}
|
|
139
|
-
const selectParam = searchParams.get("select");
|
|
154
|
+
const selectParam = searchParams.get("select") ?? searchParams.get("fields");
|
|
140
155
|
if (selectParam) {
|
|
141
156
|
select = {};
|
|
142
157
|
for (const field of selectParam.split(",")) {
|
|
143
158
|
if (field.trim()) select[field.trim()] = true;
|
|
144
159
|
}
|
|
145
160
|
}
|
|
161
|
+
const searchValue = searchParams.get("search");
|
|
162
|
+
if (searchValue && modelFields) {
|
|
163
|
+
const stringFields = modelFields.filter(
|
|
164
|
+
(f) => f.type === "String" && !f.isRelation
|
|
165
|
+
);
|
|
166
|
+
if (stringFields.length > 0) {
|
|
167
|
+
const orClauses = stringFields.map((f) => ({
|
|
168
|
+
[f.name]: { contains: searchValue, mode: "insensitive" }
|
|
169
|
+
}));
|
|
170
|
+
where["OR"] = orClauses;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
146
173
|
for (const [key, value] of searchParams.entries()) {
|
|
147
174
|
if (RESERVED_KEYS.has(key)) continue;
|
|
148
175
|
let matched = false;
|
|
@@ -204,7 +231,9 @@ function createRouter(prisma, options = {}) {
|
|
|
204
231
|
beforeOperation,
|
|
205
232
|
afterOperation,
|
|
206
233
|
defaultLimit = 20,
|
|
207
|
-
maxLimit = 100
|
|
234
|
+
maxLimit = 100,
|
|
235
|
+
softDelete = false,
|
|
236
|
+
softDeleteField
|
|
208
237
|
} = options;
|
|
209
238
|
const models = getModels(prisma);
|
|
210
239
|
const modelMap = buildModelMap(models, allow);
|
|
@@ -238,7 +267,9 @@ function createRouter(prisma, options = {}) {
|
|
|
238
267
|
searchParams,
|
|
239
268
|
defaultLimit,
|
|
240
269
|
maxLimit,
|
|
241
|
-
operation
|
|
270
|
+
operation,
|
|
271
|
+
softDelete,
|
|
272
|
+
softDeleteField
|
|
242
273
|
);
|
|
243
274
|
} catch (e) {
|
|
244
275
|
return handlePrismaError(e);
|
|
@@ -254,12 +285,13 @@ function createRouter(prisma, options = {}) {
|
|
|
254
285
|
}
|
|
255
286
|
return { handle, modelMap, models };
|
|
256
287
|
}
|
|
257
|
-
async function executeOperation(prisma, meta, method, id, body, searchParams, defaultLimit, maxLimit, operation) {
|
|
288
|
+
async function executeOperation(prisma, meta, method, id, body, searchParams, defaultLimit, maxLimit, operation, softDelete = false, softDeleteField) {
|
|
258
289
|
const delegate = getDelegate(prisma, meta);
|
|
259
290
|
const { where, orderBy, skip, take, include, select } = buildQuery(
|
|
260
291
|
searchParams,
|
|
261
292
|
defaultLimit,
|
|
262
|
-
maxLimit
|
|
293
|
+
maxLimit,
|
|
294
|
+
meta.fields
|
|
263
295
|
);
|
|
264
296
|
const includeArg = Object.keys(include).length > 0 ? include : void 0;
|
|
265
297
|
const selectArg = select && Object.keys(select).length > 0 ? select : void 0;
|
|
@@ -322,9 +354,11 @@ async function executeOperation(prisma, meta, method, id, body, searchParams, de
|
|
|
322
354
|
};
|
|
323
355
|
}
|
|
324
356
|
if (method === "GET" && !id) {
|
|
357
|
+
const softDeleteInfo = softDelete ? detectSoftDeleteField(meta.fields, softDeleteField) : null;
|
|
358
|
+
const listWhere = softDeleteInfo ? { ...where, [softDeleteInfo.field]: softDeleteInfo.field === "isActive" ? true : null } : where;
|
|
325
359
|
const [data, total] = await prisma.$transaction([
|
|
326
|
-
delegate.findMany({ where, orderBy, skip, take, ...projection }),
|
|
327
|
-
delegate.count({ where })
|
|
360
|
+
delegate.findMany({ where: listWhere, orderBy, skip, take, ...projection }),
|
|
361
|
+
delegate.count({ where: listWhere })
|
|
328
362
|
]);
|
|
329
363
|
return {
|
|
330
364
|
status: 200,
|
|
@@ -361,6 +395,14 @@ async function executeOperation(prisma, meta, method, id, body, searchParams, de
|
|
|
361
395
|
return { status: 200, data: record };
|
|
362
396
|
}
|
|
363
397
|
if (method === "DELETE" && id) {
|
|
398
|
+
const softDeleteInfo = softDelete ? detectSoftDeleteField(meta.fields, softDeleteField) : null;
|
|
399
|
+
if (softDeleteInfo) {
|
|
400
|
+
const record = await delegate.update({
|
|
401
|
+
where: { [meta.idField]: coerceId(id) },
|
|
402
|
+
data: { [softDeleteInfo.field]: softDeleteInfo.value }
|
|
403
|
+
});
|
|
404
|
+
return { status: 200, data: record };
|
|
405
|
+
}
|
|
364
406
|
await delegate.delete({
|
|
365
407
|
where: { [meta.idField]: coerceId(id) }
|
|
366
408
|
});
|
|
@@ -864,6 +906,68 @@ function buildListParameters() {
|
|
|
864
906
|
];
|
|
865
907
|
}
|
|
866
908
|
|
|
909
|
+
// src/config-generator.ts
|
|
910
|
+
function generateConfig(prisma) {
|
|
911
|
+
const models = getModels(prisma);
|
|
912
|
+
const PRISMA_TO_ZOD2 = {
|
|
913
|
+
String: "z.string()",
|
|
914
|
+
Int: "z.number().int()",
|
|
915
|
+
Float: "z.number()",
|
|
916
|
+
Decimal: "z.number()",
|
|
917
|
+
Boolean: "z.boolean()",
|
|
918
|
+
DateTime: "z.coerce.date()",
|
|
919
|
+
Json: "z.any()",
|
|
920
|
+
BigInt: "z.bigint()",
|
|
921
|
+
Bytes: "z.any()"
|
|
922
|
+
};
|
|
923
|
+
function fieldToZod2(field) {
|
|
924
|
+
if (field.isRelation) return null;
|
|
925
|
+
let zod = PRISMA_TO_ZOD2[field.type] ?? "z.any()";
|
|
926
|
+
if (!field.isRequired) zod = `${zod}.optional()`;
|
|
927
|
+
if (field.isList) zod = `z.array(${zod})`;
|
|
928
|
+
return zod;
|
|
929
|
+
}
|
|
930
|
+
const schemaBlocks = models.map((meta) => {
|
|
931
|
+
const createFields = meta.fields.filter((f) => !f.isRelation && !f.isId && !f.hasDefaultValue && !f.isUpdatedAt).map((f) => {
|
|
932
|
+
const z = fieldToZod2(f);
|
|
933
|
+
return z ? ` ${f.name}: ${z},` : null;
|
|
934
|
+
}).filter(Boolean).join("\n");
|
|
935
|
+
const updateFields = meta.fields.filter((f) => !f.isRelation && !f.isId && !f.isUpdatedAt).map((f) => {
|
|
936
|
+
const z = fieldToZod2(f);
|
|
937
|
+
if (!z) return null;
|
|
938
|
+
const opt = z.includes(".optional()") ? z : `${z}.optional()`;
|
|
939
|
+
return ` ${f.name}: ${opt},`;
|
|
940
|
+
}).filter(Boolean).join("\n");
|
|
941
|
+
return `
|
|
942
|
+
// \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
|
|
943
|
+
|
|
944
|
+
export const ${meta.name}CreateSchema = z.object({
|
|
945
|
+
${createFields}
|
|
946
|
+
});
|
|
947
|
+
|
|
948
|
+
export const ${meta.name}UpdateSchema = z.object({
|
|
949
|
+
${updateFields}
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
export type ${meta.name}Create = z.infer<typeof ${meta.name}CreateSchema>;
|
|
953
|
+
export type ${meta.name}Update = z.infer<typeof ${meta.name}UpdateSchema>;`.trim();
|
|
954
|
+
});
|
|
955
|
+
const zodSchemas = `/**
|
|
956
|
+
* Auto-generated Zod schemas from Prisma schema.
|
|
957
|
+
* Generated by omni-rest \u2014 do not edit manually.
|
|
958
|
+
*/
|
|
959
|
+
import { z } from "zod";
|
|
960
|
+
|
|
961
|
+
${schemaBlocks.join("\n\n")}
|
|
962
|
+
`;
|
|
963
|
+
return {
|
|
964
|
+
version: "1",
|
|
965
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
966
|
+
models,
|
|
967
|
+
zodSchemas
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
|
|
867
971
|
// src/adapters/express.ts
|
|
868
972
|
function expressAdapter(prisma, options = {}) {
|
|
869
973
|
const { Router } = __require("express");
|
|
@@ -1328,6 +1432,7 @@ exports.buildRuntimeSchemas = buildRuntimeSchemas;
|
|
|
1328
1432
|
exports.createRouter = createRouter;
|
|
1329
1433
|
exports.expressAdapter = expressAdapter;
|
|
1330
1434
|
exports.fastifyAdapter = fastifyAdapter;
|
|
1435
|
+
exports.generateConfig = generateConfig;
|
|
1331
1436
|
exports.generateOpenApiSpec = generateOpenApiSpec;
|
|
1332
1437
|
exports.generateZodSchemas = generateZodSchemas;
|
|
1333
1438
|
exports.getDelegate = getDelegate;
|