prisma-extension-autozod 0.0.0-stage → 1.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/LICENSE +22 -0
- package/README.md +188 -2
- package/dist/index.cjs +130 -0
- package/dist/index.d.cts +203 -0
- package/dist/index.d.ts +203 -0
- package/dist/index.js +98 -0
- package/package.json +67 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mohammed Thamer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
CHANGED
|
@@ -1,3 +1,189 @@
|
|
|
1
|
-
#
|
|
1
|
+
# prisma-extension-autozod
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/prisma-extension-autozod)
|
|
4
|
+
[](https://github.com/abzr0o/prisma-extension-autozod/actions)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
> Contract-aware compile-time linting and runtime Zod validation for the new Prisma ORM (@prisma/orm-postgres).
|
|
8
|
+
|
|
9
|
+
`prisma-extension-autozod` connects **Zod 4** and **Prisma ORM 8**. It provides two layers of safety:
|
|
10
|
+
1. **Compile-time Linting**: TypeScript flags any field in your Zod schema that does not exist in your Prisma data contract.
|
|
11
|
+
2. **Runtime Middleware**: Automatically intercepts write operations (`create`, `update`, `insert`) via Prisma 8's `beforeCompile` hook, validates payloads, normalizes column naming conventions, and blocks invalid mutations before SQL reaches your database.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Requirements
|
|
16
|
+
|
|
17
|
+
- **Node.js**: `>= 24.0.0`
|
|
18
|
+
- **Prisma ORM**: `@prisma/orm-postgres >= 8.0.0`
|
|
19
|
+
- **Zod**: `^4.0.0`
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
- **Compile-Time Contract Linting**: Catches schema mismatches and invalid fields at build-time with clear TypeScript diagnostics.
|
|
26
|
+
- **Native Prisma ORM 8 Lifecycle Hook**: Hooks directly into the `beforeCompile` AST pipeline without monkey-patching.
|
|
27
|
+
- **Automatic Column Normalization**: Remaps input casing (e.g., `first_name` to `firstName`) based on your contract's column catalog.
|
|
28
|
+
- **Context-Aware Validation**: Full validation for `create`/`insert`, and automatic `.partial()` validation for `update` operations so omitted fields do not fail.
|
|
29
|
+
- **Flexible Strictness**: Choose between `createZodRegistry` (allows omitting auto-generated/default fields like `id` or `createdAt`) and `createStrictZodRegistry` (enforces full table column coverage).
|
|
30
|
+
- **Dual ESM & CJS**: Full TypeScript `.d.ts` declarations included out-of-the-box.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# pnpm
|
|
38
|
+
pnpm add prisma-extension-autozod zod @prisma/orm-postgres
|
|
39
|
+
|
|
40
|
+
# npm
|
|
41
|
+
npm install prisma-extension-autozod zod @prisma/orm-postgres
|
|
42
|
+
|
|
43
|
+
# yarn
|
|
44
|
+
yarn add prisma-extension-autozod zod @prisma/orm-postgres
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Quickstart
|
|
50
|
+
|
|
51
|
+
### 1. Define Validated Schemas with Contract Linting
|
|
52
|
+
|
|
53
|
+
Bind your Zod schemas to your generated Prisma 8 `models` using `createZodRegistry`:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { z } from "zod";
|
|
57
|
+
import { createZodRegistry } from "prisma-extension-autozod";
|
|
58
|
+
import type { models } from "./contract.d.ts";
|
|
59
|
+
|
|
60
|
+
// Create a registry bound to your Prisma 8 contract models
|
|
61
|
+
const defineModel = createZodRegistry<typeof models.public>();
|
|
62
|
+
|
|
63
|
+
export const userValidator = defineModel(
|
|
64
|
+
"User",
|
|
65
|
+
z.object({
|
|
66
|
+
email: z.string().email("Invalid email address"),
|
|
67
|
+
username: z.string().min(2, "Username must be at least 2 characters").nullable().optional(),
|
|
68
|
+
name: z.string().min(2, "Name must be at least 2 characters").nullable().optional(),
|
|
69
|
+
})
|
|
70
|
+
);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
> **Compile-Time Safety in Action:**
|
|
74
|
+
> If you add a field that does not exist in the contract:
|
|
75
|
+
> ```typescript
|
|
76
|
+
> defineModel("User", z.object({
|
|
77
|
+
> nonExistentColumn: z.string() // Error: Field 'nonExistentColumn' does not exist in table contract
|
|
78
|
+
> }));
|
|
79
|
+
> ```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
### 2. Register Middleware in Your Prisma 8 Client
|
|
84
|
+
|
|
85
|
+
Pass the middleware to your `@prisma/orm-postgres` client setup:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import postgres from "@prisma/orm-postgres/runtime";
|
|
89
|
+
import { contractFromJson } from "@prisma/orm-postgres/static";
|
|
90
|
+
import { autoZodPrisma8 } from "prisma-extension-autozod";
|
|
91
|
+
|
|
92
|
+
import contractJson from "./contract.json" with { type: "json" };
|
|
93
|
+
import { userValidator } from "./schemas.ts";
|
|
94
|
+
|
|
95
|
+
export const contract = contractFromJson(contractJson);
|
|
96
|
+
|
|
97
|
+
export const db = postgres({
|
|
98
|
+
contract,
|
|
99
|
+
url: process.env.DATABASE_URL!,
|
|
100
|
+
middleware: [
|
|
101
|
+
autoZodPrisma8({
|
|
102
|
+
contract: contractJson,
|
|
103
|
+
validators: [userValidator],
|
|
104
|
+
}),
|
|
105
|
+
],
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
### 3. Automatic Validation & Sanitization
|
|
112
|
+
|
|
113
|
+
In the new Prisma ORM, models are accessed via `db.orm.<namespace>.<ModelName>`:
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// Valid: Passes validation and executes
|
|
117
|
+
await db.orm.public.User.create({
|
|
118
|
+
email: "alice@prisma.io",
|
|
119
|
+
username: "alice",
|
|
120
|
+
name: "Alice Smith",
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Invalid: Throws standard ZodError before hitting the database
|
|
124
|
+
await db.orm.public.User.create({
|
|
125
|
+
email: "not-an-email",
|
|
126
|
+
username: "a", // too short (< 2 characters)
|
|
127
|
+
name: "Alice",
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Update: Only supplied fields are validated
|
|
131
|
+
await db.orm.public.User
|
|
132
|
+
.where({ email: "alice@prisma.io" })
|
|
133
|
+
.update({
|
|
134
|
+
name: "Alice Wonder", // Valid; other fields are not required on updates
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## API Reference
|
|
141
|
+
|
|
142
|
+
### `createZodRegistry<TModels>()`
|
|
143
|
+
Creates a model validator factory bound to your contract models. Validates that all fields in the schema exist in the contract table, allowing omission of auto-generated or optional fields.
|
|
144
|
+
```typescript
|
|
145
|
+
const defineModel = createZodRegistry<typeof models.public>();
|
|
146
|
+
const userValidator = defineModel("User", zodSchema);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### `createStrictZodRegistry<TModels>()`
|
|
150
|
+
Creates a strict validator factory that requires all columns in the contract table to be declared in the schema, while also rejecting unknown fields.
|
|
151
|
+
```typescript
|
|
152
|
+
const defineStrict = createStrictZodRegistry<typeof models.public>();
|
|
153
|
+
const userValidator = defineStrict("User", zodSchema);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### `autoZodPrisma8(options)` (alias: `autoZod`)
|
|
157
|
+
Prisma ORM 8 middleware factory:
|
|
158
|
+
- `options.contract`: The raw `contract.json` object.
|
|
159
|
+
- `options.validators`: Array of validator entries returned by `createZodRegistry` or `createStrictZodRegistry`.
|
|
160
|
+
|
|
161
|
+
### `extractColumns(contractJson)`
|
|
162
|
+
Helper function that inspects storage namespaces inside `contract.json` and returns a normalized dictionary of table column names.
|
|
163
|
+
|
|
164
|
+
### `normalizeKey(key: string)`
|
|
165
|
+
Normalizes strings to lowercase alphanumeric format for reliable case- and separator-insensitive mapping.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Development & Testing
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
# Install dependencies
|
|
173
|
+
pnpm install
|
|
174
|
+
|
|
175
|
+
# Run tests
|
|
176
|
+
pnpm test
|
|
177
|
+
|
|
178
|
+
# Typecheck
|
|
179
|
+
pnpm run typecheck
|
|
180
|
+
|
|
181
|
+
# Build bundle (ESM + CJS + DTS)
|
|
182
|
+
pnpm run build
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## License
|
|
188
|
+
|
|
189
|
+
[MIT](LICENSE) (c) [Mohammed Thamer](https://github.com/abzr0o)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
autoZod: () => autoZod,
|
|
24
|
+
autoZodPrisma8: () => autoZodPrisma8,
|
|
25
|
+
createStrictZodRegistry: () => createStrictZodRegistry,
|
|
26
|
+
createZodRegistry: () => createZodRegistry,
|
|
27
|
+
extractColumns: () => extractColumns,
|
|
28
|
+
normalizeKey: () => normalizeKey
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(index_exports);
|
|
31
|
+
|
|
32
|
+
// src/middleware.ts
|
|
33
|
+
function normalizeKey(key) {
|
|
34
|
+
return key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
35
|
+
}
|
|
36
|
+
function extractValue(val) {
|
|
37
|
+
if (val && typeof val === "object" && "value" in val) {
|
|
38
|
+
return val.value;
|
|
39
|
+
}
|
|
40
|
+
return val;
|
|
41
|
+
}
|
|
42
|
+
function extractColumns(contractJson) {
|
|
43
|
+
const tableCols = {};
|
|
44
|
+
const namespaces = contractJson?.storage?.namespaces || {};
|
|
45
|
+
for (const ns of Object.values(namespaces)) {
|
|
46
|
+
const tables = ns?.entries?.table || ns?.tables || {};
|
|
47
|
+
for (const [table, def] of Object.entries(tables)) {
|
|
48
|
+
tableCols[normalizeKey(table)] = Object.keys(def?.columns || {});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return tableCols;
|
|
52
|
+
}
|
|
53
|
+
function autoZodPrisma8(options) {
|
|
54
|
+
const columnCatalog = extractColumns(options.contract);
|
|
55
|
+
const validatorMap = /* @__PURE__ */ new Map();
|
|
56
|
+
for (const entry of options.validators) {
|
|
57
|
+
validatorMap.set(normalizeKey(entry.tableName), entry.schema);
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
name: "autozod-validator",
|
|
61
|
+
familyId: "sql",
|
|
62
|
+
async beforeCompile(draft, ctx) {
|
|
63
|
+
const ast = draft?.ast;
|
|
64
|
+
if (!ast || ast.kind !== "insert" && ast.kind !== "update") return void 0;
|
|
65
|
+
const target = ast.table?.name;
|
|
66
|
+
if (!target) return void 0;
|
|
67
|
+
const cleanTarget = normalizeKey(target);
|
|
68
|
+
const schema = validatorMap.get(cleanTarget);
|
|
69
|
+
if (!schema) return void 0;
|
|
70
|
+
const targetCols = columnCatalog[cleanTarget] || [];
|
|
71
|
+
const colLookup = /* @__PURE__ */ new Map();
|
|
72
|
+
for (const col of targetCols) {
|
|
73
|
+
colLookup.set(normalizeKey(col), col);
|
|
74
|
+
}
|
|
75
|
+
const validateFields = (rawRecord, isUpdate) => {
|
|
76
|
+
const extracted = {};
|
|
77
|
+
for (const [col, val] of Object.entries(rawRecord)) {
|
|
78
|
+
const actualCol = colLookup.get(normalizeKey(col)) || col;
|
|
79
|
+
extracted[actualCol] = extractValue(val);
|
|
80
|
+
}
|
|
81
|
+
const schemaShape = schema.shape || {};
|
|
82
|
+
const schemaKeys = new Set(Object.keys(schemaShape));
|
|
83
|
+
const toValidate = {};
|
|
84
|
+
for (const [k, v] of Object.entries(extracted)) {
|
|
85
|
+
if (schemaKeys.has(k)) {
|
|
86
|
+
toValidate[k] = v;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const activeSchema = isUpdate ? schema.partial() : schema;
|
|
90
|
+
activeSchema.parse(toValidate);
|
|
91
|
+
};
|
|
92
|
+
if (ast.kind === "insert" && Array.isArray(ast.rows)) {
|
|
93
|
+
for (const row of ast.rows) {
|
|
94
|
+
validateFields(row, false);
|
|
95
|
+
}
|
|
96
|
+
} else if (ast.kind === "update" && ast.set) {
|
|
97
|
+
validateFields(ast.set, true);
|
|
98
|
+
}
|
|
99
|
+
return void 0;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
var autoZod = autoZodPrisma8;
|
|
104
|
+
|
|
105
|
+
// src/registry.ts
|
|
106
|
+
function createZodRegistry() {
|
|
107
|
+
return function(tableName, schema) {
|
|
108
|
+
return {
|
|
109
|
+
tableName,
|
|
110
|
+
schema
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function createStrictZodRegistry() {
|
|
115
|
+
return function(tableName, schema) {
|
|
116
|
+
return {
|
|
117
|
+
tableName,
|
|
118
|
+
schema
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
123
|
+
0 && (module.exports = {
|
|
124
|
+
autoZod,
|
|
125
|
+
autoZodPrisma8,
|
|
126
|
+
createStrictZodRegistry,
|
|
127
|
+
createZodRegistry,
|
|
128
|
+
extractColumns,
|
|
129
|
+
normalizeKey
|
|
130
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Normalizes a string type by stripping underscores and hyphens and converting to lowercase.
|
|
5
|
+
*/
|
|
6
|
+
type CleanKey<S extends string> = Lowercase<S extends `${infer Head}_${infer Tail}` ? `${Head}${CleanKey<Tail>}` : S extends `${infer Head}-${infer Tail}` ? `${Head}${CleanKey<Tail>}` : S>;
|
|
7
|
+
/**
|
|
8
|
+
* Resolves a Zod field key against the list of contract column fields using case-insensitive matching.
|
|
9
|
+
*/
|
|
10
|
+
type ResolveColumn<ZodKey extends string, ContractFields extends string> = {
|
|
11
|
+
[Field in ContractFields]: CleanKey<Field> extends CleanKey<ZodKey> ? Field : never;
|
|
12
|
+
}[ContractFields];
|
|
13
|
+
/**
|
|
14
|
+
* Returns any keys in `TShape` that do NOT correspond to a known column on the contract model.
|
|
15
|
+
*/
|
|
16
|
+
type InvalidContractKeys<TShape, ContractFields extends string> = {
|
|
17
|
+
[K in keyof TShape]: ResolveColumn<Extract<K, string>, ContractFields> extends never ? Extract<K, string> : never;
|
|
18
|
+
}[keyof TShape];
|
|
19
|
+
/**
|
|
20
|
+
* Returns any contract column names that are missing in `TShape`.
|
|
21
|
+
*/
|
|
22
|
+
type MissingContractKeys<TShape, ContractFields extends string> = {
|
|
23
|
+
[Field in ContractFields]: CleanKey<Field> extends CleanKey<Extract<keyof TShape, string>> ? never : Field;
|
|
24
|
+
}[ContractFields];
|
|
25
|
+
/**
|
|
26
|
+
* Compile-time linter type that verifies a Zod schema shape against Prisma 8 contract model fields.
|
|
27
|
+
*
|
|
28
|
+
* If all keys match, evaluates to `z.ZodObject<TShape>`.
|
|
29
|
+
* If any invalid key exists, evaluates to an error object mapping each invalid key
|
|
30
|
+
* to a descriptive compile error string.
|
|
31
|
+
*/
|
|
32
|
+
type ValidateZodAgainstContract<TShape, ContractFields extends string> = [InvalidContractKeys<TShape, ContractFields>] extends [never] ? z.ZodObject<TShape & z.ZodRawShape> : {
|
|
33
|
+
[K in keyof TShape]: ResolveColumn<Extract<K, string>, ContractFields> extends never ? `[Prisma8-Contract-Error]: Field '${Extract<K, string>}' does not exist in table contract` : any;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Represents a registered validator entry binding a table name to its Zod schema.
|
|
38
|
+
*/
|
|
39
|
+
interface ModelValidatorEntry {
|
|
40
|
+
/**
|
|
41
|
+
* The name of the database table or model to validate against.
|
|
42
|
+
*/
|
|
43
|
+
tableName: string;
|
|
44
|
+
/**
|
|
45
|
+
* The Zod object schema used to validate mutation payloads.
|
|
46
|
+
*/
|
|
47
|
+
schema: z.ZodObject<any>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Creates a strongly typed validator registry factory bound to Prisma ORM 8 contract models.
|
|
51
|
+
*
|
|
52
|
+
* Provides compile-time checking ensuring that every field declared in your Zod schema
|
|
53
|
+
* corresponds to a valid column on the database model. Fields with default or auto-generated
|
|
54
|
+
* values (such as `id`, `createdAt`, `updatedAt`) can be omitted from the schema.
|
|
55
|
+
*
|
|
56
|
+
* @template TModels The generated Prisma 8 `Models` or `typeof models.public` type map.
|
|
57
|
+
* @returns A factory function `(tableName, schema)` producing a typed `ModelValidatorEntry`.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* import { z } from "zod";
|
|
62
|
+
* import { createZodRegistry } from "prisma-extension-autozod";
|
|
63
|
+
* import type { models } from "./contract.d.ts";
|
|
64
|
+
*
|
|
65
|
+
* const defineModel = createZodRegistry<typeof models.public>();
|
|
66
|
+
*
|
|
67
|
+
* export const userValidator = defineModel(
|
|
68
|
+
* "User",
|
|
69
|
+
* z.object({
|
|
70
|
+
* email: z.string().email(),
|
|
71
|
+
* username: z.string().min(2),
|
|
72
|
+
* name: z.string().nullable().optional(),
|
|
73
|
+
* })
|
|
74
|
+
* );
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
declare function createZodRegistry<TModels extends Record<string, any>>(): <TTable extends keyof TModels & string, TShape extends z.ZodRawShape>(tableName: TTable, schema: ValidateZodAgainstContract<TShape, Extract<keyof TModels[TTable], string>>) => ModelValidatorEntry;
|
|
78
|
+
/**
|
|
79
|
+
* Creates a strict validator registry factory bound to Prisma ORM 8 contract models.
|
|
80
|
+
*
|
|
81
|
+
* In addition to rejecting unknown fields, this enforces that ALL columns defined on the
|
|
82
|
+
* contract table MUST be declared in the Zod schema.
|
|
83
|
+
*
|
|
84
|
+
* @template TModels The generated Prisma 8 `Models` or `typeof models.public` type map.
|
|
85
|
+
* @returns A factory function `(tableName, schema)` producing a typed `ModelValidatorEntry`.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* import { z } from "zod";
|
|
90
|
+
* import { createStrictZodRegistry } from "prisma-extension-autozod";
|
|
91
|
+
* import type { models } from "./contract.d.ts";
|
|
92
|
+
*
|
|
93
|
+
* const defineStrict = createStrictZodRegistry<typeof models.public>();
|
|
94
|
+
*
|
|
95
|
+
* // Compile error if any column from the table contract is omitted:
|
|
96
|
+
* export const userValidator = defineStrict(
|
|
97
|
+
* "User",
|
|
98
|
+
* z.object({
|
|
99
|
+
* id: z.number(),
|
|
100
|
+
* email: z.string().email(),
|
|
101
|
+
* username: z.string().nullable(),
|
|
102
|
+
* name: z.string().nullable(),
|
|
103
|
+
* createdAt: z.date(),
|
|
104
|
+
* updatedAt: z.date(),
|
|
105
|
+
* })
|
|
106
|
+
* );
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
declare function createStrictZodRegistry<TModels extends Record<string, any>>(): <TTable extends keyof TModels & string, TShape extends z.ZodRawShape>(tableName: TTable, schema: [InvalidContractKeys<TShape, Extract<keyof TModels[TTable], string>>] extends [never] ? [MissingContractKeys<TShape, Extract<keyof TModels[TTable], string>>] extends [never] ? z.ZodObject<TShape> : { [K in MissingContractKeys<TShape, Extract<keyof TModels[TTable], string>>]: `[Prisma8-Contract-Error]: Missing required table column '${K}' in schema`; } : { [K in keyof TShape]: ResolveColumn<Extract<K, string>, Extract<keyof TModels[TTable], string>> extends never ? `[Prisma8-Contract-Error]: Field '${Extract<K, string>}' does not exist in table contract` : any; }) => ModelValidatorEntry;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Shape of the AutoZod SQL middleware compatible with Prisma ORM 8 clients.
|
|
113
|
+
*/
|
|
114
|
+
interface AutoZodMiddleware {
|
|
115
|
+
/**
|
|
116
|
+
* Unique identifier name for the middleware.
|
|
117
|
+
*/
|
|
118
|
+
name: string;
|
|
119
|
+
/**
|
|
120
|
+
* Target storage engine family. Always 'sql' for PostgreSQL.
|
|
121
|
+
*/
|
|
122
|
+
familyId: "sql";
|
|
123
|
+
/**
|
|
124
|
+
* Lifecycle hook invoked prior to SQL plan compilation.
|
|
125
|
+
* Intercepts and validates AST mutation rows and values.
|
|
126
|
+
*/
|
|
127
|
+
beforeCompile(draft: any, ctx: any): Promise<undefined>;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Normalizes a key by stripping hyphens, underscores, and special characters,
|
|
131
|
+
* and converting to lowercase.
|
|
132
|
+
*
|
|
133
|
+
* @param key The column or table string to normalize.
|
|
134
|
+
* @returns A sanitized lowercase alphanumeric string.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* normalizeKey("first_name"); // "firstname"
|
|
139
|
+
* normalizeKey("User-Profile"); // "userprofile"
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
declare function normalizeKey(key: string): string;
|
|
143
|
+
/**
|
|
144
|
+
* Inspects a compiled Prisma ORM 8 `contract.json` object and extracts a
|
|
145
|
+
* dictionary of normalized table names mapped to their column list.
|
|
146
|
+
*
|
|
147
|
+
* @param contractJson The parsed Prisma 8 contract JSON object.
|
|
148
|
+
* @returns A mapping from normalized table names to array of column names.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* import contractJson from "./contract.json" with { type: "json" };
|
|
153
|
+
*
|
|
154
|
+
* const columns = extractColumns(contractJson);
|
|
155
|
+
* console.log(columns["user"]); // ["id", "email", "name", ...]
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
declare function extractColumns(contractJson: any): Record<string, string[]>;
|
|
159
|
+
/**
|
|
160
|
+
* Factory creating Prisma ORM 8 SQL middleware for automatic runtime Zod validation.
|
|
161
|
+
*
|
|
162
|
+
* Hooks into the query compiler via `beforeCompile` and intercepts `insert` and `update`
|
|
163
|
+
* operations. Maps columns according to the contract, validates payload values against
|
|
164
|
+
* registered Zod schemas, and throws `ZodError` before SQL execution if invalid.
|
|
165
|
+
*
|
|
166
|
+
* @param options.contract The parsed Prisma 8 `contract.json` object.
|
|
167
|
+
* @param options.validators An array of validator entries produced by `createZodRegistry`.
|
|
168
|
+
* @returns An `AutoZodMiddleware` instance ready to be passed to `postgres({ middleware: [...] })`.
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```typescript
|
|
172
|
+
* import postgres from "@prisma/orm-postgres/runtime";
|
|
173
|
+
* import { contractFromJson } from "@prisma/orm-postgres/static";
|
|
174
|
+
* import { autoZodPrisma8 } from "prisma-extension-autozod";
|
|
175
|
+
*
|
|
176
|
+
* import contractJson from "./contract.json" with { type: "json" };
|
|
177
|
+
* import { userValidator } from "./schemas.ts";
|
|
178
|
+
*
|
|
179
|
+
* export const contract = contractFromJson(contractJson);
|
|
180
|
+
*
|
|
181
|
+
* export const db = postgres({
|
|
182
|
+
* contract,
|
|
183
|
+
* url: process.env.DATABASE_URL!,
|
|
184
|
+
* middleware: [
|
|
185
|
+
* autoZodPrisma8({
|
|
186
|
+
* contract: contractJson,
|
|
187
|
+
* validators: [userValidator],
|
|
188
|
+
* }),
|
|
189
|
+
* ],
|
|
190
|
+
* });
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
declare function autoZodPrisma8(options: {
|
|
194
|
+
contract: any;
|
|
195
|
+
validators: ModelValidatorEntry[];
|
|
196
|
+
}): AutoZodMiddleware;
|
|
197
|
+
/**
|
|
198
|
+
* Convenient alias for `autoZodPrisma8`.
|
|
199
|
+
*/
|
|
200
|
+
declare const autoZod: typeof autoZodPrisma8;
|
|
201
|
+
|
|
202
|
+
export { autoZod, autoZodPrisma8, createStrictZodRegistry, createZodRegistry, extractColumns, normalizeKey };
|
|
203
|
+
export type { AutoZodMiddleware, CleanKey, InvalidContractKeys, MissingContractKeys, ModelValidatorEntry, ResolveColumn, ValidateZodAgainstContract };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Normalizes a string type by stripping underscores and hyphens and converting to lowercase.
|
|
5
|
+
*/
|
|
6
|
+
type CleanKey<S extends string> = Lowercase<S extends `${infer Head}_${infer Tail}` ? `${Head}${CleanKey<Tail>}` : S extends `${infer Head}-${infer Tail}` ? `${Head}${CleanKey<Tail>}` : S>;
|
|
7
|
+
/**
|
|
8
|
+
* Resolves a Zod field key against the list of contract column fields using case-insensitive matching.
|
|
9
|
+
*/
|
|
10
|
+
type ResolveColumn<ZodKey extends string, ContractFields extends string> = {
|
|
11
|
+
[Field in ContractFields]: CleanKey<Field> extends CleanKey<ZodKey> ? Field : never;
|
|
12
|
+
}[ContractFields];
|
|
13
|
+
/**
|
|
14
|
+
* Returns any keys in `TShape` that do NOT correspond to a known column on the contract model.
|
|
15
|
+
*/
|
|
16
|
+
type InvalidContractKeys<TShape, ContractFields extends string> = {
|
|
17
|
+
[K in keyof TShape]: ResolveColumn<Extract<K, string>, ContractFields> extends never ? Extract<K, string> : never;
|
|
18
|
+
}[keyof TShape];
|
|
19
|
+
/**
|
|
20
|
+
* Returns any contract column names that are missing in `TShape`.
|
|
21
|
+
*/
|
|
22
|
+
type MissingContractKeys<TShape, ContractFields extends string> = {
|
|
23
|
+
[Field in ContractFields]: CleanKey<Field> extends CleanKey<Extract<keyof TShape, string>> ? never : Field;
|
|
24
|
+
}[ContractFields];
|
|
25
|
+
/**
|
|
26
|
+
* Compile-time linter type that verifies a Zod schema shape against Prisma 8 contract model fields.
|
|
27
|
+
*
|
|
28
|
+
* If all keys match, evaluates to `z.ZodObject<TShape>`.
|
|
29
|
+
* If any invalid key exists, evaluates to an error object mapping each invalid key
|
|
30
|
+
* to a descriptive compile error string.
|
|
31
|
+
*/
|
|
32
|
+
type ValidateZodAgainstContract<TShape, ContractFields extends string> = [InvalidContractKeys<TShape, ContractFields>] extends [never] ? z.ZodObject<TShape & z.ZodRawShape> : {
|
|
33
|
+
[K in keyof TShape]: ResolveColumn<Extract<K, string>, ContractFields> extends never ? `[Prisma8-Contract-Error]: Field '${Extract<K, string>}' does not exist in table contract` : any;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Represents a registered validator entry binding a table name to its Zod schema.
|
|
38
|
+
*/
|
|
39
|
+
interface ModelValidatorEntry {
|
|
40
|
+
/**
|
|
41
|
+
* The name of the database table or model to validate against.
|
|
42
|
+
*/
|
|
43
|
+
tableName: string;
|
|
44
|
+
/**
|
|
45
|
+
* The Zod object schema used to validate mutation payloads.
|
|
46
|
+
*/
|
|
47
|
+
schema: z.ZodObject<any>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Creates a strongly typed validator registry factory bound to Prisma ORM 8 contract models.
|
|
51
|
+
*
|
|
52
|
+
* Provides compile-time checking ensuring that every field declared in your Zod schema
|
|
53
|
+
* corresponds to a valid column on the database model. Fields with default or auto-generated
|
|
54
|
+
* values (such as `id`, `createdAt`, `updatedAt`) can be omitted from the schema.
|
|
55
|
+
*
|
|
56
|
+
* @template TModels The generated Prisma 8 `Models` or `typeof models.public` type map.
|
|
57
|
+
* @returns A factory function `(tableName, schema)` producing a typed `ModelValidatorEntry`.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* import { z } from "zod";
|
|
62
|
+
* import { createZodRegistry } from "prisma-extension-autozod";
|
|
63
|
+
* import type { models } from "./contract.d.ts";
|
|
64
|
+
*
|
|
65
|
+
* const defineModel = createZodRegistry<typeof models.public>();
|
|
66
|
+
*
|
|
67
|
+
* export const userValidator = defineModel(
|
|
68
|
+
* "User",
|
|
69
|
+
* z.object({
|
|
70
|
+
* email: z.string().email(),
|
|
71
|
+
* username: z.string().min(2),
|
|
72
|
+
* name: z.string().nullable().optional(),
|
|
73
|
+
* })
|
|
74
|
+
* );
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
declare function createZodRegistry<TModels extends Record<string, any>>(): <TTable extends keyof TModels & string, TShape extends z.ZodRawShape>(tableName: TTable, schema: ValidateZodAgainstContract<TShape, Extract<keyof TModels[TTable], string>>) => ModelValidatorEntry;
|
|
78
|
+
/**
|
|
79
|
+
* Creates a strict validator registry factory bound to Prisma ORM 8 contract models.
|
|
80
|
+
*
|
|
81
|
+
* In addition to rejecting unknown fields, this enforces that ALL columns defined on the
|
|
82
|
+
* contract table MUST be declared in the Zod schema.
|
|
83
|
+
*
|
|
84
|
+
* @template TModels The generated Prisma 8 `Models` or `typeof models.public` type map.
|
|
85
|
+
* @returns A factory function `(tableName, schema)` producing a typed `ModelValidatorEntry`.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* import { z } from "zod";
|
|
90
|
+
* import { createStrictZodRegistry } from "prisma-extension-autozod";
|
|
91
|
+
* import type { models } from "./contract.d.ts";
|
|
92
|
+
*
|
|
93
|
+
* const defineStrict = createStrictZodRegistry<typeof models.public>();
|
|
94
|
+
*
|
|
95
|
+
* // Compile error if any column from the table contract is omitted:
|
|
96
|
+
* export const userValidator = defineStrict(
|
|
97
|
+
* "User",
|
|
98
|
+
* z.object({
|
|
99
|
+
* id: z.number(),
|
|
100
|
+
* email: z.string().email(),
|
|
101
|
+
* username: z.string().nullable(),
|
|
102
|
+
* name: z.string().nullable(),
|
|
103
|
+
* createdAt: z.date(),
|
|
104
|
+
* updatedAt: z.date(),
|
|
105
|
+
* })
|
|
106
|
+
* );
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
declare function createStrictZodRegistry<TModels extends Record<string, any>>(): <TTable extends keyof TModels & string, TShape extends z.ZodRawShape>(tableName: TTable, schema: [InvalidContractKeys<TShape, Extract<keyof TModels[TTable], string>>] extends [never] ? [MissingContractKeys<TShape, Extract<keyof TModels[TTable], string>>] extends [never] ? z.ZodObject<TShape> : { [K in MissingContractKeys<TShape, Extract<keyof TModels[TTable], string>>]: `[Prisma8-Contract-Error]: Missing required table column '${K}' in schema`; } : { [K in keyof TShape]: ResolveColumn<Extract<K, string>, Extract<keyof TModels[TTable], string>> extends never ? `[Prisma8-Contract-Error]: Field '${Extract<K, string>}' does not exist in table contract` : any; }) => ModelValidatorEntry;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Shape of the AutoZod SQL middleware compatible with Prisma ORM 8 clients.
|
|
113
|
+
*/
|
|
114
|
+
interface AutoZodMiddleware {
|
|
115
|
+
/**
|
|
116
|
+
* Unique identifier name for the middleware.
|
|
117
|
+
*/
|
|
118
|
+
name: string;
|
|
119
|
+
/**
|
|
120
|
+
* Target storage engine family. Always 'sql' for PostgreSQL.
|
|
121
|
+
*/
|
|
122
|
+
familyId: "sql";
|
|
123
|
+
/**
|
|
124
|
+
* Lifecycle hook invoked prior to SQL plan compilation.
|
|
125
|
+
* Intercepts and validates AST mutation rows and values.
|
|
126
|
+
*/
|
|
127
|
+
beforeCompile(draft: any, ctx: any): Promise<undefined>;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Normalizes a key by stripping hyphens, underscores, and special characters,
|
|
131
|
+
* and converting to lowercase.
|
|
132
|
+
*
|
|
133
|
+
* @param key The column or table string to normalize.
|
|
134
|
+
* @returns A sanitized lowercase alphanumeric string.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* normalizeKey("first_name"); // "firstname"
|
|
139
|
+
* normalizeKey("User-Profile"); // "userprofile"
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
declare function normalizeKey(key: string): string;
|
|
143
|
+
/**
|
|
144
|
+
* Inspects a compiled Prisma ORM 8 `contract.json` object and extracts a
|
|
145
|
+
* dictionary of normalized table names mapped to their column list.
|
|
146
|
+
*
|
|
147
|
+
* @param contractJson The parsed Prisma 8 contract JSON object.
|
|
148
|
+
* @returns A mapping from normalized table names to array of column names.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* import contractJson from "./contract.json" with { type: "json" };
|
|
153
|
+
*
|
|
154
|
+
* const columns = extractColumns(contractJson);
|
|
155
|
+
* console.log(columns["user"]); // ["id", "email", "name", ...]
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
declare function extractColumns(contractJson: any): Record<string, string[]>;
|
|
159
|
+
/**
|
|
160
|
+
* Factory creating Prisma ORM 8 SQL middleware for automatic runtime Zod validation.
|
|
161
|
+
*
|
|
162
|
+
* Hooks into the query compiler via `beforeCompile` and intercepts `insert` and `update`
|
|
163
|
+
* operations. Maps columns according to the contract, validates payload values against
|
|
164
|
+
* registered Zod schemas, and throws `ZodError` before SQL execution if invalid.
|
|
165
|
+
*
|
|
166
|
+
* @param options.contract The parsed Prisma 8 `contract.json` object.
|
|
167
|
+
* @param options.validators An array of validator entries produced by `createZodRegistry`.
|
|
168
|
+
* @returns An `AutoZodMiddleware` instance ready to be passed to `postgres({ middleware: [...] })`.
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```typescript
|
|
172
|
+
* import postgres from "@prisma/orm-postgres/runtime";
|
|
173
|
+
* import { contractFromJson } from "@prisma/orm-postgres/static";
|
|
174
|
+
* import { autoZodPrisma8 } from "prisma-extension-autozod";
|
|
175
|
+
*
|
|
176
|
+
* import contractJson from "./contract.json" with { type: "json" };
|
|
177
|
+
* import { userValidator } from "./schemas.ts";
|
|
178
|
+
*
|
|
179
|
+
* export const contract = contractFromJson(contractJson);
|
|
180
|
+
*
|
|
181
|
+
* export const db = postgres({
|
|
182
|
+
* contract,
|
|
183
|
+
* url: process.env.DATABASE_URL!,
|
|
184
|
+
* middleware: [
|
|
185
|
+
* autoZodPrisma8({
|
|
186
|
+
* contract: contractJson,
|
|
187
|
+
* validators: [userValidator],
|
|
188
|
+
* }),
|
|
189
|
+
* ],
|
|
190
|
+
* });
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
declare function autoZodPrisma8(options: {
|
|
194
|
+
contract: any;
|
|
195
|
+
validators: ModelValidatorEntry[];
|
|
196
|
+
}): AutoZodMiddleware;
|
|
197
|
+
/**
|
|
198
|
+
* Convenient alias for `autoZodPrisma8`.
|
|
199
|
+
*/
|
|
200
|
+
declare const autoZod: typeof autoZodPrisma8;
|
|
201
|
+
|
|
202
|
+
export { autoZod, autoZodPrisma8, createStrictZodRegistry, createZodRegistry, extractColumns, normalizeKey };
|
|
203
|
+
export type { AutoZodMiddleware, CleanKey, InvalidContractKeys, MissingContractKeys, ModelValidatorEntry, ResolveColumn, ValidateZodAgainstContract };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// src/middleware.ts
|
|
2
|
+
function normalizeKey(key) {
|
|
3
|
+
return key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
4
|
+
}
|
|
5
|
+
function extractValue(val) {
|
|
6
|
+
if (val && typeof val === "object" && "value" in val) {
|
|
7
|
+
return val.value;
|
|
8
|
+
}
|
|
9
|
+
return val;
|
|
10
|
+
}
|
|
11
|
+
function extractColumns(contractJson) {
|
|
12
|
+
const tableCols = {};
|
|
13
|
+
const namespaces = contractJson?.storage?.namespaces || {};
|
|
14
|
+
for (const ns of Object.values(namespaces)) {
|
|
15
|
+
const tables = ns?.entries?.table || ns?.tables || {};
|
|
16
|
+
for (const [table, def] of Object.entries(tables)) {
|
|
17
|
+
tableCols[normalizeKey(table)] = Object.keys(def?.columns || {});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return tableCols;
|
|
21
|
+
}
|
|
22
|
+
function autoZodPrisma8(options) {
|
|
23
|
+
const columnCatalog = extractColumns(options.contract);
|
|
24
|
+
const validatorMap = /* @__PURE__ */ new Map();
|
|
25
|
+
for (const entry of options.validators) {
|
|
26
|
+
validatorMap.set(normalizeKey(entry.tableName), entry.schema);
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
name: "autozod-validator",
|
|
30
|
+
familyId: "sql",
|
|
31
|
+
async beforeCompile(draft, ctx) {
|
|
32
|
+
const ast = draft?.ast;
|
|
33
|
+
if (!ast || ast.kind !== "insert" && ast.kind !== "update") return void 0;
|
|
34
|
+
const target = ast.table?.name;
|
|
35
|
+
if (!target) return void 0;
|
|
36
|
+
const cleanTarget = normalizeKey(target);
|
|
37
|
+
const schema = validatorMap.get(cleanTarget);
|
|
38
|
+
if (!schema) return void 0;
|
|
39
|
+
const targetCols = columnCatalog[cleanTarget] || [];
|
|
40
|
+
const colLookup = /* @__PURE__ */ new Map();
|
|
41
|
+
for (const col of targetCols) {
|
|
42
|
+
colLookup.set(normalizeKey(col), col);
|
|
43
|
+
}
|
|
44
|
+
const validateFields = (rawRecord, isUpdate) => {
|
|
45
|
+
const extracted = {};
|
|
46
|
+
for (const [col, val] of Object.entries(rawRecord)) {
|
|
47
|
+
const actualCol = colLookup.get(normalizeKey(col)) || col;
|
|
48
|
+
extracted[actualCol] = extractValue(val);
|
|
49
|
+
}
|
|
50
|
+
const schemaShape = schema.shape || {};
|
|
51
|
+
const schemaKeys = new Set(Object.keys(schemaShape));
|
|
52
|
+
const toValidate = {};
|
|
53
|
+
for (const [k, v] of Object.entries(extracted)) {
|
|
54
|
+
if (schemaKeys.has(k)) {
|
|
55
|
+
toValidate[k] = v;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const activeSchema = isUpdate ? schema.partial() : schema;
|
|
59
|
+
activeSchema.parse(toValidate);
|
|
60
|
+
};
|
|
61
|
+
if (ast.kind === "insert" && Array.isArray(ast.rows)) {
|
|
62
|
+
for (const row of ast.rows) {
|
|
63
|
+
validateFields(row, false);
|
|
64
|
+
}
|
|
65
|
+
} else if (ast.kind === "update" && ast.set) {
|
|
66
|
+
validateFields(ast.set, true);
|
|
67
|
+
}
|
|
68
|
+
return void 0;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
var autoZod = autoZodPrisma8;
|
|
73
|
+
|
|
74
|
+
// src/registry.ts
|
|
75
|
+
function createZodRegistry() {
|
|
76
|
+
return function(tableName, schema) {
|
|
77
|
+
return {
|
|
78
|
+
tableName,
|
|
79
|
+
schema
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function createStrictZodRegistry() {
|
|
84
|
+
return function(tableName, schema) {
|
|
85
|
+
return {
|
|
86
|
+
tableName,
|
|
87
|
+
schema
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export {
|
|
92
|
+
autoZod,
|
|
93
|
+
autoZodPrisma8,
|
|
94
|
+
createStrictZodRegistry,
|
|
95
|
+
createZodRegistry,
|
|
96
|
+
extractColumns,
|
|
97
|
+
normalizeKey
|
|
98
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,69 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prisma-extension-autozod",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Contract-aware compile-time linting and runtime Zod validation for Prisma ORM 8 (@prisma/orm-postgres)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=24.0.0"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"prepublishOnly": "npm run typecheck && npm run test && npm run build"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"prisma",
|
|
31
|
+
"prisma8",
|
|
32
|
+
"orm-postgres",
|
|
33
|
+
"zod",
|
|
34
|
+
"validation",
|
|
35
|
+
"middleware",
|
|
36
|
+
"autozod",
|
|
37
|
+
"orm",
|
|
38
|
+
"contract-linting",
|
|
39
|
+
"prisma-orm-extension"
|
|
40
|
+
],
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"author": {
|
|
43
|
+
"name": "Mohammed Thamer",
|
|
44
|
+
"email": "mt20021911@gmail.com",
|
|
45
|
+
"url": "https://github.com/abzr0o"
|
|
46
|
+
},
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+https://github.com/abzr0o/prisma-extension-autozod.git"
|
|
50
|
+
},
|
|
51
|
+
"bugs": {
|
|
52
|
+
"url": "https://github.com/abzr0o/prisma-extension-autozod/issues"
|
|
53
|
+
},
|
|
54
|
+
"homepage": "https://github.com/abzr0o/prisma-extension-autozod#readme",
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"@prisma/orm-postgres": "^8.0.0",
|
|
57
|
+
"zod": "^4.0.0"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@prisma/orm-postgres": "8.0.0-rc.22",
|
|
61
|
+
"tsup": "^9.0.0",
|
|
62
|
+
"typescript": "5.8.2",
|
|
63
|
+
"vitest": "^5.1.0"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@prisma/orm-family-sql": "8.0.0-rc.22",
|
|
67
|
+
"zod": "^4.6.7"
|
|
68
|
+
}
|
|
69
|
+
}
|