prisma-effect-schema 0.1.1
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 +21 -0
- package/README.md +176 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +4 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-25MM3WYP.js +416 -0
- package/dist/chunk-25MM3WYP.js.map +1 -0
- package/dist/chunk-AV5EXPPU.js +55 -0
- package/dist/chunk-AV5EXPPU.js.map +1 -0
- package/dist/generator.d.ts +2 -0
- package/dist/generator.js +3 -0
- package/dist/generator.js.map +1 -0
- package/dist/index.d.ts +231 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/package.json +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Frontcore
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# prisma-effect-schema
|
|
2
|
+
|
|
3
|
+
A Prisma generator that creates [Effect Schema](https://effect.website/docs/schema/introduction) definitions from your Prisma models.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Generates Effect Schemas for all Prisma models and enums
|
|
8
|
+
- Creates branded ID types for type-safe entity references
|
|
9
|
+
- Handles all Prisma scalar types (String, Int, Float, Boolean, DateTime, Json, Bytes, BigInt, Decimal)
|
|
10
|
+
- Deterministic output (sorted fields/models) to minimize git diffs
|
|
11
|
+
- No timestamps in generated files to avoid unnecessary churn
|
|
12
|
+
- Configurable via Prisma schema
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install prisma-effect-schema
|
|
18
|
+
# or
|
|
19
|
+
pnpm add prisma-effect-schema
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
Add the generator to your `schema.prisma`:
|
|
25
|
+
|
|
26
|
+
```prisma
|
|
27
|
+
generator client {
|
|
28
|
+
provider = "prisma-client-js"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
generator effectSchema {
|
|
32
|
+
provider = "prisma-effect-schema"
|
|
33
|
+
output = "./generated/effect-schemas.ts"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
datasource db {
|
|
37
|
+
provider = "postgresql"
|
|
38
|
+
url = env("DATABASE_URL")
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
model User {
|
|
42
|
+
id String @id @default(cuid())
|
|
43
|
+
email String @unique
|
|
44
|
+
name String?
|
|
45
|
+
posts Post[]
|
|
46
|
+
createdAt DateTime @default(now())
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
model Post {
|
|
50
|
+
id String @id @default(cuid())
|
|
51
|
+
title String
|
|
52
|
+
content String?
|
|
53
|
+
published Boolean @default(false)
|
|
54
|
+
authorId String
|
|
55
|
+
author User @relation(fields: [authorId], references: [id])
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Then run:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npx prisma generate
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
This will generate `effect-schemas.ts` with:
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { Schema } from "effect";
|
|
69
|
+
|
|
70
|
+
// Branded IDs
|
|
71
|
+
export const UserId = Schema.String.pipe(Schema.brand("UserId"));
|
|
72
|
+
export type UserId = typeof UserId.Type;
|
|
73
|
+
|
|
74
|
+
export const PostId = Schema.String.pipe(Schema.brand("PostId"));
|
|
75
|
+
export type PostId = typeof PostId.Type;
|
|
76
|
+
|
|
77
|
+
// Models
|
|
78
|
+
export const User = Schema.Struct({
|
|
79
|
+
id: UserId,
|
|
80
|
+
email: Schema.String,
|
|
81
|
+
name: Schema.NullOr(Schema.String),
|
|
82
|
+
createdAt: Schema.Date,
|
|
83
|
+
});
|
|
84
|
+
export type User = typeof User.Type;
|
|
85
|
+
|
|
86
|
+
export const Post = Schema.Struct({
|
|
87
|
+
id: PostId,
|
|
88
|
+
title: Schema.String,
|
|
89
|
+
content: Schema.NullOr(Schema.String),
|
|
90
|
+
published: Schema.Boolean,
|
|
91
|
+
authorId: UserId, // References User's branded ID
|
|
92
|
+
});
|
|
93
|
+
export type Post = typeof Post.Type;
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Configuration Options
|
|
97
|
+
|
|
98
|
+
```prisma
|
|
99
|
+
generator effectSchema {
|
|
100
|
+
provider = "prisma-effect-schema"
|
|
101
|
+
output = "./generated/effect-schemas.ts"
|
|
102
|
+
|
|
103
|
+
// Include relation fields (uses Schema.suspend for circular refs)
|
|
104
|
+
// Default: false
|
|
105
|
+
includeRelations = "true"
|
|
106
|
+
|
|
107
|
+
// Generate branded ID types (UserId, PostId, etc.)
|
|
108
|
+
// Default: true
|
|
109
|
+
useBrandedIds = "true"
|
|
110
|
+
|
|
111
|
+
// Sort fields alphabetically for deterministic output
|
|
112
|
+
// Default: true
|
|
113
|
+
sortFields = "true"
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Type Mapping
|
|
118
|
+
|
|
119
|
+
| Prisma Type | Effect Schema |
|
|
120
|
+
| -------------- | ----------------------------- |
|
|
121
|
+
| `String` | `Schema.String` |
|
|
122
|
+
| `Int` | `Schema.Int` |
|
|
123
|
+
| `Float` | `Schema.Number` |
|
|
124
|
+
| `Boolean` | `Schema.Boolean` |
|
|
125
|
+
| `DateTime` | `Schema.Date` |
|
|
126
|
+
| `Json` | `JsonValueSchema` (recursive) |
|
|
127
|
+
| `Bytes` | `Schema.Uint8Array` |
|
|
128
|
+
| `BigInt` | `Schema.BigInt` |
|
|
129
|
+
| `Decimal` | `Schema.String` |
|
|
130
|
+
| `Enum` | `Schema.Literal(...)` |
|
|
131
|
+
| Optional (`?`) | `Schema.NullOr(...)` |
|
|
132
|
+
| List (`[]`) | `Schema.Array(...)` |
|
|
133
|
+
|
|
134
|
+
## Date Handling
|
|
135
|
+
|
|
136
|
+
By default, the generator uses `Schema.Date` which expects JavaScript `Date` objects. This matches what Prisma returns from database queries.
|
|
137
|
+
|
|
138
|
+
If you're validating API input where dates come as ISO strings, you can compose the schema:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
import { Schema } from "effect";
|
|
142
|
+
import { User } from "./generated/effect-schemas";
|
|
143
|
+
|
|
144
|
+
// For API input validation
|
|
145
|
+
const UserInput = User.pipe(
|
|
146
|
+
Schema.transform(User, {
|
|
147
|
+
decode: (input) => ({
|
|
148
|
+
...input,
|
|
149
|
+
createdAt: new Date(input.createdAt),
|
|
150
|
+
}),
|
|
151
|
+
encode: (user) => ({
|
|
152
|
+
...user,
|
|
153
|
+
createdAt: user.createdAt.toISOString(),
|
|
154
|
+
}),
|
|
155
|
+
}),
|
|
156
|
+
);
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Programmatic API
|
|
160
|
+
|
|
161
|
+
You can also use the generator programmatically:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { generate, resolveConfig } from "prisma-effect-schema";
|
|
165
|
+
import { getDMMF } from "@prisma/sdk";
|
|
166
|
+
|
|
167
|
+
const dmmf = await getDMMF({ datamodelPath: "./prisma/schema.prisma" });
|
|
168
|
+
const config = resolveConfig({ useBrandedIds: true });
|
|
169
|
+
|
|
170
|
+
const { content, stats } = generate({ dmmf, config });
|
|
171
|
+
console.log(`Generated ${stats.modelCount} models`);
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
MIT
|
package/dist/bin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/bin.js
ADDED
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
import { Array as Arr, Option, pipe, Schema } from "effect";
|
|
3
|
+
var firstString = (value) => pipe(value, Arr.ensure, Arr.head);
|
|
4
|
+
var BooleanFromString = Schema.transform(
|
|
5
|
+
Schema.Union(Schema.String, Schema.Array(Schema.String)),
|
|
6
|
+
Schema.Boolean,
|
|
7
|
+
{
|
|
8
|
+
decode: (value) => pipe(value, firstString, Option.map((s) => s === "true"), Option.getOrElse(() => false)),
|
|
9
|
+
encode: (b) => b ? "true" : "false"
|
|
10
|
+
}
|
|
11
|
+
);
|
|
12
|
+
var DateTimeHandling = Schema.transform(
|
|
13
|
+
Schema.Union(Schema.String, Schema.Array(Schema.String)),
|
|
14
|
+
Schema.Literal("Date", "DateTimeString"),
|
|
15
|
+
{
|
|
16
|
+
decode: (value) => pipe(
|
|
17
|
+
value,
|
|
18
|
+
firstString,
|
|
19
|
+
Option.filter((s) => s === "DateTimeString"),
|
|
20
|
+
Option.map(() => "DateTimeString"),
|
|
21
|
+
Option.getOrElse(() => "Date")
|
|
22
|
+
),
|
|
23
|
+
encode: (s) => s
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
var GeneratorConfigSchema = Schema.Struct({
|
|
27
|
+
/**
|
|
28
|
+
* Whether to include relation fields in the generated schemas.
|
|
29
|
+
* Relations use Schema.suspend() for lazy evaluation to handle circular deps.
|
|
30
|
+
* @default false
|
|
31
|
+
*/
|
|
32
|
+
includeRelations: Schema.optionalWith(BooleanFromString, {
|
|
33
|
+
default: () => false
|
|
34
|
+
}),
|
|
35
|
+
/**
|
|
36
|
+
* Whether to generate branded ID types for models with string IDs.
|
|
37
|
+
* When true, generates `UserId`, `PostId`, etc. and uses them in model schemas.
|
|
38
|
+
* @default true
|
|
39
|
+
*/
|
|
40
|
+
useBrandedIds: Schema.optionalWith(BooleanFromString, {
|
|
41
|
+
default: () => true
|
|
42
|
+
}),
|
|
43
|
+
/**
|
|
44
|
+
* How to handle DateTime fields.
|
|
45
|
+
* - 'Date': Use Schema.Date (expects Date objects, for Prisma results)
|
|
46
|
+
* - 'DateTimeString': Use Schema.Date with dateTime annotation (for API validation)
|
|
47
|
+
* @default 'Date'
|
|
48
|
+
*/
|
|
49
|
+
dateTimeHandling: Schema.optionalWith(DateTimeHandling, {
|
|
50
|
+
default: () => "Date"
|
|
51
|
+
}),
|
|
52
|
+
/**
|
|
53
|
+
* Whether to sort fields alphabetically for deterministic output.
|
|
54
|
+
* @default true
|
|
55
|
+
*/
|
|
56
|
+
sortFields: Schema.optionalWith(BooleanFromString, {
|
|
57
|
+
default: () => true
|
|
58
|
+
}),
|
|
59
|
+
/**
|
|
60
|
+
* Custom header to prepend to the generated file.
|
|
61
|
+
* If not provided, uses a default header without timestamps.
|
|
62
|
+
*/
|
|
63
|
+
customHeader: Schema.optionalWith(
|
|
64
|
+
Schema.transform(
|
|
65
|
+
Schema.Union(Schema.String, Schema.Array(Schema.String)),
|
|
66
|
+
Schema.NullOr(Schema.String),
|
|
67
|
+
{
|
|
68
|
+
decode: (value) => pipe(value, firstString, Option.getOrElse(() => null)),
|
|
69
|
+
encode: (s) => s ?? ""
|
|
70
|
+
}
|
|
71
|
+
),
|
|
72
|
+
{ default: () => null }
|
|
73
|
+
)
|
|
74
|
+
});
|
|
75
|
+
var parseConfig = (options) => Schema.decodeUnknown(GeneratorConfigSchema)(options.generator.config);
|
|
76
|
+
|
|
77
|
+
// src/errors.ts
|
|
78
|
+
import dedent from "dedent";
|
|
79
|
+
import { Schema as Schema2 } from "effect";
|
|
80
|
+
var AppTag = "[prisma-effect-schema]";
|
|
81
|
+
var UnsupportedTypeError = class extends Schema2.TaggedError()(
|
|
82
|
+
"UnsupportedTypeError",
|
|
83
|
+
{
|
|
84
|
+
typeName: Schema2.String,
|
|
85
|
+
fieldName: Schema2.String,
|
|
86
|
+
modelName: Schema2.String
|
|
87
|
+
}
|
|
88
|
+
) {
|
|
89
|
+
get message() {
|
|
90
|
+
return dedent`
|
|
91
|
+
${AppTag} Unsupported Prisma type "${this.typeName}" for field "${this.fieldName}" in model "${this.modelName}".
|
|
92
|
+
Please open an issue at https://github.com/frontcore/prisma-effect-schema/issues
|
|
93
|
+
`;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
var NoOutputConfiguredError = class extends Schema2.TaggedError()(
|
|
97
|
+
"NoOutputConfiguredError",
|
|
98
|
+
{
|
|
99
|
+
cause: Schema2.Unknown,
|
|
100
|
+
details: Schema2.String
|
|
101
|
+
}
|
|
102
|
+
) {
|
|
103
|
+
static message = `${AppTag} No output path specified in generator config`;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// src/emit.ts
|
|
107
|
+
import { Match } from "effect";
|
|
108
|
+
var emitBaseType = (base) => Match.value(base).pipe(
|
|
109
|
+
Match.tag(
|
|
110
|
+
"Primitive",
|
|
111
|
+
({ schema }) => schema === "Json" ? "JsonValueSchema" : `Schema.${schema}`
|
|
112
|
+
),
|
|
113
|
+
Match.tag("BrandedId", ({ name }) => name),
|
|
114
|
+
Match.tag("Enum", ({ name }) => name),
|
|
115
|
+
Match.tag("Relation", ({ modelName }) => `Schema.suspend(() => ${modelName})`),
|
|
116
|
+
Match.exhaustive
|
|
117
|
+
);
|
|
118
|
+
var applyWrapper = (inner, wrapper) => {
|
|
119
|
+
switch (wrapper) {
|
|
120
|
+
case "Array":
|
|
121
|
+
return `Schema.Array(${inner})`;
|
|
122
|
+
case "NullOr":
|
|
123
|
+
return `Schema.NullOr(${inner})`;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
var emit = (type) => type.wrappers.reduce(applyWrapper, emitBaseType(type.base));
|
|
127
|
+
|
|
128
|
+
// src/resolver.ts
|
|
129
|
+
import {
|
|
130
|
+
Array as Arr2,
|
|
131
|
+
Data,
|
|
132
|
+
HashMap,
|
|
133
|
+
Option as Option2,
|
|
134
|
+
pipe as pipe2,
|
|
135
|
+
Record
|
|
136
|
+
} from "effect";
|
|
137
|
+
import { capitalize } from "effect/String";
|
|
138
|
+
var Primitive = class extends Data.TaggedClass("Primitive") {
|
|
139
|
+
};
|
|
140
|
+
var BrandedId = class extends Data.TaggedClass("BrandedId") {
|
|
141
|
+
};
|
|
142
|
+
var Enum = class extends Data.TaggedClass("Enum") {
|
|
143
|
+
};
|
|
144
|
+
var Relation = class extends Data.TaggedClass("Relation") {
|
|
145
|
+
};
|
|
146
|
+
var ResolvedType = class extends Data.Class {
|
|
147
|
+
};
|
|
148
|
+
var ScalarTypeMap = {
|
|
149
|
+
Int: "Int",
|
|
150
|
+
Float: "Number",
|
|
151
|
+
Boolean: "Boolean",
|
|
152
|
+
Json: "Json",
|
|
153
|
+
Bytes: "Uint8Array",
|
|
154
|
+
BigInt: "BigInt",
|
|
155
|
+
Decimal: "Decimal"
|
|
156
|
+
};
|
|
157
|
+
var collectBrandedIds = (models) => pipe2(
|
|
158
|
+
models,
|
|
159
|
+
Arr2.filterMap((model) => {
|
|
160
|
+
const pkField = model.fields.find((f) => f.isId && f.type === "String");
|
|
161
|
+
if (!pkField) return Option2.none();
|
|
162
|
+
const suffix = capitalize(pkField.name);
|
|
163
|
+
return Option2.some([model.name, `${model.name}${suffix}`]);
|
|
164
|
+
}),
|
|
165
|
+
HashMap.fromIterable
|
|
166
|
+
);
|
|
167
|
+
var buildForeignKeyMap = (models) => pipe2(
|
|
168
|
+
models,
|
|
169
|
+
Arr2.flatMap(
|
|
170
|
+
(model) => pipe2(
|
|
171
|
+
model.fields,
|
|
172
|
+
Arr2.filter((field) => field.kind === "object"),
|
|
173
|
+
Arr2.flatMap((relationField) => {
|
|
174
|
+
const fkFields = relationField.relationFromFields ?? [];
|
|
175
|
+
const targetModel = relationField.type;
|
|
176
|
+
return fkFields.map((fkField) => [fkField, targetModel]);
|
|
177
|
+
})
|
|
178
|
+
)
|
|
179
|
+
),
|
|
180
|
+
HashMap.fromIterable
|
|
181
|
+
);
|
|
182
|
+
var SchemaResolver = {
|
|
183
|
+
make: (resolverConfig) => {
|
|
184
|
+
const { modelName, brandedIds, foreignKeys, config } = resolverConfig;
|
|
185
|
+
const resolveBrandedId = (field) => {
|
|
186
|
+
if (!config.useBrandedIds) return Option2.none();
|
|
187
|
+
if (field.isId) {
|
|
188
|
+
return HashMap.get(brandedIds, modelName);
|
|
189
|
+
}
|
|
190
|
+
return pipe2(
|
|
191
|
+
HashMap.get(foreignKeys, field.name),
|
|
192
|
+
Option2.flatMap((targetModel) => HashMap.get(brandedIds, targetModel))
|
|
193
|
+
);
|
|
194
|
+
};
|
|
195
|
+
const resolveScalarType = (field) => {
|
|
196
|
+
if (field.type === "DateTime") {
|
|
197
|
+
return new Primitive({
|
|
198
|
+
schema: config.dateTimeHandling === "DateTimeString" ? "DateTimeUtc" : "Date"
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
const mapping = Record.get(ScalarTypeMap, field.type);
|
|
202
|
+
if (Option2.isSome(mapping)) {
|
|
203
|
+
return new Primitive({ schema: mapping.value });
|
|
204
|
+
}
|
|
205
|
+
if (field.type === "String") {
|
|
206
|
+
return pipe2(
|
|
207
|
+
resolveBrandedId(field),
|
|
208
|
+
Option2.match({
|
|
209
|
+
onNone: () => new Primitive({ schema: "String" }),
|
|
210
|
+
onSome: (name) => new BrandedId({ name })
|
|
211
|
+
})
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
throw new UnsupportedTypeError({
|
|
215
|
+
typeName: field.type,
|
|
216
|
+
fieldName: field.name,
|
|
217
|
+
modelName
|
|
218
|
+
});
|
|
219
|
+
};
|
|
220
|
+
const resolveBaseType = (field) => {
|
|
221
|
+
switch (field.kind) {
|
|
222
|
+
case "enum":
|
|
223
|
+
return new Enum({ name: field.type });
|
|
224
|
+
case "object":
|
|
225
|
+
return new Relation({ modelName: field.type });
|
|
226
|
+
default:
|
|
227
|
+
return resolveScalarType(field);
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
const resolve = (field) => {
|
|
231
|
+
const wrappers = [];
|
|
232
|
+
if (field.isList) {
|
|
233
|
+
wrappers.push("Array");
|
|
234
|
+
}
|
|
235
|
+
if (!field.isRequired) {
|
|
236
|
+
wrappers.push("NullOr");
|
|
237
|
+
}
|
|
238
|
+
return new ResolvedType({
|
|
239
|
+
base: resolveBaseType(field),
|
|
240
|
+
wrappers
|
|
241
|
+
});
|
|
242
|
+
};
|
|
243
|
+
const fieldToSchema = (field) => emit(resolve(field));
|
|
244
|
+
return {
|
|
245
|
+
resolve,
|
|
246
|
+
fieldToSchema,
|
|
247
|
+
brandedIds
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// src/templates.ts
|
|
253
|
+
import dedent2 from "dedent";
|
|
254
|
+
import { Array as Arr3, HashMap as HashMap2, Order, pipe as pipe3 } from "effect";
|
|
255
|
+
var DEFAULT_HEADER = dedent2`
|
|
256
|
+
// This file was auto-generated by prisma-effect-schema
|
|
257
|
+
// Do not edit manually - changes will be overwritten
|
|
258
|
+
// https://github.com/frontcore/prisma-effect-schema
|
|
259
|
+
|
|
260
|
+
import { Schema } from "effect"
|
|
261
|
+
`;
|
|
262
|
+
var JSON_VALUE_SCHEMA = dedent2`
|
|
263
|
+
// Recursive JSON value schema matching Prisma's JsonValue type
|
|
264
|
+
type JsonValue = string | number | boolean | null | JsonArray | JsonObject
|
|
265
|
+
type JsonArray = ReadonlyArray<JsonValue>
|
|
266
|
+
type JsonObject = { readonly [key: string]: JsonValue }
|
|
267
|
+
|
|
268
|
+
const JsonValueSchema: Schema.Schema<JsonValue> = Schema.suspend(
|
|
269
|
+
(): Schema.Schema<JsonValue> =>
|
|
270
|
+
Schema.Union(
|
|
271
|
+
Schema.Null,
|
|
272
|
+
Schema.Boolean,
|
|
273
|
+
Schema.Number,
|
|
274
|
+
Schema.String,
|
|
275
|
+
Schema.Array(JsonValueSchema),
|
|
276
|
+
Schema.Record({ key: Schema.String, value: JsonValueSchema })
|
|
277
|
+
)
|
|
278
|
+
)
|
|
279
|
+
`;
|
|
280
|
+
var sectionHeader = (title) => dedent2`
|
|
281
|
+
// ============================================================================
|
|
282
|
+
// ${title}
|
|
283
|
+
// ============================================================================
|
|
284
|
+
|
|
285
|
+
`;
|
|
286
|
+
var ByName = () => Order.mapInput(Order.string, (item) => item.name);
|
|
287
|
+
var sortByName = (items) => Arr3.sort(items, ByName());
|
|
288
|
+
var generateEnumSchema = (enumDef) => {
|
|
289
|
+
const values = pipe3(
|
|
290
|
+
sortByName(enumDef.values),
|
|
291
|
+
Arr3.map((v) => `"${v.name}"`),
|
|
292
|
+
Arr3.join(", ")
|
|
293
|
+
);
|
|
294
|
+
return dedent2`
|
|
295
|
+
export const ${enumDef.name} = Schema.Literal(${values})
|
|
296
|
+
export type ${enumDef.name} = typeof ${enumDef.name}.Type
|
|
297
|
+
`;
|
|
298
|
+
};
|
|
299
|
+
var generateEnumSchemas = (enums) => pipe3(sortByName(enums), Arr3.map(generateEnumSchema), Arr3.join("\n"));
|
|
300
|
+
var generateBrandedIdSchema = ([, brandedIdName]) => dedent2`
|
|
301
|
+
export const ${brandedIdName} = Schema.String.pipe(Schema.brand("${brandedIdName}"))
|
|
302
|
+
export type ${brandedIdName} = typeof ${brandedIdName}.Type
|
|
303
|
+
`;
|
|
304
|
+
var generateBrandedIdSchemas = (brandedIds) => pipe3(
|
|
305
|
+
brandedIds,
|
|
306
|
+
HashMap2.toEntries,
|
|
307
|
+
Arr3.sort(Order.mapInput(Order.string, ([key]) => key)),
|
|
308
|
+
Arr3.map(generateBrandedIdSchema),
|
|
309
|
+
Arr3.join("\n\n")
|
|
310
|
+
);
|
|
311
|
+
var generateFieldsCode = (fields, resolver, sortFields) => pipe3(
|
|
312
|
+
sortFields ? sortByName(fields) : fields,
|
|
313
|
+
Arr3.map((field) => ` ${field.name}: ${resolver.fieldToSchema(field)}`),
|
|
314
|
+
Arr3.join(",\n")
|
|
315
|
+
);
|
|
316
|
+
var generateModelSchema = (model, resolver, config) => {
|
|
317
|
+
const scalarFields = model.fields.filter((f) => f.kind !== "object");
|
|
318
|
+
const hasRelations = model.fields.some((f) => f.kind === "object");
|
|
319
|
+
const scalarFieldsCode = generateFieldsCode(
|
|
320
|
+
scalarFields,
|
|
321
|
+
resolver,
|
|
322
|
+
config.sortFields
|
|
323
|
+
);
|
|
324
|
+
const baseSchema = dedent2`
|
|
325
|
+
export const ${model.name} = Schema.Struct({
|
|
326
|
+
${scalarFieldsCode}
|
|
327
|
+
})
|
|
328
|
+
export type ${model.name} = typeof ${model.name}.Type
|
|
329
|
+
`;
|
|
330
|
+
if (!config.includeRelations || !hasRelations) {
|
|
331
|
+
return baseSchema;
|
|
332
|
+
}
|
|
333
|
+
const allFieldsCode = generateFieldsCode(
|
|
334
|
+
model.fields,
|
|
335
|
+
resolver,
|
|
336
|
+
config.sortFields
|
|
337
|
+
);
|
|
338
|
+
return dedent2`
|
|
339
|
+
${baseSchema}
|
|
340
|
+
|
|
341
|
+
export const ${model.name}WithRelations = Schema.Struct({
|
|
342
|
+
${allFieldsCode}
|
|
343
|
+
})
|
|
344
|
+
export type ${model.name}WithRelations = typeof ${model.name}WithRelations.Type
|
|
345
|
+
`;
|
|
346
|
+
};
|
|
347
|
+
var generateModelSchemas = (models, makeResolver, config) => pipe3(
|
|
348
|
+
sortByName(models),
|
|
349
|
+
Arr3.map((model) => generateModelSchema(model, makeResolver(model.name), config)),
|
|
350
|
+
Arr3.join("\n")
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
// src/generate.ts
|
|
354
|
+
import { HashMap as HashMap3 } from "effect";
|
|
355
|
+
var generate = (input) => {
|
|
356
|
+
const { dmmf, config } = input;
|
|
357
|
+
const { models, enums } = dmmf.datamodel;
|
|
358
|
+
const brandedIds = config.useBrandedIds ? collectBrandedIds(models) : HashMap3.empty();
|
|
359
|
+
const foreignKeys = buildForeignKeyMap(models);
|
|
360
|
+
const brandedIdCount = HashMap3.size(brandedIds);
|
|
361
|
+
const hasJsonFields = models.some(
|
|
362
|
+
(model) => model.fields.some((field) => field.type === "Json")
|
|
363
|
+
);
|
|
364
|
+
const makeResolver = (modelName) => SchemaResolver.make({ modelName, brandedIds, foreignKeys, config });
|
|
365
|
+
const sections = [
|
|
366
|
+
// Header
|
|
367
|
+
config.customHeader ?? DEFAULT_HEADER,
|
|
368
|
+
// JSON schema (only if needed)
|
|
369
|
+
...hasJsonFields ? ["\n" + JSON_VALUE_SCHEMA] : [],
|
|
370
|
+
// Enums
|
|
371
|
+
...enums.length > 0 ? [sectionHeader("Enums"), generateEnumSchemas(enums)] : [],
|
|
372
|
+
// Branded IDs
|
|
373
|
+
...brandedIdCount > 0 ? [sectionHeader("Branded IDs"), generateBrandedIdSchemas(brandedIds), ""] : [],
|
|
374
|
+
// Models
|
|
375
|
+
sectionHeader("Models (scalar fields only)"),
|
|
376
|
+
generateModelSchemas(models, makeResolver, config)
|
|
377
|
+
];
|
|
378
|
+
return {
|
|
379
|
+
content: sections.join(""),
|
|
380
|
+
stats: {
|
|
381
|
+
enumCount: enums.length,
|
|
382
|
+
modelCount: models.length,
|
|
383
|
+
brandedIdCount
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
export {
|
|
389
|
+
GeneratorConfigSchema,
|
|
390
|
+
parseConfig,
|
|
391
|
+
UnsupportedTypeError,
|
|
392
|
+
NoOutputConfiguredError,
|
|
393
|
+
emitBaseType,
|
|
394
|
+
applyWrapper,
|
|
395
|
+
emit,
|
|
396
|
+
Primitive,
|
|
397
|
+
BrandedId,
|
|
398
|
+
Enum,
|
|
399
|
+
Relation,
|
|
400
|
+
ResolvedType,
|
|
401
|
+
collectBrandedIds,
|
|
402
|
+
buildForeignKeyMap,
|
|
403
|
+
SchemaResolver,
|
|
404
|
+
DEFAULT_HEADER,
|
|
405
|
+
JSON_VALUE_SCHEMA,
|
|
406
|
+
sectionHeader,
|
|
407
|
+
generateEnumSchema,
|
|
408
|
+
generateEnumSchemas,
|
|
409
|
+
generateBrandedIdSchema,
|
|
410
|
+
generateBrandedIdSchemas,
|
|
411
|
+
generateFieldsCode,
|
|
412
|
+
generateModelSchema,
|
|
413
|
+
generateModelSchemas,
|
|
414
|
+
generate
|
|
415
|
+
};
|
|
416
|
+
//# sourceMappingURL=chunk-25MM3WYP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts","../src/errors.ts","../src/emit.ts","../src/resolver.ts","../src/templates.ts","../src/generate.ts"],"sourcesContent":["import type { GeneratorOptions } from \"@prisma/generator-helper\";\nimport { Array as Arr, Option, pipe, Schema } from \"effect\";\n\n/**\n * Prisma config values can be string | string[] - normalize to first string\n */\nconst firstString = (value: string | readonly string[]): Option.Option<string> =>\n pipe(value, Arr.ensure, Arr.head);\n\n/**\n * Schema for parsing \"true\"/\"false\" strings to booleans (handles string | string[])\n */\nconst BooleanFromString = Schema.transform(\n Schema.Union(Schema.String, Schema.Array(Schema.String)),\n Schema.Boolean,\n {\n decode: (value) => pipe(value, firstString, Option.map((s) => s === \"true\"), Option.getOrElse(() => false)),\n encode: (b) => (b ? \"true\" : \"false\"),\n }\n);\n\n/**\n * Schema for DateTime handling mode (handles string | string[])\n */\nconst DateTimeHandling = Schema.transform(\n Schema.Union(Schema.String, Schema.Array(Schema.String)),\n Schema.Literal(\"Date\", \"DateTimeString\"),\n {\n decode: (value) =>\n pipe(\n value,\n firstString,\n Option.filter((s) => s === \"DateTimeString\"),\n Option.map(() => \"DateTimeString\" as const),\n Option.getOrElse(() => \"Date\" as const)\n ),\n encode: (s) => s,\n }\n);\n\n/**\n * Generator configuration schema with defaults.\n * Parses Prisma generator config and applies defaults in one step.\n */\nexport const GeneratorConfigSchema = Schema.Struct({\n /**\n * Whether to include relation fields in the generated schemas.\n * Relations use Schema.suspend() for lazy evaluation to handle circular deps.\n * @default false\n */\n includeRelations: Schema.optionalWith(BooleanFromString, {\n default: () => false,\n }),\n\n /**\n * Whether to generate branded ID types for models with string IDs.\n * When true, generates `UserId`, `PostId`, etc. and uses them in model schemas.\n * @default true\n */\n useBrandedIds: Schema.optionalWith(BooleanFromString, {\n default: () => true,\n }),\n\n /**\n * How to handle DateTime fields.\n * - 'Date': Use Schema.Date (expects Date objects, for Prisma results)\n * - 'DateTimeString': Use Schema.Date with dateTime annotation (for API validation)\n * @default 'Date'\n */\n dateTimeHandling: Schema.optionalWith(DateTimeHandling, {\n default: () => \"Date\" as const,\n }),\n\n /**\n * Whether to sort fields alphabetically for deterministic output.\n * @default true\n */\n sortFields: Schema.optionalWith(BooleanFromString, {\n default: () => true,\n }),\n\n /**\n * Custom header to prepend to the generated file.\n * If not provided, uses a default header without timestamps.\n */\n customHeader: Schema.optionalWith(\n Schema.transform(\n Schema.Union(Schema.String, Schema.Array(Schema.String)),\n Schema.NullOr(Schema.String),\n {\n decode: (value) => pipe(value, firstString, Option.getOrElse(() => null as string | null)),\n encode: (s) => s ?? \"\",\n }\n ),\n { default: () => null }\n ),\n});\n\n/**\n * Resolved configuration type (derived from schema)\n */\nexport type GeneratorConfig = typeof GeneratorConfigSchema.Type;\n\n\n\n/**\n * Parse generator config from Prisma schema using Effect Schema\n */\nexport const parseConfig = (options: GeneratorOptions) =>\n Schema.decodeUnknown(GeneratorConfigSchema)(options.generator.config);\n","import dedent from \"dedent\";\nimport { Schema } from \"effect\";\n\nexport const AppTag = \"[prisma-effect-schema]\";\n\n/**\n * Error thrown when an unsupported Prisma type is encountered\n */\nexport class UnsupportedTypeError extends Schema.TaggedError<UnsupportedTypeError>()(\n \"UnsupportedTypeError\",\n {\n typeName: Schema.String,\n fieldName: Schema.String,\n modelName: Schema.String,\n },\n) {\n override get message(): string {\n return dedent`\n ${AppTag} Unsupported Prisma type \"${this.typeName}\" for field \"${this.fieldName}\" in model \"${this.modelName}\". \n Please open an issue at https://github.com/frontcore/prisma-effect-schema/issues\n `;\n }\n}\n\nexport class NoOutputConfiguredError extends Schema.TaggedError<NoOutputConfiguredError>()(\n \"NoOutputConfiguredError\",\n {\n cause: Schema.Unknown,\n details: Schema.String,\n },\n) {\n public static message = `${AppTag} No output path specified in generator config`;\n}\n\nexport const ConfigError = NoOutputConfiguredError;\n","/**\n * Code Emission Module\n *\n * Pure functions for transforming resolved types into Effect Schema strings.\n * Separated from resolution logic for testability and reusability.\n */\nimport { Match } from \"effect\";\nimport type { BaseType, ResolvedType, Wrapper } from \"./resolver.js\";\n\n/**\n * Emit a base type to its Effect Schema string representation\n */\nexport const emitBaseType = (base: BaseType): string =>\n Match.value(base).pipe(\n Match.tag(\"Primitive\", ({ schema }) =>\n schema === \"Json\" ? \"JsonValueSchema\" : `Schema.${schema}`\n ),\n Match.tag(\"BrandedId\", ({ name }) => name),\n Match.tag(\"Enum\", ({ name }) => name),\n Match.tag(\"Relation\", ({ modelName }) => `Schema.suspend(() => ${modelName})`),\n Match.exhaustive\n );\n\n/**\n * Apply a single wrapper to a schema string\n */\nexport const applyWrapper = (inner: string, wrapper: Wrapper): string => {\n switch (wrapper) {\n case \"Array\":\n return `Schema.Array(${inner})`;\n case \"NullOr\":\n return `Schema.NullOr(${inner})`;\n }\n};\n\n/**\n * Emit a fully resolved type (base + wrappers) to Effect Schema string.\n * Wrappers are applied left-to-right (innermost first).\n */\nexport const emit = (type: ResolvedType): string =>\n type.wrappers.reduce(applyWrapper, emitBaseType(type.base));\n","/**\n * Type Resolution Module\n *\n * Separates the \"thinking\" (what type should this field be?) from the \"writing\"\n * (how do we emit it as a string?). Returns structured data that can be tested,\n * logged, and transformed before emission.\n */\nimport type { DMMF } from \"@prisma/generator-helper\";\nimport {\n Array as Arr,\n Data,\n HashMap,\n Option,\n pipe,\n Record,\n} from \"effect\";\nimport { capitalize } from \"effect/String\";\nimport type { GeneratorConfig } from \"./config.js\";\nimport { emit } from \"./emit.js\";\nimport { UnsupportedTypeError } from \"./errors.js\";\n\n// ============================================================================\n// Resolved Types (Data.TaggedClass for structural equality + pattern matching)\n// ============================================================================\n\n/**\n * A primitive scalar type from Prisma mapped to Effect Schema\n */\nexport class Primitive extends Data.TaggedClass(\"Primitive\")<{\n readonly schema:\n | \"Int\"\n | \"String\"\n | \"Boolean\"\n | \"Number\"\n | \"Date\" // Schema.Date - for Prisma results (Date objects)\n | \"DateTimeUtc\" // Schema.DateTimeUtc - for API validation (ISO strings)\n | \"BigInt\"\n | \"Uint8Array\"\n | \"Json\"\n | \"Decimal\";\n}> {}\n\n/**\n * A branded ID type for type-safe IDs\n */\nexport class BrandedId extends Data.TaggedClass(\"BrandedId\")<{\n readonly name: string;\n}> {}\n\n/**\n * An enum type reference\n */\nexport class Enum extends Data.TaggedClass(\"Enum\")<{\n readonly name: string;\n}> {}\n\n/**\n * A relation to another model (uses Schema.suspend for circular refs)\n */\nexport class Relation extends Data.TaggedClass(\"Relation\")<{\n readonly modelName: string;\n}> {}\n\n/**\n * Union of all possible base types a field can resolve to\n */\nexport type BaseType = Primitive | BrandedId | Enum | Relation;\n\n/**\n * Wrappers that can be applied to a base type\n */\nexport type Wrapper = \"Array\" | \"NullOr\";\n\n/**\n * A fully resolved field type: base type + wrappers to apply\n */\nexport class ResolvedType extends Data.Class<{\n readonly base: BaseType;\n readonly wrappers: readonly Wrapper[];\n}> {}\n\n// ============================================================================\n// SchemaResolver Interface\n// ============================================================================\n\nexport interface SchemaResolver {\n /**\n * Resolve a field to its structured type representation.\n * Use this for testing or when you need to inspect the decision.\n */\n readonly resolve: (field: DMMF.Field) => ResolvedType;\n\n /**\n * Convenience method: resolve + emit in one call.\n * Use this for the common case where you just need the string.\n */\n readonly fieldToSchema: (field: DMMF.Field) => string;\n\n /**\n * The computed branded IDs map (modelName -> brandedIdName).\n * Exposed for generating branded ID schema declarations.\n */\n readonly brandedIds: HashMap.HashMap<string, string>;\n}\n\n// ============================================================================\n// Internal: Scalar Type Mapping\n// ============================================================================\n\ntype PrimitiveSchema = Primitive[\"schema\"];\n\n/**\n * Maps Prisma scalar types to Primitive schema names.\n * Note: String is handled separately (may become BrandedId).\n * Note: DateTime is handled separately (respects dateTimeHandling config).\n */\nconst ScalarTypeMap: Record.ReadonlyRecord<string, PrimitiveSchema> = {\n Int: \"Int\",\n Float: \"Number\",\n Boolean: \"Boolean\",\n Json: \"Json\",\n Bytes: \"Uint8Array\",\n BigInt: \"BigInt\",\n Decimal: \"Decimal\",\n};\n\n// ============================================================================\n// Branded ID Collection (uses actual PK field name for suffix)\n// ============================================================================\n\n/**\n * Collects branded IDs for models with string primary keys.\n * Uses the actual PK field name for the suffix:\n * - User.id (String @id) -> \"UserId\"\n * - Course.slug (String @id) -> \"CourseSlug\"\n */\nexport const collectBrandedIds = (\n models: readonly DMMF.Model[]\n): HashMap.HashMap<string, string> =>\n pipe(\n models,\n Arr.filterMap((model) => {\n const pkField = model.fields.find((f) => f.isId && f.type === \"String\");\n if (!pkField) return Option.none();\n \n // Use the PK field name, capitalized: \"id\" -> \"Id\", \"slug\" -> \"Slug\"\n const suffix = capitalize(pkField.name);\n return Option.some([model.name, `${model.name}${suffix}`] as const);\n }),\n HashMap.fromIterable\n );\n\n// ============================================================================\n// Foreign Key Map (relation-based, not heuristic)\n// ============================================================================\n\n/**\n * Builds a map from FK field names to their target model names.\n * Uses DMMF relation metadata (relationFromFields) for accuracy.\n * \n * Example output:\n * {\n * \"userId\": \"User\",\n * \"authorId\": \"User\",\n * \"courseSlug\": \"Course\",\n * \"avatarId\": \"File\"\n * }\n */\nexport const buildForeignKeyMap = (\n models: readonly DMMF.Model[]\n): HashMap.HashMap<string, string> =>\n pipe(\n models,\n Arr.flatMap((model) =>\n pipe(\n model.fields,\n Arr.filter((field) => field.kind === \"object\"),\n Arr.flatMap((relationField) => {\n // relationFromFields contains the FK field names for this relation\n const fkFields = relationField.relationFromFields ?? [];\n // The relation's type is the target model name\n const targetModel = relationField.type;\n \n return fkFields.map((fkField) => [fkField, targetModel] as const);\n })\n )\n ),\n HashMap.fromIterable\n );\n\n// ============================================================================\n// SchemaResolver Factory\n// ============================================================================\n\nexport interface SchemaResolverConfig {\n readonly modelName: string;\n readonly brandedIds: HashMap.HashMap<string, string>;\n readonly foreignKeys: HashMap.HashMap<string, string>;\n readonly config: GeneratorConfig;\n}\n\n/**\n * Create a SchemaResolver for a specific model.\n * Dependencies are captured at construction time.\n */\nexport const SchemaResolver = {\n make: (resolverConfig: SchemaResolverConfig): SchemaResolver => {\n const { modelName, brandedIds, foreignKeys, config } = resolverConfig;\n\n // ========================================================================\n // Branded ID Resolution (relation-based)\n // ========================================================================\n\n const resolveBrandedId = (field: DMMF.Field): Option.Option<string> => {\n if (!config.useBrandedIds) return Option.none();\n\n // Primary key uses this model's branded ID\n if (field.isId) {\n return HashMap.get(brandedIds, modelName);\n }\n\n // Foreign key - look up in FK map, then get target model's branded ID\n return pipe(\n HashMap.get(foreignKeys, field.name),\n Option.flatMap((targetModel) => HashMap.get(brandedIds, targetModel))\n );\n };\n\n // ========================================================================\n // Base Type Resolution\n // ========================================================================\n\n const resolveScalarType = (field: DMMF.Field): BaseType => {\n // Handle DateTime with config\n if (field.type === \"DateTime\") {\n return new Primitive({\n schema: config.dateTimeHandling === \"DateTimeString\" ? \"DateTimeUtc\" : \"Date\",\n });\n }\n\n // Check non-String scalar types\n const mapping = Record.get(ScalarTypeMap, field.type);\n if (Option.isSome(mapping)) {\n return new Primitive({ schema: mapping.value });\n }\n\n // String type: try branded ID, fallback to Primitive String\n if (field.type === \"String\") {\n return pipe(\n resolveBrandedId(field),\n Option.match({\n onNone: () => new Primitive({ schema: \"String\" }),\n onSome: (name) => new BrandedId({ name }),\n })\n );\n }\n\n throw new UnsupportedTypeError({\n typeName: field.type,\n fieldName: field.name,\n modelName,\n });\n };\n\n const resolveBaseType = (field: DMMF.Field): BaseType => {\n switch (field.kind) {\n case \"enum\":\n return new Enum({ name: field.type });\n case \"object\":\n return new Relation({ modelName: field.type });\n default:\n return resolveScalarType(field);\n }\n };\n\n // ========================================================================\n // Full Resolution (base + wrappers)\n // ========================================================================\n\n const resolve = (field: DMMF.Field): ResolvedType => {\n const wrappers: Wrapper[] = [];\n\n if (field.isList) {\n wrappers.push(\"Array\");\n }\n\n if (!field.isRequired) {\n wrappers.push(\"NullOr\");\n }\n\n return new ResolvedType({\n base: resolveBaseType(field),\n wrappers,\n });\n };\n\n const fieldToSchema = (field: DMMF.Field): string => emit(resolve(field));\n\n return {\n resolve,\n fieldToSchema,\n brandedIds,\n };\n },\n};\n","/**\n * Code Templates Module\n *\n * String templates for generating Effect Schema source code.\n * Separated from orchestration logic for clarity and testability.\n */\nimport type { DMMF } from \"@prisma/generator-helper\";\nimport dedent from \"dedent\";\nimport { Array as Arr, HashMap, Order, pipe } from \"effect\";\nimport type { GeneratorConfig } from \"./config.js\";\nimport type { SchemaResolver } from \"./resolver.js\";\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nexport const DEFAULT_HEADER = dedent`\n // This file was auto-generated by prisma-effect-schema\n // Do not edit manually - changes will be overwritten\n // https://github.com/frontcore/prisma-effect-schema\n\n import { Schema } from \"effect\"\n`;\n\nexport const JSON_VALUE_SCHEMA = dedent`\n // Recursive JSON value schema matching Prisma's JsonValue type\n type JsonValue = string | number | boolean | null | JsonArray | JsonObject\n type JsonArray = ReadonlyArray<JsonValue>\n type JsonObject = { readonly [key: string]: JsonValue }\n\n const JsonValueSchema: Schema.Schema<JsonValue> = Schema.suspend(\n (): Schema.Schema<JsonValue> =>\n Schema.Union(\n Schema.Null,\n Schema.Boolean,\n Schema.Number,\n Schema.String,\n Schema.Array(JsonValueSchema),\n Schema.Record({ key: Schema.String, value: JsonValueSchema })\n )\n )\n`;\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nexport const sectionHeader = (title: string): string => dedent`\n // ============================================================================\n // ${title}\n // ============================================================================\n\n`;\n\nconst ByName = <T extends { name: string }>(): Order.Order<T> =>\n Order.mapInput(Order.string, (item: T) => item.name);\n\nconst sortByName = <T extends { name: string }>(items: readonly T[]): T[] =>\n Arr.sort(items, ByName());\n\n// ============================================================================\n// Enum Templates\n// ============================================================================\n\nexport const generateEnumSchema = (enumDef: DMMF.DatamodelEnum): string => {\n const values = pipe(\n sortByName(enumDef.values),\n Arr.map((v) => `\"${v.name}\"`),\n Arr.join(\", \")\n );\n\n return dedent`\n export const ${enumDef.name} = Schema.Literal(${values})\n export type ${enumDef.name} = typeof ${enumDef.name}.Type\n `;\n};\n\nexport const generateEnumSchemas = (\n enums: readonly DMMF.DatamodelEnum[]\n): string =>\n pipe(sortByName(enums), Arr.map(generateEnumSchema), Arr.join(\"\\n\"));\n\n// ============================================================================\n// Branded ID Templates\n// ============================================================================\n\nexport const generateBrandedIdSchema = ([, brandedIdName]: readonly [\n string,\n string\n]): string =>\n dedent`\n export const ${brandedIdName} = Schema.String.pipe(Schema.brand(\"${brandedIdName}\"))\n export type ${brandedIdName} = typeof ${brandedIdName}.Type\n `;\n\nexport const generateBrandedIdSchemas = (\n brandedIds: HashMap.HashMap<string, string>\n): string =>\n pipe(\n brandedIds,\n HashMap.toEntries,\n Arr.sort(Order.mapInput(Order.string, ([key]: [string, string]) => key)),\n Arr.map(generateBrandedIdSchema),\n Arr.join(\"\\n\\n\")\n );\n\n// ============================================================================\n// Field Templates\n// ============================================================================\n\nexport const generateFieldsCode = (\n fields: readonly DMMF.Field[],\n resolver: SchemaResolver,\n sortFields: boolean\n): string =>\n pipe(\n sortFields ? sortByName(fields) : fields,\n Arr.map((field) => ` ${field.name}: ${resolver.fieldToSchema(field)}`),\n Arr.join(\",\\n\")\n );\n\n// ============================================================================\n// Model Templates\n// ============================================================================\n\nexport const generateModelSchema = (\n model: DMMF.Model,\n resolver: SchemaResolver,\n config: GeneratorConfig\n): string => {\n const scalarFields = model.fields.filter((f) => f.kind !== \"object\");\n const hasRelations = model.fields.some((f) => f.kind === \"object\");\n\n const scalarFieldsCode = generateFieldsCode(\n scalarFields,\n resolver,\n config.sortFields\n );\n\n const baseSchema = dedent`\n export const ${model.name} = Schema.Struct({\n ${scalarFieldsCode}\n })\n export type ${model.name} = typeof ${model.name}.Type\n `;\n\n // Optionally generate schema with relations\n if (!config.includeRelations || !hasRelations) {\n return baseSchema;\n }\n\n const allFieldsCode = generateFieldsCode(\n model.fields,\n resolver,\n config.sortFields\n );\n\n return dedent`\n ${baseSchema}\n\n export const ${model.name}WithRelations = Schema.Struct({\n ${allFieldsCode}\n })\n export type ${model.name}WithRelations = typeof ${model.name}WithRelations.Type\n `;\n};\n\nexport const generateModelSchemas = (\n models: readonly DMMF.Model[],\n makeResolver: (modelName: string) => SchemaResolver,\n config: GeneratorConfig\n): string =>\n pipe(\n sortByName(models),\n Arr.map((model) => generateModelSchema(model, makeResolver(model.name), config)),\n Arr.join(\"\\n\")\n );\n","/**\n * Effect Schema Code Generation\n *\n * Orchestrates generation of Effect Schema source code from Prisma DMMF.\n */\nimport type { DMMF } from \"@prisma/generator-helper\";\nimport { HashMap } from \"effect\";\nimport type { GeneratorConfig } from \"./config.js\";\nimport { buildForeignKeyMap, collectBrandedIds, SchemaResolver } from \"./resolver.js\";\nimport {\n DEFAULT_HEADER,\n generateBrandedIdSchemas,\n generateEnumSchemas,\n generateModelSchemas,\n JSON_VALUE_SCHEMA,\n sectionHeader,\n} from \"./templates.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface GenerateInput {\n dmmf: DMMF.Document;\n config: GeneratorConfig;\n}\n\nexport interface GenerateOutput {\n content: string;\n stats: {\n enumCount: number;\n modelCount: number;\n brandedIdCount: number;\n };\n}\n\n// ============================================================================\n// Main Entry Point\n// ============================================================================\n\n/**\n * Generates Effect Schema source code from Prisma DMMF.\n */\nexport const generate = (input: GenerateInput): GenerateOutput => {\n const { dmmf, config } = input;\n const { models, enums } = dmmf.datamodel;\n\n // Collect branded IDs from models with string primary keys\n const brandedIds = config.useBrandedIds\n ? collectBrandedIds(models)\n : HashMap.empty<string, string>();\n\n // Build FK map from relation metadata\n const foreignKeys = buildForeignKeyMap(models);\n\n const brandedIdCount = HashMap.size(brandedIds);\n\n // Check if any model has Json fields (to conditionally include JsonValueSchema)\n const hasJsonFields = models.some((model) =>\n model.fields.some((field) => field.type === \"Json\")\n );\n\n // Factory for creating resolvers per model\n const makeResolver = (modelName: string) =>\n SchemaResolver.make({ modelName, brandedIds, foreignKeys, config });\n\n // Assemble sections\n const sections = [\n // Header\n config.customHeader ?? DEFAULT_HEADER,\n\n // JSON schema (only if needed)\n ...(hasJsonFields ? [\"\\n\" + JSON_VALUE_SCHEMA] : []),\n\n // Enums\n ...(enums.length > 0\n ? [sectionHeader(\"Enums\"), generateEnumSchemas(enums)]\n : []),\n\n // Branded IDs\n ...(brandedIdCount > 0\n ? [sectionHeader(\"Branded IDs\"), generateBrandedIdSchemas(brandedIds), \"\"]\n : []),\n\n // Models\n sectionHeader(\"Models (scalar fields only)\"),\n generateModelSchemas(models, makeResolver, config),\n ];\n\n return {\n content: sections.join(\"\"),\n stats: {\n enumCount: enums.length,\n modelCount: models.length,\n brandedIdCount,\n },\n };\n};\n"],"mappings":"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","names":["Schema","Arr","Option","pipe","pipe","Arr","Option","dedent","Arr","HashMap","pipe","HashMap","HashMap"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NoOutputConfiguredError,
|
|
3
|
+
generate,
|
|
4
|
+
parseConfig
|
|
5
|
+
} from "./chunk-25MM3WYP.js";
|
|
6
|
+
|
|
7
|
+
// src/generator.ts
|
|
8
|
+
import { FileSystem } from "@effect/platform";
|
|
9
|
+
import { NodeFileSystem, NodePath } from "@effect/platform-node";
|
|
10
|
+
import { Path } from "@effect/platform/Path";
|
|
11
|
+
import { generatorHandler } from "@prisma/generator-helper";
|
|
12
|
+
import { Effect, Layer } from "effect";
|
|
13
|
+
var program = Effect.fnUntraced(function* (options) {
|
|
14
|
+
const fs = yield* FileSystem.FileSystem;
|
|
15
|
+
const path = yield* Path;
|
|
16
|
+
const outputPath = yield* Effect.fromNullable(
|
|
17
|
+
options.generator.output?.value
|
|
18
|
+
).pipe(
|
|
19
|
+
Effect.mapError(
|
|
20
|
+
({ cause, message, ...rest }) => new NoOutputConfiguredError({
|
|
21
|
+
cause,
|
|
22
|
+
details: message,
|
|
23
|
+
...rest
|
|
24
|
+
})
|
|
25
|
+
)
|
|
26
|
+
);
|
|
27
|
+
const config = yield* parseConfig(options);
|
|
28
|
+
const { content, stats } = generate({
|
|
29
|
+
dmmf: options.dmmf,
|
|
30
|
+
config
|
|
31
|
+
});
|
|
32
|
+
yield* fs.makeDirectory(path.dirname(outputPath), { recursive: true });
|
|
33
|
+
yield* fs.writeFileString(outputPath, content);
|
|
34
|
+
yield* Effect.logInfo(`\u2705 prisma-effect-schema: Generated Effect Schemas`);
|
|
35
|
+
yield* Effect.logInfo(` Output: ${outputPath}`);
|
|
36
|
+
yield* Effect.logInfo(
|
|
37
|
+
` Stats: ${stats.enumCount} enums, ${stats.modelCount} models, ${stats.brandedIdCount} branded IDs`
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
var PlatformLive = Layer.mergeAll(NodeFileSystem.layer, NodePath.layer);
|
|
41
|
+
generatorHandler({
|
|
42
|
+
onManifest() {
|
|
43
|
+
return {
|
|
44
|
+
defaultOutput: "./generated/effect-schemas.ts",
|
|
45
|
+
prettyName: "Effect Schema Generator"
|
|
46
|
+
// No requiresGenerators - we only need the DMMF which is always available
|
|
47
|
+
// ?? explain
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
onGenerate: (options) => program(options).pipe(
|
|
51
|
+
Effect.provide(PlatformLive),
|
|
52
|
+
Effect.runPromise
|
|
53
|
+
)
|
|
54
|
+
});
|
|
55
|
+
//# sourceMappingURL=chunk-AV5EXPPU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/generator.ts"],"sourcesContent":["/**\n * Prisma Generator Handler\n *\n * This module registers the generator with Prisma and handles the generation lifecycle.\n */\nimport { FileSystem } from \"@effect/platform\";\nimport { NodeFileSystem, NodePath } from \"@effect/platform-node\";\nimport { Path } from \"@effect/platform/Path\";\nimport type { GeneratorOptions } from \"@prisma/generator-helper\";\nimport { generatorHandler } from \"@prisma/generator-helper\";\nimport { Effect, Layer, Option } from \"effect\";\nimport { parseConfig } from \"./config.js\";\nimport { NoOutputConfiguredError } from \"./errors.js\";\nimport { generate } from \"./generate.js\";\n\n\n\nconst program = Effect.fnUntraced(function* (options: GeneratorOptions) {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path;\n\n const outputPath = yield* Effect.fromNullable(\n options.generator.output?.value\n ).pipe(\n Effect.mapError(\n ({ cause, message, ...rest }) =>\n new NoOutputConfiguredError({\n cause,\n details: message,\n ...rest,\n })\n )\n );\n\n const config = yield* parseConfig(options);\n\n const { content, stats } = generate({\n dmmf: options.dmmf,\n config,\n });\n\n // Ensure output directory exists\n yield* fs.makeDirectory(path.dirname(outputPath), { recursive: true });\n\n // Write the generated file\n yield* fs.writeFileString(outputPath, content);\n\n yield* Effect.logInfo(`✅ prisma-effect-schema: Generated Effect Schemas`);\n yield* Effect.logInfo(` Output: ${outputPath}`);\n yield* Effect.logInfo(\n ` Stats: ${stats.enumCount} enums, ${stats.modelCount} models, ${stats.brandedIdCount} branded IDs`\n );\n})\nconst PlatformLive = Layer.mergeAll(NodeFileSystem.layer, NodePath.layer);\n\n\n\n// Register as Prisma generator\ngeneratorHandler({\n onManifest() {\n return {\n defaultOutput: \"./generated/effect-schemas.ts\",\n prettyName: \"Effect Schema Generator\",\n // No requiresGenerators - we only need the DMMF which is always available\n // ?? explain\n };\n },\n onGenerate: (options: GeneratorOptions) => program(options).pipe(\n Effect.provide(PlatformLive),\n Effect.runPromise\n ),\n});\n"],"mappings":";;;;;;;AAKA,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB,gBAAgB;AACzC,SAAS,YAAY;AAErB,SAAS,wBAAwB;AACjC,SAAS,QAAQ,aAAqB;AAOtC,IAAM,UAAU,OAAO,WAAW,WAAW,SAA2B;AACtE,QAAM,KAAK,OAAO,WAAW;AAC7B,QAAM,OAAO,OAAO;AAEpB,QAAM,aAAa,OAAO,OAAO;AAAA,IAC/B,QAAQ,UAAU,QAAQ;AAAA,EAC5B,EAAE;AAAA,IACA,OAAO;AAAA,MACL,CAAC,EAAE,OAAO,SAAS,GAAG,KAAK,MACzB,IAAI,wBAAwB;AAAA,QAC1B;AAAA,QACA,SAAS;AAAA,QACT,GAAG;AAAA,MACL,CAAC;AAAA,IACL;AAAA,EACF;AAEA,QAAM,SAAS,OAAO,YAAY,OAAO;AAEzC,QAAM,EAAE,SAAS,MAAM,IAAI,SAAS;AAAA,IAClC,MAAM,QAAQ;AAAA,IACd;AAAA,EACF,CAAC;AAGD,SAAO,GAAG,cAAc,KAAK,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAGrE,SAAO,GAAG,gBAAgB,YAAY,OAAO;AAE7C,SAAO,OAAO,QAAQ,uDAAkD;AACxE,SAAO,OAAO,QAAQ,cAAc,UAAU,EAAE;AAChD,SAAO,OAAO;AAAA,IACZ,aAAa,MAAM,SAAS,WAAW,MAAM,UAAU,YAAY,MAAM,cAAc;AAAA,EACzF;AACF,CAAC;AACD,IAAM,eAAe,MAAM,SAAS,eAAe,OAAO,SAAS,KAAK;AAKxE,iBAAiB;AAAA,EACf,aAAa;AACX,WAAO;AAAA,MACL,eAAe;AAAA,MACf,YAAY;AAAA;AAAA;AAAA,IAGd;AAAA,EACF;AAAA,EACA,YAAY,CAAC,YAA8B,QAAQ,OAAO,EAAE;AAAA,IAC1D,OAAO,QAAQ,YAAY;AAAA,IAC3B,OAAO;AAAA,EACT;AACF,CAAC;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { Schema, Data, HashMap } from 'effect';
|
|
2
|
+
import { DMMF } from '@prisma/generator-helper';
|
|
3
|
+
import * as effect_Types from 'effect/Types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generator configuration schema with defaults.
|
|
7
|
+
* Parses Prisma generator config and applies defaults in one step.
|
|
8
|
+
*/
|
|
9
|
+
declare const GeneratorConfigSchema: Schema.Struct<{
|
|
10
|
+
/**
|
|
11
|
+
* Whether to include relation fields in the generated schemas.
|
|
12
|
+
* Relations use Schema.suspend() for lazy evaluation to handle circular deps.
|
|
13
|
+
* @default false
|
|
14
|
+
*/
|
|
15
|
+
includeRelations: Schema.optionalWith<Schema.transform<Schema.Union<[typeof Schema.String, Schema.Array$<typeof Schema.String>]>, typeof Schema.Boolean>, {
|
|
16
|
+
default: () => false;
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* Whether to generate branded ID types for models with string IDs.
|
|
20
|
+
* When true, generates `UserId`, `PostId`, etc. and uses them in model schemas.
|
|
21
|
+
* @default true
|
|
22
|
+
*/
|
|
23
|
+
useBrandedIds: Schema.optionalWith<Schema.transform<Schema.Union<[typeof Schema.String, Schema.Array$<typeof Schema.String>]>, typeof Schema.Boolean>, {
|
|
24
|
+
default: () => true;
|
|
25
|
+
}>;
|
|
26
|
+
/**
|
|
27
|
+
* How to handle DateTime fields.
|
|
28
|
+
* - 'Date': Use Schema.Date (expects Date objects, for Prisma results)
|
|
29
|
+
* - 'DateTimeString': Use Schema.Date with dateTime annotation (for API validation)
|
|
30
|
+
* @default 'Date'
|
|
31
|
+
*/
|
|
32
|
+
dateTimeHandling: Schema.optionalWith<Schema.transform<Schema.Union<[typeof Schema.String, Schema.Array$<typeof Schema.String>]>, Schema.Literal<["Date", "DateTimeString"]>>, {
|
|
33
|
+
default: () => "Date";
|
|
34
|
+
}>;
|
|
35
|
+
/**
|
|
36
|
+
* Whether to sort fields alphabetically for deterministic output.
|
|
37
|
+
* @default true
|
|
38
|
+
*/
|
|
39
|
+
sortFields: Schema.optionalWith<Schema.transform<Schema.Union<[typeof Schema.String, Schema.Array$<typeof Schema.String>]>, typeof Schema.Boolean>, {
|
|
40
|
+
default: () => true;
|
|
41
|
+
}>;
|
|
42
|
+
/**
|
|
43
|
+
* Custom header to prepend to the generated file.
|
|
44
|
+
* If not provided, uses a default header without timestamps.
|
|
45
|
+
*/
|
|
46
|
+
customHeader: Schema.optionalWith<Schema.transform<Schema.Union<[typeof Schema.String, Schema.Array$<typeof Schema.String>]>, Schema.NullOr<typeof Schema.String>>, {
|
|
47
|
+
default: () => null;
|
|
48
|
+
}>;
|
|
49
|
+
}>;
|
|
50
|
+
/**
|
|
51
|
+
* Resolved configuration type (derived from schema)
|
|
52
|
+
*/
|
|
53
|
+
type GeneratorConfig = typeof GeneratorConfigSchema.Type;
|
|
54
|
+
|
|
55
|
+
declare const UnsupportedTypeError_base: Schema.TaggedErrorClass<UnsupportedTypeError, "UnsupportedTypeError", {
|
|
56
|
+
readonly _tag: Schema.tag<"UnsupportedTypeError">;
|
|
57
|
+
} & {
|
|
58
|
+
typeName: typeof Schema.String;
|
|
59
|
+
fieldName: typeof Schema.String;
|
|
60
|
+
modelName: typeof Schema.String;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* Error thrown when an unsupported Prisma type is encountered
|
|
64
|
+
*/
|
|
65
|
+
declare class UnsupportedTypeError extends UnsupportedTypeError_base {
|
|
66
|
+
get message(): string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Effect Schema Code Generation
|
|
71
|
+
*
|
|
72
|
+
* Orchestrates generation of Effect Schema source code from Prisma DMMF.
|
|
73
|
+
*/
|
|
74
|
+
|
|
75
|
+
interface GenerateInput {
|
|
76
|
+
dmmf: DMMF.Document;
|
|
77
|
+
config: GeneratorConfig;
|
|
78
|
+
}
|
|
79
|
+
interface GenerateOutput {
|
|
80
|
+
content: string;
|
|
81
|
+
stats: {
|
|
82
|
+
enumCount: number;
|
|
83
|
+
modelCount: number;
|
|
84
|
+
brandedIdCount: number;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Generates Effect Schema source code from Prisma DMMF.
|
|
89
|
+
*/
|
|
90
|
+
declare const generate: (input: GenerateInput) => GenerateOutput;
|
|
91
|
+
|
|
92
|
+
declare const Primitive_base: new <A extends Record<string, any> = {}>(args: effect_Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => Readonly<A> & {
|
|
93
|
+
readonly _tag: "Primitive";
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* A primitive scalar type from Prisma mapped to Effect Schema
|
|
97
|
+
*/
|
|
98
|
+
declare class Primitive extends Primitive_base<{
|
|
99
|
+
readonly schema: "Int" | "String" | "Boolean" | "Number" | "Date" | "DateTimeUtc" | "BigInt" | "Uint8Array" | "Json" | "Decimal";
|
|
100
|
+
}> {
|
|
101
|
+
}
|
|
102
|
+
declare const BrandedId_base: new <A extends Record<string, any> = {}>(args: effect_Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => Readonly<A> & {
|
|
103
|
+
readonly _tag: "BrandedId";
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* A branded ID type for type-safe IDs
|
|
107
|
+
*/
|
|
108
|
+
declare class BrandedId extends BrandedId_base<{
|
|
109
|
+
readonly name: string;
|
|
110
|
+
}> {
|
|
111
|
+
}
|
|
112
|
+
declare const Enum_base: new <A extends Record<string, any> = {}>(args: effect_Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => Readonly<A> & {
|
|
113
|
+
readonly _tag: "Enum";
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* An enum type reference
|
|
117
|
+
*/
|
|
118
|
+
declare class Enum extends Enum_base<{
|
|
119
|
+
readonly name: string;
|
|
120
|
+
}> {
|
|
121
|
+
}
|
|
122
|
+
declare const Relation_base: new <A extends Record<string, any> = {}>(args: effect_Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => Readonly<A> & {
|
|
123
|
+
readonly _tag: "Relation";
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* A relation to another model (uses Schema.suspend for circular refs)
|
|
127
|
+
*/
|
|
128
|
+
declare class Relation extends Relation_base<{
|
|
129
|
+
readonly modelName: string;
|
|
130
|
+
}> {
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Union of all possible base types a field can resolve to
|
|
134
|
+
*/
|
|
135
|
+
type BaseType = Primitive | BrandedId | Enum | Relation;
|
|
136
|
+
/**
|
|
137
|
+
* Wrappers that can be applied to a base type
|
|
138
|
+
*/
|
|
139
|
+
type Wrapper = "Array" | "NullOr";
|
|
140
|
+
/**
|
|
141
|
+
* A fully resolved field type: base type + wrappers to apply
|
|
142
|
+
*/
|
|
143
|
+
declare class ResolvedType extends Data.Class<{
|
|
144
|
+
readonly base: BaseType;
|
|
145
|
+
readonly wrappers: readonly Wrapper[];
|
|
146
|
+
}> {
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Collects branded IDs for models with string primary keys.
|
|
150
|
+
* Uses the actual PK field name for the suffix:
|
|
151
|
+
* - User.id (String @id) -> "UserId"
|
|
152
|
+
* - Course.slug (String @id) -> "CourseSlug"
|
|
153
|
+
*/
|
|
154
|
+
declare const collectBrandedIds: (models: readonly DMMF.Model[]) => HashMap.HashMap<string, string>;
|
|
155
|
+
/**
|
|
156
|
+
* Builds a map from FK field names to their target model names.
|
|
157
|
+
* Uses DMMF relation metadata (relationFromFields) for accuracy.
|
|
158
|
+
*
|
|
159
|
+
* Example output:
|
|
160
|
+
* {
|
|
161
|
+
* "userId": "User",
|
|
162
|
+
* "authorId": "User",
|
|
163
|
+
* "courseSlug": "Course",
|
|
164
|
+
* "avatarId": "File"
|
|
165
|
+
* }
|
|
166
|
+
*/
|
|
167
|
+
declare const buildForeignKeyMap: (models: readonly DMMF.Model[]) => HashMap.HashMap<string, string>;
|
|
168
|
+
interface SchemaResolverConfig {
|
|
169
|
+
readonly modelName: string;
|
|
170
|
+
readonly brandedIds: HashMap.HashMap<string, string>;
|
|
171
|
+
readonly foreignKeys: HashMap.HashMap<string, string>;
|
|
172
|
+
readonly config: GeneratorConfig;
|
|
173
|
+
}
|
|
174
|
+
interface SchemaResolver {
|
|
175
|
+
/**
|
|
176
|
+
* Resolve a field to its structured type representation.
|
|
177
|
+
* Use this for testing or when you need to inspect the decision.
|
|
178
|
+
*/
|
|
179
|
+
readonly resolve: (field: DMMF.Field) => ResolvedType;
|
|
180
|
+
/**
|
|
181
|
+
* Convenience method: resolve + emit in one call.
|
|
182
|
+
* Use this for the common case where you just need the string.
|
|
183
|
+
*/
|
|
184
|
+
readonly fieldToSchema: (field: DMMF.Field) => string;
|
|
185
|
+
/**
|
|
186
|
+
* The computed branded IDs map (modelName -> brandedIdName).
|
|
187
|
+
* Exposed for generating branded ID schema declarations.
|
|
188
|
+
*/
|
|
189
|
+
readonly brandedIds: HashMap.HashMap<string, string>;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Create a SchemaResolver for a specific model.
|
|
193
|
+
* Dependencies are captured at construction time.
|
|
194
|
+
*/
|
|
195
|
+
declare const SchemaResolver: {
|
|
196
|
+
make: (resolverConfig: SchemaResolverConfig) => SchemaResolver;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Emit a base type to its Effect Schema string representation
|
|
201
|
+
*/
|
|
202
|
+
declare const emitBaseType: (base: BaseType) => string;
|
|
203
|
+
/**
|
|
204
|
+
* Apply a single wrapper to a schema string
|
|
205
|
+
*/
|
|
206
|
+
declare const applyWrapper: (inner: string, wrapper: Wrapper) => string;
|
|
207
|
+
/**
|
|
208
|
+
* Emit a fully resolved type (base + wrappers) to Effect Schema string.
|
|
209
|
+
* Wrappers are applied left-to-right (innermost first).
|
|
210
|
+
*/
|
|
211
|
+
declare const emit: (type: ResolvedType) => string;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Code Templates Module
|
|
215
|
+
*
|
|
216
|
+
* String templates for generating Effect Schema source code.
|
|
217
|
+
* Separated from orchestration logic for clarity and testability.
|
|
218
|
+
*/
|
|
219
|
+
|
|
220
|
+
declare const DEFAULT_HEADER: string;
|
|
221
|
+
declare const JSON_VALUE_SCHEMA: string;
|
|
222
|
+
declare const sectionHeader: (title: string) => string;
|
|
223
|
+
declare const generateEnumSchema: (enumDef: DMMF.DatamodelEnum) => string;
|
|
224
|
+
declare const generateEnumSchemas: (enums: readonly DMMF.DatamodelEnum[]) => string;
|
|
225
|
+
declare const generateBrandedIdSchema: ([, brandedIdName]: readonly [string, string]) => string;
|
|
226
|
+
declare const generateBrandedIdSchemas: (brandedIds: HashMap.HashMap<string, string>) => string;
|
|
227
|
+
declare const generateFieldsCode: (fields: readonly DMMF.Field[], resolver: SchemaResolver, sortFields: boolean) => string;
|
|
228
|
+
declare const generateModelSchema: (model: DMMF.Model, resolver: SchemaResolver, config: GeneratorConfig) => string;
|
|
229
|
+
declare const generateModelSchemas: (models: readonly DMMF.Model[], makeResolver: (modelName: string) => SchemaResolver, config: GeneratorConfig) => string;
|
|
230
|
+
|
|
231
|
+
export { type BaseType, BrandedId, DEFAULT_HEADER, Enum, type GenerateInput, type GenerateOutput, type GeneratorConfig, GeneratorConfigSchema, JSON_VALUE_SCHEMA, Primitive, Relation, ResolvedType, SchemaResolver, type SchemaResolverConfig, UnsupportedTypeError, type Wrapper, applyWrapper, buildForeignKeyMap, collectBrandedIds, emit, emitBaseType, generate, generateBrandedIdSchema, generateBrandedIdSchemas, generateEnumSchema, generateEnumSchemas, generateFieldsCode, generateModelSchema, generateModelSchemas, sectionHeader };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BrandedId,
|
|
3
|
+
DEFAULT_HEADER,
|
|
4
|
+
Enum,
|
|
5
|
+
GeneratorConfigSchema,
|
|
6
|
+
JSON_VALUE_SCHEMA,
|
|
7
|
+
Primitive,
|
|
8
|
+
Relation,
|
|
9
|
+
ResolvedType,
|
|
10
|
+
SchemaResolver,
|
|
11
|
+
UnsupportedTypeError,
|
|
12
|
+
applyWrapper,
|
|
13
|
+
buildForeignKeyMap,
|
|
14
|
+
collectBrandedIds,
|
|
15
|
+
emit,
|
|
16
|
+
emitBaseType,
|
|
17
|
+
generate,
|
|
18
|
+
generateBrandedIdSchema,
|
|
19
|
+
generateBrandedIdSchemas,
|
|
20
|
+
generateEnumSchema,
|
|
21
|
+
generateEnumSchemas,
|
|
22
|
+
generateFieldsCode,
|
|
23
|
+
generateModelSchema,
|
|
24
|
+
generateModelSchemas,
|
|
25
|
+
sectionHeader
|
|
26
|
+
} from "./chunk-25MM3WYP.js";
|
|
27
|
+
export {
|
|
28
|
+
BrandedId,
|
|
29
|
+
DEFAULT_HEADER,
|
|
30
|
+
Enum,
|
|
31
|
+
GeneratorConfigSchema,
|
|
32
|
+
JSON_VALUE_SCHEMA,
|
|
33
|
+
Primitive,
|
|
34
|
+
Relation,
|
|
35
|
+
ResolvedType,
|
|
36
|
+
SchemaResolver,
|
|
37
|
+
UnsupportedTypeError,
|
|
38
|
+
applyWrapper,
|
|
39
|
+
buildForeignKeyMap,
|
|
40
|
+
collectBrandedIds,
|
|
41
|
+
emit,
|
|
42
|
+
emitBaseType,
|
|
43
|
+
generate,
|
|
44
|
+
generateBrandedIdSchema,
|
|
45
|
+
generateBrandedIdSchemas,
|
|
46
|
+
generateEnumSchema,
|
|
47
|
+
generateEnumSchemas,
|
|
48
|
+
generateFieldsCode,
|
|
49
|
+
generateModelSchema,
|
|
50
|
+
generateModelSchemas,
|
|
51
|
+
sectionHeader
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "prisma-effect-schema",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Prisma generator that creates Effect Schemas from your Prisma models",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"prisma",
|
|
7
|
+
"effect",
|
|
8
|
+
"schema",
|
|
9
|
+
"generator",
|
|
10
|
+
"typescript",
|
|
11
|
+
"validation"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "Frontcore",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/frontcore/prisma-effect-schema.git"
|
|
18
|
+
},
|
|
19
|
+
"type": "module",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./generator": {
|
|
26
|
+
"types": "./dist/generator.d.ts",
|
|
27
|
+
"import": "./dist/generator.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"bin": {
|
|
31
|
+
"prisma-effect-schema": "./dist/bin.js"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"README.md",
|
|
36
|
+
"LICENSE"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsup",
|
|
40
|
+
"dev": "tsup --watch",
|
|
41
|
+
"typecheck": "tsc --noEmit",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:watch": "vitest",
|
|
44
|
+
"prepublishOnly": "pnpm build"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@effect/platform": "^0.95.0",
|
|
48
|
+
"@effect/platform-node": "^0.105.0",
|
|
49
|
+
"@prisma/generator-helper": "^6.5.0",
|
|
50
|
+
"dedent": "^1.7.2"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/dedent": "^0.7.2",
|
|
54
|
+
"@types/node": "^22.13.10",
|
|
55
|
+
"effect": "^3.14.8",
|
|
56
|
+
"prisma": "^6.5.0",
|
|
57
|
+
"tsup": "^8.4.0",
|
|
58
|
+
"typescript": "^5.8.2",
|
|
59
|
+
"vitest": "^3.0.8"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"effect": ">=3.0.0"
|
|
63
|
+
},
|
|
64
|
+
"engines": {
|
|
65
|
+
"node": ">=18"
|
|
66
|
+
}
|
|
67
|
+
}
|