prisma-effect-schema 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +2 -2
- package/dist/{chunk-AV5EXPPU.js → chunk-CM4H5F25.js} +4 -4
- package/dist/chunk-CM4H5F25.js.map +1 -0
- package/dist/{chunk-25MM3WYP.js → chunk-TRP4TWLF.js} +39 -55
- package/dist/chunk-TRP4TWLF.js.map +1 -0
- package/dist/generator.js +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/package.json +2 -4
- package/dist/chunk-25MM3WYP.js.map +0 -1
- package/dist/chunk-AV5EXPPU.js.map +0 -1
package/dist/bin.js
CHANGED
|
@@ -2,13 +2,13 @@ import {
|
|
|
2
2
|
NoOutputConfiguredError,
|
|
3
3
|
generate,
|
|
4
4
|
parseConfig
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-TRP4TWLF.js";
|
|
6
6
|
|
|
7
7
|
// src/generator.ts
|
|
8
8
|
import { FileSystem } from "@effect/platform";
|
|
9
9
|
import { NodeFileSystem, NodePath } from "@effect/platform-node";
|
|
10
10
|
import { Path } from "@effect/platform/Path";
|
|
11
|
-
import
|
|
11
|
+
import generatorHelper from "@prisma/generator-helper";
|
|
12
12
|
import { Effect, Layer } from "effect";
|
|
13
13
|
var program = Effect.fnUntraced(function* (options) {
|
|
14
14
|
const fs = yield* FileSystem.FileSystem;
|
|
@@ -38,7 +38,7 @@ var program = Effect.fnUntraced(function* (options) {
|
|
|
38
38
|
);
|
|
39
39
|
});
|
|
40
40
|
var PlatformLive = Layer.mergeAll(NodeFileSystem.layer, NodePath.layer);
|
|
41
|
-
generatorHandler({
|
|
41
|
+
generatorHelper.generatorHandler({
|
|
42
42
|
onManifest() {
|
|
43
43
|
return {
|
|
44
44
|
defaultOutput: "./generated/effect-schemas.ts",
|
|
@@ -52,4 +52,4 @@ generatorHandler({
|
|
|
52
52
|
Effect.runPromise
|
|
53
53
|
)
|
|
54
54
|
});
|
|
55
|
-
//# sourceMappingURL=chunk-
|
|
55
|
+
//# sourceMappingURL=chunk-CM4H5F25.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 generatorHelper 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\ngeneratorHelper.generatorHandler({\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,OAAO,qBAAqB;AAC5B,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,gBAAgB,iBAAiB;AAAA,EAC/B,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":[]}
|
|
@@ -75,7 +75,6 @@ var GeneratorConfigSchema = Schema.Struct({
|
|
|
75
75
|
var parseConfig = (options) => Schema.decodeUnknown(GeneratorConfigSchema)(options.generator.config);
|
|
76
76
|
|
|
77
77
|
// src/errors.ts
|
|
78
|
-
import dedent from "dedent";
|
|
79
78
|
import { Schema as Schema2 } from "effect";
|
|
80
79
|
var AppTag = "[prisma-effect-schema]";
|
|
81
80
|
var UnsupportedTypeError = class extends Schema2.TaggedError()(
|
|
@@ -87,10 +86,7 @@ var UnsupportedTypeError = class extends Schema2.TaggedError()(
|
|
|
87
86
|
}
|
|
88
87
|
) {
|
|
89
88
|
get message() {
|
|
90
|
-
return
|
|
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
|
-
`;
|
|
89
|
+
return `${AppTag} Unsupported Prisma type "${this.typeName}" for field "${this.fieldName}" in model "${this.modelName}". Please open an issue at https://github.com/frontcore/prisma-effect-schema/issues`;
|
|
94
90
|
}
|
|
95
91
|
};
|
|
96
92
|
var NoOutputConfiguredError = class extends Schema2.TaggedError()(
|
|
@@ -250,37 +246,33 @@ var SchemaResolver = {
|
|
|
250
246
|
};
|
|
251
247
|
|
|
252
248
|
// src/templates.ts
|
|
253
|
-
import dedent2 from "dedent";
|
|
254
249
|
import { Array as Arr3, HashMap as HashMap2, Order, pipe as pipe3 } from "effect";
|
|
255
|
-
var DEFAULT_HEADER =
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
// https://github.com/frontcore/prisma-effect-schema
|
|
250
|
+
var DEFAULT_HEADER = `// This file was auto-generated by prisma-effect-schema
|
|
251
|
+
// Do not edit manually - changes will be overwritten
|
|
252
|
+
// https://github.com/frontcore/prisma-effect-schema
|
|
259
253
|
|
|
260
|
-
|
|
254
|
+
import { Schema } from "effect"
|
|
261
255
|
`;
|
|
262
|
-
var JSON_VALUE_SCHEMA =
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
type JsonObject = { readonly [key: string]: JsonValue }
|
|
256
|
+
var JSON_VALUE_SCHEMA = `// Recursive JSON value schema matching Prisma's JsonValue type
|
|
257
|
+
type JsonValue = string | number | boolean | null | JsonArray | JsonObject
|
|
258
|
+
type JsonArray = ReadonlyArray<JsonValue>
|
|
259
|
+
type JsonObject = { readonly [key: string]: JsonValue }
|
|
267
260
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
261
|
+
const JsonValueSchema: Schema.Schema<JsonValue> = Schema.suspend(
|
|
262
|
+
(): Schema.Schema<JsonValue> =>
|
|
263
|
+
Schema.Union(
|
|
264
|
+
Schema.Null,
|
|
265
|
+
Schema.Boolean,
|
|
266
|
+
Schema.Number,
|
|
267
|
+
Schema.String,
|
|
268
|
+
Schema.Array(JsonValueSchema),
|
|
269
|
+
Schema.Record({ key: Schema.String, value: JsonValueSchema })
|
|
270
|
+
)
|
|
271
|
+
)
|
|
279
272
|
`;
|
|
280
|
-
var sectionHeader = (title) =>
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
// ============================================================================
|
|
273
|
+
var sectionHeader = (title) => `// ============================================================================
|
|
274
|
+
// ${title}
|
|
275
|
+
// ============================================================================
|
|
284
276
|
|
|
285
277
|
`;
|
|
286
278
|
var ByName = () => Order.mapInput(Order.string, (item) => item.name);
|
|
@@ -291,16 +283,12 @@ var generateEnumSchema = (enumDef) => {
|
|
|
291
283
|
Arr3.map((v) => `"${v.name}"`),
|
|
292
284
|
Arr3.join(", ")
|
|
293
285
|
);
|
|
294
|
-
return
|
|
295
|
-
|
|
296
|
-
export type ${enumDef.name} = typeof ${enumDef.name}.Type
|
|
297
|
-
`;
|
|
286
|
+
return `export const ${enumDef.name} = Schema.Literal(${values})
|
|
287
|
+
export type ${enumDef.name} = typeof ${enumDef.name}.Type`;
|
|
298
288
|
};
|
|
299
289
|
var generateEnumSchemas = (enums) => pipe3(sortByName(enums), Arr3.map(generateEnumSchema), Arr3.join("\n"));
|
|
300
|
-
var generateBrandedIdSchema = ([, brandedIdName]) =>
|
|
301
|
-
|
|
302
|
-
export type ${brandedIdName} = typeof ${brandedIdName}.Type
|
|
303
|
-
`;
|
|
290
|
+
var generateBrandedIdSchema = ([, brandedIdName]) => `export const ${brandedIdName} = Schema.String.pipe(Schema.brand("${brandedIdName}"))
|
|
291
|
+
export type ${brandedIdName} = typeof ${brandedIdName}.Type`;
|
|
304
292
|
var generateBrandedIdSchemas = (brandedIds) => pipe3(
|
|
305
293
|
brandedIds,
|
|
306
294
|
HashMap2.toEntries,
|
|
@@ -321,12 +309,10 @@ var generateModelSchema = (model, resolver, config) => {
|
|
|
321
309
|
resolver,
|
|
322
310
|
config.sortFields
|
|
323
311
|
);
|
|
324
|
-
const baseSchema =
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
export type ${model.name} = typeof ${model.name}.Type
|
|
329
|
-
`;
|
|
312
|
+
const baseSchema = `export const ${model.name} = Schema.Struct({
|
|
313
|
+
${scalarFieldsCode}
|
|
314
|
+
})
|
|
315
|
+
export type ${model.name} = typeof ${model.name}.Type`;
|
|
330
316
|
if (!config.includeRelations || !hasRelations) {
|
|
331
317
|
return baseSchema;
|
|
332
318
|
}
|
|
@@ -335,14 +321,12 @@ var generateModelSchema = (model, resolver, config) => {
|
|
|
335
321
|
resolver,
|
|
336
322
|
config.sortFields
|
|
337
323
|
);
|
|
338
|
-
return
|
|
339
|
-
${baseSchema}
|
|
324
|
+
return `${baseSchema}
|
|
340
325
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
`;
|
|
326
|
+
export const ${model.name}WithRelations = Schema.Struct({
|
|
327
|
+
${allFieldsCode}
|
|
328
|
+
})
|
|
329
|
+
export type ${model.name}WithRelations = typeof ${model.name}WithRelations.Type`;
|
|
346
330
|
};
|
|
347
331
|
var generateModelSchemas = (models, makeResolver, config) => pipe3(
|
|
348
332
|
sortByName(models),
|
|
@@ -370,13 +354,13 @@ var generate = (input) => {
|
|
|
370
354
|
// Enums
|
|
371
355
|
...enums.length > 0 ? [sectionHeader("Enums"), generateEnumSchemas(enums)] : [],
|
|
372
356
|
// Branded IDs
|
|
373
|
-
...brandedIdCount > 0 ? [sectionHeader("Branded IDs"), generateBrandedIdSchemas(brandedIds)
|
|
357
|
+
...brandedIdCount > 0 ? [sectionHeader("Branded IDs"), generateBrandedIdSchemas(brandedIds)] : [],
|
|
374
358
|
// Models
|
|
375
359
|
sectionHeader("Models (scalar fields only)"),
|
|
376
360
|
generateModelSchemas(models, makeResolver, config)
|
|
377
361
|
];
|
|
378
362
|
return {
|
|
379
|
-
content: sections.join(""),
|
|
363
|
+
content: sections.join("\n"),
|
|
380
364
|
stats: {
|
|
381
365
|
enumCount: enums.length,
|
|
382
366
|
modelCount: models.length,
|
|
@@ -413,4 +397,4 @@ export {
|
|
|
413
397
|
generateModelSchemas,
|
|
414
398
|
generate
|
|
415
399
|
};
|
|
416
|
-
//# sourceMappingURL=chunk-
|
|
400
|
+
//# sourceMappingURL=chunk-TRP4TWLF.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 { 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 `${AppTag} Unsupported Prisma type \"${this.typeName}\" for field \"${this.fieldName}\" in model \"${this.modelName}\". Please open an issue at https://github.com/frontcore/prisma-effect-schema/issues`;\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 { 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 = `// 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\nimport { Schema } from \"effect\"\n`;\n\nexport const JSON_VALUE_SCHEMA = `// Recursive JSON value schema matching Prisma's JsonValue type\ntype JsonValue = string | number | boolean | null | JsonArray | JsonObject\ntype JsonArray = ReadonlyArray<JsonValue>\ntype JsonObject = { readonly [key: string]: JsonValue }\n\nconst 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 =>\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 `export const ${enumDef.name} = Schema.Literal(${values})\nexport type ${enumDef.name} = typeof ${enumDef.name}.Type`;\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 `export const ${brandedIdName} = Schema.String.pipe(Schema.brand(\"${brandedIdName}\"))\nexport type ${brandedIdName} = typeof ${brandedIdName}.Type`;\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 = `export const ${model.name} = Schema.Struct({\n${scalarFieldsCode}\n})\nexport type ${model.name} = typeof ${model.name}.Type`;\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 `${baseSchema}\n\nexport const ${model.name}WithRelations = Schema.Struct({\n${allFieldsCode}\n})\nexport type ${model.name}WithRelations = typeof ${model.name}WithRelations.Type`;\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\"),\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","Arr","HashMap","pipe","HashMap","HashMap"]}
|
package/dist/generator.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import "./chunk-
|
|
2
|
-
import "./chunk-
|
|
1
|
+
import "./chunk-CM4H5F25.js";
|
|
2
|
+
import "./chunk-TRP4TWLF.js";
|
|
3
3
|
//# sourceMappingURL=generator.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -217,8 +217,8 @@ declare const emit: (type: ResolvedType) => string;
|
|
|
217
217
|
* Separated from orchestration logic for clarity and testability.
|
|
218
218
|
*/
|
|
219
219
|
|
|
220
|
-
declare const DEFAULT_HEADER
|
|
221
|
-
declare const JSON_VALUE_SCHEMA: string;
|
|
220
|
+
declare const DEFAULT_HEADER = "// 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\nimport { Schema } from \"effect\"\n";
|
|
221
|
+
declare const JSON_VALUE_SCHEMA = "// Recursive JSON value schema matching Prisma's JsonValue type\ntype JsonValue = string | number | boolean | null | JsonArray | JsonObject\ntype JsonArray = ReadonlyArray<JsonValue>\ntype JsonObject = { readonly [key: string]: JsonValue }\n\nconst 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";
|
|
222
222
|
declare const sectionHeader: (title: string) => string;
|
|
223
223
|
declare const generateEnumSchema: (enumDef: DMMF.DatamodelEnum) => string;
|
|
224
224
|
declare const generateEnumSchemas: (enums: readonly DMMF.DatamodelEnum[]) => string;
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prisma-effect-schema",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Prisma generator that creates Effect Schemas from your Prisma models",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"prisma",
|
|
@@ -46,11 +46,9 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@effect/platform": "^0.95.0",
|
|
48
48
|
"@effect/platform-node": "^0.105.0",
|
|
49
|
-
"@prisma/generator-helper": "^6.5.0"
|
|
50
|
-
"dedent": "^1.7.2"
|
|
49
|
+
"@prisma/generator-helper": "^6.5.0"
|
|
51
50
|
},
|
|
52
51
|
"devDependencies": {
|
|
53
|
-
"@types/dedent": "^0.7.2",
|
|
54
52
|
"@types/node": "^22.13.10",
|
|
55
53
|
"effect": "^3.14.8",
|
|
56
54
|
"prisma": "^6.5.0",
|
|
@@ -1 +0,0 @@
|
|
|
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"]}
|
|
@@ -1 +0,0 @@
|
|
|
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":[]}
|