prisma-safe-ai-query 1.0.5 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +506 -125
- package/dist/agent.d.ts +73 -7
- package/dist/agent.js +334 -10
- package/dist/engine.d.ts +33 -6
- package/dist/engine.js +363 -43
- package/dist/index.d.ts +4 -3
- package/dist/index.js +7 -1
- package/dist/introspect.d.ts +42 -0
- package/dist/introspect.js +270 -0
- package/dist/schema.d.ts +26 -36
- package/dist/schema.js +93 -10
- package/package.json +40 -35
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.findPrismaSchemaPath = findPrismaSchemaPath;
|
|
37
|
+
exports.parsePrismaSchemaText = parsePrismaSchemaText;
|
|
38
|
+
exports.extractPrismaSchema = extractPrismaSchema;
|
|
39
|
+
exports.formatSchemaForAIContext = formatSchemaForAIContext;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
/**
|
|
43
|
+
* Searches default standard locations for schema.prisma if no explicit path is given.
|
|
44
|
+
*/
|
|
45
|
+
function findPrismaSchemaPath(customPath) {
|
|
46
|
+
if (customPath) {
|
|
47
|
+
const resolved = path.resolve(customPath);
|
|
48
|
+
if (fs.existsSync(resolved))
|
|
49
|
+
return resolved;
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const defaultLocations = [
|
|
53
|
+
path.resolve(process.cwd(), "prisma", "schema.prisma"),
|
|
54
|
+
path.resolve(process.cwd(), "schema.prisma"),
|
|
55
|
+
];
|
|
56
|
+
for (const loc of defaultLocations) {
|
|
57
|
+
if (fs.existsSync(loc))
|
|
58
|
+
return loc;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const SCALAR_TYPES = new Set([
|
|
63
|
+
"String",
|
|
64
|
+
"Int",
|
|
65
|
+
"Float",
|
|
66
|
+
"Boolean",
|
|
67
|
+
"DateTime",
|
|
68
|
+
"Json",
|
|
69
|
+
"Bytes",
|
|
70
|
+
"Decimal",
|
|
71
|
+
"BigInt",
|
|
72
|
+
]);
|
|
73
|
+
/**
|
|
74
|
+
* Parses raw schema.prisma contents into structured models, relations, foreign keys, and enums,
|
|
75
|
+
* filtering out datasource URLs, generators, comments, and private credentials.
|
|
76
|
+
*/
|
|
77
|
+
function parsePrismaSchemaText(schemaContent) {
|
|
78
|
+
const lines = schemaContent.split(/\r?\n/);
|
|
79
|
+
const models = [];
|
|
80
|
+
const enums = [];
|
|
81
|
+
let currentModel = null;
|
|
82
|
+
let currentEnum = null;
|
|
83
|
+
for (const rawLine of lines) {
|
|
84
|
+
const line = rawLine.replace(/\/\/.*$/, "").trim();
|
|
85
|
+
if (!line)
|
|
86
|
+
continue;
|
|
87
|
+
const modelStartMatch = line.match(/^model\s+([A-Za-z0-9_]+)\s*\{/);
|
|
88
|
+
if (modelStartMatch) {
|
|
89
|
+
currentModel = { name: modelStartMatch[1], fields: [] };
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const enumStartMatch = line.match(/^enum\s+([A-Za-z0-9_]+)\s*\{/);
|
|
93
|
+
if (enumStartMatch) {
|
|
94
|
+
currentEnum = { name: enumStartMatch[1], values: [] };
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (line === "}" || line.startsWith("}")) {
|
|
98
|
+
if (currentModel) {
|
|
99
|
+
models.push(currentModel);
|
|
100
|
+
currentModel = null;
|
|
101
|
+
}
|
|
102
|
+
if (currentEnum) {
|
|
103
|
+
enums.push(currentEnum);
|
|
104
|
+
currentEnum = null;
|
|
105
|
+
}
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (currentEnum) {
|
|
109
|
+
const enumVal = line.trim();
|
|
110
|
+
if (enumVal && /^[A-Za-z0-9_]+$/.test(enumVal)) {
|
|
111
|
+
currentEnum.values.push(enumVal);
|
|
112
|
+
}
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (currentModel) {
|
|
116
|
+
if (line.startsWith("@@"))
|
|
117
|
+
continue;
|
|
118
|
+
const tokens = line.split(/\s+/).filter(Boolean);
|
|
119
|
+
if (tokens.length >= 2) {
|
|
120
|
+
const fieldName = tokens[0];
|
|
121
|
+
const fieldType = tokens[1];
|
|
122
|
+
if (fieldName.startsWith("@"))
|
|
123
|
+
continue;
|
|
124
|
+
const isList = fieldType.endsWith("[]");
|
|
125
|
+
const isRequired = !fieldType.endsWith("?");
|
|
126
|
+
const cleanType = fieldType.replace(/[?\[\]]/g, "");
|
|
127
|
+
// Extract @relation(fields: [userId], references: [id]) if present
|
|
128
|
+
let relationFromFields;
|
|
129
|
+
let relationToFields;
|
|
130
|
+
const fieldsMatch = line.match(/fields:\s*\[([^\]]+)\]/);
|
|
131
|
+
if (fieldsMatch) {
|
|
132
|
+
relationFromFields = fieldsMatch[1]
|
|
133
|
+
.split(",")
|
|
134
|
+
.map((s) => s.trim())
|
|
135
|
+
.filter(Boolean);
|
|
136
|
+
}
|
|
137
|
+
const refsMatch = line.match(/references:\s*\[([^\]]+)\]/);
|
|
138
|
+
if (refsMatch) {
|
|
139
|
+
relationToFields = refsMatch[1]
|
|
140
|
+
.split(",")
|
|
141
|
+
.map((s) => s.trim())
|
|
142
|
+
.filter(Boolean);
|
|
143
|
+
}
|
|
144
|
+
const isExplicitRelation = line.includes("@relation") || !SCALAR_TYPES.has(cleanType);
|
|
145
|
+
currentModel.fields.push({
|
|
146
|
+
name: fieldName,
|
|
147
|
+
type: cleanType,
|
|
148
|
+
isList,
|
|
149
|
+
isRequired,
|
|
150
|
+
relation: isExplicitRelation,
|
|
151
|
+
...(relationFromFields ? { relationFromFields } : {}),
|
|
152
|
+
...(relationToFields ? { relationToFields } : {}),
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Post-process: Ensure fields whose type is an Enum are marked as non-relations
|
|
158
|
+
const enumNames = new Set(enums.map((e) => e.name));
|
|
159
|
+
for (const model of models) {
|
|
160
|
+
for (const field of model.fields) {
|
|
161
|
+
if (enumNames.has(field.type)) {
|
|
162
|
+
field.relation = false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return { models, enums };
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Extracts schema information from a Prisma client instance, DMMF, _runtimeDataModel, or schema file.
|
|
170
|
+
*/
|
|
171
|
+
function extractPrismaSchema(input) {
|
|
172
|
+
if (input && typeof input === "object") {
|
|
173
|
+
// 1. Standard DMMF format (Array of models)
|
|
174
|
+
const dmmf = input.dmmf || input._dmmf;
|
|
175
|
+
if (dmmf?.datamodel?.models && Array.isArray(dmmf.datamodel.models)) {
|
|
176
|
+
return {
|
|
177
|
+
models: dmmf.datamodel.models.map((m) => ({
|
|
178
|
+
name: m.name,
|
|
179
|
+
fields: (m.fields || []).map((f) => ({
|
|
180
|
+
name: f.name,
|
|
181
|
+
type: f.type,
|
|
182
|
+
isList: Boolean(f.isList),
|
|
183
|
+
isRequired: Boolean(f.isRequired),
|
|
184
|
+
relation: f.kind === "object",
|
|
185
|
+
...(f.relationFromFields?.length
|
|
186
|
+
? { relationFromFields: f.relationFromFields }
|
|
187
|
+
: {}),
|
|
188
|
+
...(f.relationToFields?.length
|
|
189
|
+
? { relationToFields: f.relationToFields }
|
|
190
|
+
: {}),
|
|
191
|
+
})),
|
|
192
|
+
})),
|
|
193
|
+
enums: (dmmf.datamodel.enums || []).map((e) => ({
|
|
194
|
+
name: e.name,
|
|
195
|
+
values: (e.values || []).map((v) => typeof v === "string" ? v : v.name),
|
|
196
|
+
})),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
// 2. Prisma v5/v6/v7 _runtimeDataModel format (Record map of models)
|
|
200
|
+
const runtimeModel = input._runtimeDataModel ||
|
|
201
|
+
(input.models && !Array.isArray(input.models) ? input : null);
|
|
202
|
+
if (runtimeModel?.models && typeof runtimeModel.models === "object") {
|
|
203
|
+
const models = Object.entries(runtimeModel.models).map(([modelName, m]) => ({
|
|
204
|
+
name: modelName,
|
|
205
|
+
fields: (m.fields || []).map((f) => ({
|
|
206
|
+
name: f.name,
|
|
207
|
+
type: f.type,
|
|
208
|
+
isList: Boolean(f.isList),
|
|
209
|
+
isRequired: Boolean(f.isRequired),
|
|
210
|
+
relation: f.kind === "object",
|
|
211
|
+
...(f.relationFromFields?.length
|
|
212
|
+
? { relationFromFields: f.relationFromFields }
|
|
213
|
+
: {}),
|
|
214
|
+
...(f.relationToFields?.length
|
|
215
|
+
? { relationToFields: f.relationToFields }
|
|
216
|
+
: {}),
|
|
217
|
+
})),
|
|
218
|
+
}));
|
|
219
|
+
const enums = runtimeModel.enums
|
|
220
|
+
? Object.entries(runtimeModel.enums).map(([enumName, e]) => ({
|
|
221
|
+
name: enumName,
|
|
222
|
+
values: (e.values || []).map((v) => typeof v === "string" ? v : v.name),
|
|
223
|
+
}))
|
|
224
|
+
: [];
|
|
225
|
+
return { models, enums };
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// 3. File path resolution
|
|
229
|
+
const filePath = findPrismaSchemaPath(typeof input === "string" ? input : undefined);
|
|
230
|
+
if (!filePath) {
|
|
231
|
+
return { models: [], enums: [] };
|
|
232
|
+
}
|
|
233
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
234
|
+
return parsePrismaSchemaText(content);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Formats extracted schema models, foreign-key relationships, and enums into an AI-digestible context string.
|
|
238
|
+
*/
|
|
239
|
+
function formatSchemaForAIContext(schema) {
|
|
240
|
+
if (!schema.models.length && !schema.enums.length) {
|
|
241
|
+
return "";
|
|
242
|
+
}
|
|
243
|
+
const parts = [];
|
|
244
|
+
if (schema.models.length > 0) {
|
|
245
|
+
parts.push("Database Models and Relations:");
|
|
246
|
+
for (const m of schema.models) {
|
|
247
|
+
const camelModelName = m.name.charAt(0).toLowerCase() + m.name.slice(1);
|
|
248
|
+
const fieldStrs = m.fields.map((f) => {
|
|
249
|
+
let descriptor = `${f.name}: ${f.type}${f.isList ? "[]" : ""}${!f.isRequired ? "?" : ""}`;
|
|
250
|
+
if (f.relation) {
|
|
251
|
+
if (f.relationFromFields?.length && f.relationToFields?.length) {
|
|
252
|
+
descriptor += ` (relation to ${f.type} via ${f.relationFromFields.join(",")} -> ${f.relationToFields.join(",")})`;
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
descriptor += ` (relation to ${f.type})`;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return descriptor;
|
|
259
|
+
});
|
|
260
|
+
parts.push(`- Model ${m.name} (targetTable: "${camelModelName}") { ${fieldStrs.join(", ")} }`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (schema.enums.length > 0) {
|
|
264
|
+
parts.push("Database Enums:");
|
|
265
|
+
for (const e of schema.enums) {
|
|
266
|
+
parts.push(`- Enum ${e.name} [${e.values.join(", ")}]`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return parts.join("\n");
|
|
270
|
+
}
|
package/dist/schema.d.ts
CHANGED
|
@@ -1,49 +1,39 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
export declare const PrismaOperatorSchema: z.ZodEnum<["equals", "not", "in", "notIn", "lt", "lte", "gt", "gte", "contains", "startsWith", "endsWith"]>;
|
|
3
|
-
export
|
|
4
|
-
field: z.ZodString;
|
|
5
|
-
operator: z.ZodEnum<["equals", "not", "in", "notIn", "lt", "lte", "gt", "gte", "contains", "startsWith", "endsWith"]>;
|
|
6
|
-
value: z.ZodAny;
|
|
7
|
-
}, "strip", z.ZodTypeAny, {
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const PrismaOperatorSchema: z.ZodEnum<["equals", "not", "in", "notIn", "lt", "lte", "gt", "gte", "contains", "startsWith", "endsWith", "some", "every", "none", "is", "isNot"]>;
|
|
3
|
+
export interface QueryCondition {
|
|
8
4
|
field: string;
|
|
9
|
-
operator:
|
|
5
|
+
operator: z.infer<typeof PrismaOperatorSchema>;
|
|
10
6
|
value?: any;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}>;
|
|
7
|
+
}
|
|
8
|
+
export declare const QueryConditionSchema: z.ZodType<QueryCondition>;
|
|
9
|
+
export declare const OrderByDirectionSchema: z.ZodEnum<["asc", "desc"]>;
|
|
10
|
+
export declare const OrderByItemSchema: z.ZodRecord<z.ZodString, z.ZodEnum<["asc", "desc"]>>;
|
|
16
11
|
export declare const AIQueryResponseSchema: z.ZodObject<{
|
|
17
12
|
targetTable: z.ZodOptional<z.ZodString>;
|
|
18
13
|
logicalOperator: z.ZodDefault<z.ZodEnum<["AND", "OR"]>>;
|
|
19
|
-
filters: z.ZodArray<z.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
operator: "equals" | "not" | "in" | "notIn" | "lt" | "lte" | "gt" | "gte" | "contains" | "startsWith" | "endsWith";
|
|
26
|
-
value?: any;
|
|
27
|
-
}, {
|
|
28
|
-
field: string;
|
|
29
|
-
operator: "equals" | "not" | "in" | "notIn" | "lt" | "lte" | "gt" | "gte" | "contains" | "startsWith" | "endsWith";
|
|
30
|
-
value?: any;
|
|
31
|
-
}>, "many">;
|
|
14
|
+
filters: z.ZodArray<z.ZodType<QueryCondition, z.ZodTypeDef, QueryCondition>, "many">;
|
|
15
|
+
include: z.ZodEffects<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>, Record<string, any> | undefined, unknown>;
|
|
16
|
+
select: z.ZodEffects<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>, Record<string, any> | undefined, unknown>;
|
|
17
|
+
orderBy: z.ZodEffects<z.ZodOptional<z.ZodUnion<[z.ZodRecord<z.ZodString, z.ZodEnum<["asc", "desc"]>>, z.ZodArray<z.ZodRecord<z.ZodString, z.ZodEnum<["asc", "desc"]>>, "many">, z.ZodRecord<z.ZodString, z.ZodAny>]>>, Record<string, any> | Record<string, "asc" | "desc"> | Record<string, "asc" | "desc">[] | undefined, unknown>;
|
|
18
|
+
take: z.ZodEffects<z.ZodOptional<z.ZodUnion<[z.ZodNumber, z.ZodString, z.ZodNull]>>, number | undefined, string | number | null | undefined>;
|
|
19
|
+
skip: z.ZodEffects<z.ZodOptional<z.ZodUnion<[z.ZodNumber, z.ZodString, z.ZodNull]>>, number | undefined, string | number | null | undefined>;
|
|
32
20
|
}, "strip", z.ZodTypeAny, {
|
|
33
21
|
logicalOperator: "AND" | "OR";
|
|
34
|
-
filters:
|
|
35
|
-
field: string;
|
|
36
|
-
operator: "equals" | "not" | "in" | "notIn" | "lt" | "lte" | "gt" | "gte" | "contains" | "startsWith" | "endsWith";
|
|
37
|
-
value?: any;
|
|
38
|
-
}[];
|
|
22
|
+
filters: QueryCondition[];
|
|
39
23
|
targetTable?: string | undefined;
|
|
24
|
+
include?: Record<string, any> | undefined;
|
|
25
|
+
select?: Record<string, any> | undefined;
|
|
26
|
+
orderBy?: Record<string, any> | Record<string, "asc" | "desc"> | Record<string, "asc" | "desc">[] | undefined;
|
|
27
|
+
take?: number | undefined;
|
|
28
|
+
skip?: number | undefined;
|
|
40
29
|
}, {
|
|
41
|
-
filters:
|
|
42
|
-
field: string;
|
|
43
|
-
operator: "equals" | "not" | "in" | "notIn" | "lt" | "lte" | "gt" | "gte" | "contains" | "startsWith" | "endsWith";
|
|
44
|
-
value?: any;
|
|
45
|
-
}[];
|
|
30
|
+
filters: QueryCondition[];
|
|
46
31
|
targetTable?: string | undefined;
|
|
47
32
|
logicalOperator?: "AND" | "OR" | undefined;
|
|
33
|
+
include?: unknown;
|
|
34
|
+
select?: unknown;
|
|
35
|
+
orderBy?: unknown;
|
|
36
|
+
take?: string | number | null | undefined;
|
|
37
|
+
skip?: string | number | null | undefined;
|
|
48
38
|
}>;
|
|
49
39
|
export type AIQueryResponse = z.infer<typeof AIQueryResponseSchema>;
|
package/dist/schema.js
CHANGED
|
@@ -1,18 +1,101 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AIQueryResponseSchema = exports.QueryConditionSchema = exports.PrismaOperatorSchema = void 0;
|
|
3
|
+
exports.AIQueryResponseSchema = exports.OrderByItemSchema = exports.OrderByDirectionSchema = exports.QueryConditionSchema = exports.PrismaOperatorSchema = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
exports.PrismaOperatorSchema = zod_1.z.enum([
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
"equals",
|
|
7
|
+
"not",
|
|
8
|
+
"in",
|
|
9
|
+
"notIn",
|
|
10
|
+
"lt",
|
|
11
|
+
"lte",
|
|
12
|
+
"gt",
|
|
13
|
+
"gte",
|
|
14
|
+
"contains",
|
|
15
|
+
"startsWith",
|
|
16
|
+
"endsWith",
|
|
17
|
+
// Relational list operators
|
|
18
|
+
"some",
|
|
19
|
+
"every",
|
|
20
|
+
"none",
|
|
21
|
+
// Nullability & existence operators
|
|
22
|
+
"is",
|
|
23
|
+
"isNot",
|
|
8
24
|
]);
|
|
9
|
-
exports.QueryConditionSchema = zod_1.z.object({
|
|
10
|
-
field: zod_1.z
|
|
25
|
+
exports.QueryConditionSchema = zod_1.z.lazy(() => zod_1.z.object({
|
|
26
|
+
field: zod_1.z
|
|
27
|
+
.string()
|
|
28
|
+
.describe("The database column name or relation field (supports dot-notation like 'user.email')"),
|
|
11
29
|
operator: exports.PrismaOperatorSchema,
|
|
12
|
-
value: zod_1.z
|
|
13
|
-
|
|
30
|
+
value: zod_1.z
|
|
31
|
+
.any()
|
|
32
|
+
.optional()
|
|
33
|
+
.describe("The filter value or nested sub-condition"),
|
|
34
|
+
}));
|
|
35
|
+
exports.OrderByDirectionSchema = zod_1.z.enum(["asc", "desc"]);
|
|
36
|
+
exports.OrderByItemSchema = zod_1.z.record(zod_1.z.string(), exports.OrderByDirectionSchema);
|
|
37
|
+
const parseJsonOrObject = (val) => {
|
|
38
|
+
if (val === null || val === undefined || val === "")
|
|
39
|
+
return undefined;
|
|
40
|
+
if (typeof val === "string") {
|
|
41
|
+
const trimmed = val.trim();
|
|
42
|
+
if (!trimmed)
|
|
43
|
+
return undefined;
|
|
44
|
+
try {
|
|
45
|
+
return JSON.parse(trimmed);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return val;
|
|
52
|
+
};
|
|
14
53
|
exports.AIQueryResponseSchema = zod_1.z.object({
|
|
15
|
-
targetTable: zod_1.z
|
|
16
|
-
|
|
17
|
-
|
|
54
|
+
targetTable: zod_1.z
|
|
55
|
+
.string()
|
|
56
|
+
.optional()
|
|
57
|
+
.describe("The primary database table to query (e.g., 'subscription', 'user')"),
|
|
58
|
+
logicalOperator: zod_1.z.enum(["AND", "OR"]).default("AND"),
|
|
59
|
+
filters: zod_1.z
|
|
60
|
+
.array(exports.QueryConditionSchema)
|
|
61
|
+
.describe("Filter conditions for where clause"),
|
|
62
|
+
include: zod_1.z
|
|
63
|
+
.preprocess(parseJsonOrObject, zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional())
|
|
64
|
+
.describe("Relations to include in multi-level queries (nested object structure)"),
|
|
65
|
+
select: zod_1.z
|
|
66
|
+
.preprocess(parseJsonOrObject, zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional())
|
|
67
|
+
.describe("Specific fields to select"),
|
|
68
|
+
orderBy: zod_1.z
|
|
69
|
+
.preprocess(parseJsonOrObject, zod_1.z
|
|
70
|
+
.union([
|
|
71
|
+
exports.OrderByItemSchema,
|
|
72
|
+
zod_1.z.array(exports.OrderByItemSchema),
|
|
73
|
+
zod_1.z.record(zod_1.z.string(), zod_1.z.any()),
|
|
74
|
+
])
|
|
75
|
+
.optional())
|
|
76
|
+
.describe("Ordering of the results"),
|
|
77
|
+
take: zod_1.z
|
|
78
|
+
.union([zod_1.z.number(), zod_1.z.string(), zod_1.z.null()])
|
|
79
|
+
.optional()
|
|
80
|
+
.transform((val) => {
|
|
81
|
+
if (val === null || val === undefined || val === "")
|
|
82
|
+
return undefined;
|
|
83
|
+
const parsed = typeof val === "string" ? parseInt(val, 10) : val;
|
|
84
|
+
if (isNaN(parsed) || parsed <= 0)
|
|
85
|
+
return undefined;
|
|
86
|
+
return parsed;
|
|
87
|
+
})
|
|
88
|
+
.describe("Maximum number of rows to return (positive integer)"),
|
|
89
|
+
skip: zod_1.z
|
|
90
|
+
.union([zod_1.z.number(), zod_1.z.string(), zod_1.z.null()])
|
|
91
|
+
.optional()
|
|
92
|
+
.transform((val) => {
|
|
93
|
+
if (val === null || val === undefined || val === "")
|
|
94
|
+
return undefined;
|
|
95
|
+
const parsed = typeof val === "string" ? parseInt(val, 10) : val;
|
|
96
|
+
if (isNaN(parsed) || parsed < 0)
|
|
97
|
+
return undefined;
|
|
98
|
+
return parsed;
|
|
99
|
+
})
|
|
100
|
+
.describe("Number of rows to skip (pagination)"),
|
|
18
101
|
});
|
package/package.json
CHANGED
|
@@ -1,35 +1,40 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "prisma-safe-ai-query",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "An end-to-end AI ORM: securely translates natural language into Prisma queries using Google Gemini and Zod.",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"@
|
|
34
|
-
|
|
35
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "prisma-safe-ai-query",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "An end-to-end AI ORM: securely translates natural language into Prisma queries using Google Gemini and Zod.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md",
|
|
10
|
+
"LICENSE"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"prepublishOnly": "npm run build",
|
|
15
|
+
"test": "npm run build && node --test tests/engine.test.js"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"prisma",
|
|
19
|
+
"ai",
|
|
20
|
+
"llm",
|
|
21
|
+
"gemini",
|
|
22
|
+
"zod",
|
|
23
|
+
"security",
|
|
24
|
+
"orm",
|
|
25
|
+
"multi-level-query"
|
|
26
|
+
],
|
|
27
|
+
"author": "Olugbenga Taiwo",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@google/genai": "^1.45.0"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@prisma/client": ">=5.0.0",
|
|
34
|
+
"zod": "^3.22.0 || ^4.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"typescript": "^5.7.3",
|
|
38
|
+
"@types/node": "^22.20.1"
|
|
39
|
+
}
|
|
40
|
+
}
|