@effectify/prisma 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +33 -38
  2. package/dist/src/cli.d.ts +1 -1
  3. package/dist/src/cli.js +15 -14
  4. package/dist/src/commands/init.d.ts +1 -1
  5. package/dist/src/commands/init.js +36 -47
  6. package/dist/src/commands/prisma.d.ts +4 -4
  7. package/dist/src/commands/prisma.js +12 -12
  8. package/dist/src/generators/sql-schema-generator.js +15 -16
  9. package/dist/src/runtime/index.d.ts +303 -0
  10. package/dist/src/runtime/index.js +216 -0
  11. package/dist/src/schema-generator/effect/enum.d.ts +12 -0
  12. package/dist/src/schema-generator/effect/enum.js +18 -0
  13. package/dist/src/schema-generator/effect/generator.d.ts +16 -0
  14. package/dist/src/schema-generator/effect/generator.js +42 -0
  15. package/dist/src/schema-generator/effect/join-table.d.ts +12 -0
  16. package/dist/src/schema-generator/effect/join-table.js +28 -0
  17. package/dist/src/schema-generator/effect/type.d.ts +18 -0
  18. package/dist/src/schema-generator/effect/type.js +82 -0
  19. package/dist/src/schema-generator/index.d.ts +11 -0
  20. package/dist/src/schema-generator/index.js +83 -0
  21. package/dist/src/schema-generator/kysely/generator.d.ts +11 -0
  22. package/dist/src/schema-generator/kysely/generator.js +7 -0
  23. package/dist/src/schema-generator/kysely/type.d.ts +14 -0
  24. package/dist/src/schema-generator/kysely/type.js +44 -0
  25. package/dist/src/schema-generator/prisma/enum.d.ts +19 -0
  26. package/dist/src/schema-generator/prisma/enum.js +19 -0
  27. package/dist/src/schema-generator/prisma/generator.d.ts +53 -0
  28. package/dist/src/schema-generator/prisma/generator.js +29 -0
  29. package/dist/src/schema-generator/prisma/relation.d.ts +83 -0
  30. package/dist/src/schema-generator/prisma/relation.js +165 -0
  31. package/dist/src/schema-generator/prisma/type.d.ts +108 -0
  32. package/dist/src/schema-generator/prisma/type.js +85 -0
  33. package/dist/src/schema-generator/utils/annotations.d.ts +32 -0
  34. package/dist/src/schema-generator/utils/annotations.js +79 -0
  35. package/dist/src/schema-generator/utils/codegen.d.ts +9 -0
  36. package/dist/src/schema-generator/utils/codegen.js +14 -0
  37. package/dist/src/schema-generator/utils/naming.d.ts +29 -0
  38. package/dist/src/schema-generator/utils/naming.js +68 -0
  39. package/dist/src/schema-generator/utils/type-mappings.d.ts +62 -0
  40. package/dist/src/schema-generator/utils/type-mappings.js +70 -0
  41. package/dist/src/services/formatter-service.d.ts +10 -0
  42. package/dist/src/services/formatter-service.js +18 -0
  43. package/dist/src/services/generator-context.d.ts +2 -2
  44. package/dist/src/services/generator-context.js +2 -2
  45. package/dist/src/services/generator-service.d.ts +9 -8
  46. package/dist/src/services/generator-service.js +39 -43
  47. package/dist/src/services/render-service.d.ts +3 -3
  48. package/dist/src/services/render-service.js +8 -8
  49. package/dist/src/templates/effect-branded-id.eta +2 -0
  50. package/dist/src/templates/effect-enums.eta +9 -0
  51. package/dist/src/templates/effect-index.eta +4 -0
  52. package/dist/src/templates/effect-join-table.eta +8 -0
  53. package/dist/src/templates/effect-model.eta +6 -0
  54. package/dist/src/templates/effect-types-header.eta +7 -0
  55. package/dist/src/templates/index-default.eta +2 -1
  56. package/dist/src/templates/kysely-db-interface.eta +6 -0
  57. package/dist/src/templates/prisma-repository.eta +57 -32
  58. package/package.json +11 -6
@@ -0,0 +1,108 @@
1
+ import type { DMMF } from "@prisma/generator-helper";
2
+ /**
3
+ * Check if a field is a UUID using native DMMF type information
4
+ * 3-tier detection: native type � documentation � field name patterns
5
+ */
6
+ export declare function isUuidField(field: DMMF.Field): boolean;
7
+ /**
8
+ * Get the database column name for a field (respects @map directive)
9
+ */
10
+ export declare function getFieldDbName(field: DMMF.Field): string;
11
+ /**
12
+ * Check if field has a default value using native DMMF property
13
+ */
14
+ export declare function hasDefaultValue(field: DMMF.Field): boolean;
15
+ /**
16
+ * Check if field is an ID field using native DMMF property
17
+ */
18
+ export declare function isIdField(field: DMMF.Field): boolean;
19
+ /**
20
+ * Check if field is required using native DMMF property
21
+ */
22
+ export declare function isRequiredField(field: DMMF.Field): boolean;
23
+ /**
24
+ * Check if field is a list/array using native DMMF property
25
+ */
26
+ export declare function isListField(field: DMMF.Field): boolean;
27
+ /**
28
+ * Filter models to exclude internal models (starting with _)
29
+ */
30
+ export declare function filterInternalModels(models: readonly DMMF.Model[]): DMMF.ReadonlyDeep<{
31
+ name: string;
32
+ dbName: string | null;
33
+ schema: string | null;
34
+ fields: DMMF.Field[];
35
+ uniqueFields: string[][];
36
+ uniqueIndexes: DMMF.uniqueIndex[];
37
+ documentation?: string;
38
+ primaryKey: DMMF.PrimaryKey | null;
39
+ isGenerated?: boolean;
40
+ }>[];
41
+ /**
42
+ * Filter fields to only include scalar and enum fields (exclude relations)
43
+ */
44
+ export declare function filterSchemaFields(fields: readonly DMMF.Field[]): DMMF.ReadonlyDeep<{
45
+ kind: DMMF.FieldKind;
46
+ name: string;
47
+ isRequired: boolean;
48
+ isList: boolean;
49
+ isUnique: boolean;
50
+ isId: boolean;
51
+ isReadOnly: boolean;
52
+ isGenerated?: boolean;
53
+ isUpdatedAt?: boolean;
54
+ type: string;
55
+ nativeType?: [string, string[]] | null;
56
+ dbName?: string | null;
57
+ hasDefaultValue: boolean;
58
+ default?: DMMF.FieldDefault | DMMF.FieldDefaultScalar | DMMF.FieldDefaultScalar[];
59
+ relationFromFields?: string[];
60
+ relationToFields?: string[];
61
+ relationOnDelete?: string;
62
+ relationOnUpdate?: string;
63
+ relationName?: string;
64
+ documentation?: string;
65
+ }>[];
66
+ /**
67
+ * Get the database table name for a model (respects @@map directive)
68
+ */
69
+ export declare function getModelDbName(model: DMMF.Model): string;
70
+ /**
71
+ * Sort models alphabetically for deterministic output
72
+ */
73
+ export declare function sortModels(models: readonly DMMF.Model[]): DMMF.ReadonlyDeep<{
74
+ name: string;
75
+ dbName: string | null;
76
+ schema: string | null;
77
+ fields: DMMF.Field[];
78
+ uniqueFields: string[][];
79
+ uniqueIndexes: DMMF.uniqueIndex[];
80
+ documentation?: string;
81
+ primaryKey: DMMF.PrimaryKey | null;
82
+ isGenerated?: boolean;
83
+ }>[];
84
+ /**
85
+ * Sort fields alphabetically for deterministic output
86
+ */
87
+ export declare function sortFields(fields: readonly DMMF.Field[]): DMMF.ReadonlyDeep<{
88
+ kind: DMMF.FieldKind;
89
+ name: string;
90
+ isRequired: boolean;
91
+ isList: boolean;
92
+ isUnique: boolean;
93
+ isId: boolean;
94
+ isReadOnly: boolean;
95
+ isGenerated?: boolean;
96
+ isUpdatedAt?: boolean;
97
+ type: string;
98
+ nativeType?: [string, string[]] | null;
99
+ dbName?: string | null;
100
+ hasDefaultValue: boolean;
101
+ default?: DMMF.FieldDefault | DMMF.FieldDefaultScalar | DMMF.FieldDefaultScalar[];
102
+ relationFromFields?: string[];
103
+ relationToFields?: string[];
104
+ relationOnDelete?: string;
105
+ relationOnUpdate?: string;
106
+ relationName?: string;
107
+ documentation?: string;
108
+ }>[];
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Check if a field is a UUID using native DMMF type information
3
+ * 3-tier detection: native type � documentation � field name patterns
4
+ */
5
+ export function isUuidField(field) {
6
+ // 1. Check native type (most reliable)
7
+ if (field.nativeType?.[0] === "Uuid") {
8
+ return true;
9
+ }
10
+ // 2. Check documentation for @db.Uuid
11
+ if (field.documentation?.includes("@db.Uuid")) {
12
+ return true;
13
+ }
14
+ // 3. Fallback: Field name patterns (only for String type)
15
+ if (field.type !== "String") {
16
+ return false;
17
+ }
18
+ const uuidFieldPatterns = [
19
+ /^id$/, // Primary ID fields
20
+ /_id$/, // Foreign key ID fields
21
+ /^.*_uuid$/, // uuid suffix
22
+ /^uuid$/, // Direct uuid fields
23
+ ];
24
+ return uuidFieldPatterns.some((pattern) => pattern.test(field.name));
25
+ }
26
+ /**
27
+ * Get the database column name for a field (respects @map directive)
28
+ */
29
+ export function getFieldDbName(field) {
30
+ return field.dbName ?? field.name;
31
+ }
32
+ /**
33
+ * Check if field has a default value using native DMMF property
34
+ */
35
+ export function hasDefaultValue(field) {
36
+ return field.hasDefaultValue === true;
37
+ }
38
+ /**
39
+ * Check if field is an ID field using native DMMF property
40
+ */
41
+ export function isIdField(field) {
42
+ return field.isId === true;
43
+ }
44
+ /**
45
+ * Check if field is required using native DMMF property
46
+ */
47
+ export function isRequiredField(field) {
48
+ return field.isRequired === true;
49
+ }
50
+ /**
51
+ * Check if field is a list/array using native DMMF property
52
+ */
53
+ export function isListField(field) {
54
+ return field.isList === true;
55
+ }
56
+ /**
57
+ * Filter models to exclude internal models (starting with _)
58
+ */
59
+ export function filterInternalModels(models) {
60
+ return models.filter((model) => !model.name.startsWith("_"));
61
+ }
62
+ /**
63
+ * Filter fields to only include scalar and enum fields (exclude relations)
64
+ */
65
+ export function filterSchemaFields(fields) {
66
+ return fields.filter((field) => field.kind === "scalar" || field.kind === "enum");
67
+ }
68
+ /**
69
+ * Get the database table name for a model (respects @@map directive)
70
+ */
71
+ export function getModelDbName(model) {
72
+ return model.dbName ?? model.name;
73
+ }
74
+ /**
75
+ * Sort models alphabetically for deterministic output
76
+ */
77
+ export function sortModels(models) {
78
+ return models.slice().sort((a, b) => a.name.localeCompare(b.name));
79
+ }
80
+ /**
81
+ * Sort fields alphabetically for deterministic output
82
+ */
83
+ export function sortFields(fields) {
84
+ return fields.slice().sort((a, b) => a.name.localeCompare(b.name));
85
+ }
@@ -0,0 +1,32 @@
1
+ import type { DMMF } from "@prisma/generator-helper";
2
+ /**
3
+ * @customType Annotation Parser
4
+ *
5
+ * Allows overriding Effect Schema types for Prisma-supported fields.
6
+ *
7
+ * WORKS FOR: Prisma scalar types (String, Int, Boolean, DateTime, etc.)
8
+ *
9
+ * USE CASES:
10
+ * // Email validation for String field
11
+ * /// @customType(Schema.String.pipe(Schema.email()))
12
+ * email String
13
+ *
14
+ * // Positive number constraint for Int field
15
+ * /// @customType(Schema.Number.pipe(Schema.positive()))
16
+ * age Int
17
+ *
18
+ * // Custom branded type
19
+ * /// @customType(Schema.String.pipe(Schema.brand('UserId')))
20
+ * userId String
21
+ *
22
+ * @param field - Prisma DMMF field
23
+ * @returns Extracted type string or null if no annotation found
24
+ */
25
+ export declare function extractEffectTypeOverride(field: DMMF.Field): string | null;
26
+ /**
27
+ * Check if field has any custom type annotations
28
+ *
29
+ * @param fields - Array of Prisma fields
30
+ * @returns true if any field uses custom types in @effectType
31
+ */
32
+ export declare function hasCustomTypeAnnotations(fields: readonly DMMF.Field[]): boolean;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * @customType Annotation Parser
3
+ *
4
+ * Allows overriding Effect Schema types for Prisma-supported fields.
5
+ *
6
+ * WORKS FOR: Prisma scalar types (String, Int, Boolean, DateTime, etc.)
7
+ *
8
+ * USE CASES:
9
+ * // Email validation for String field
10
+ * /// @customType(Schema.String.pipe(Schema.email()))
11
+ * email String
12
+ *
13
+ * // Positive number constraint for Int field
14
+ * /// @customType(Schema.Number.pipe(Schema.positive()))
15
+ * age Int
16
+ *
17
+ * // Custom branded type
18
+ * /// @customType(Schema.String.pipe(Schema.brand('UserId')))
19
+ * userId String
20
+ *
21
+ * @param field - Prisma DMMF field
22
+ * @returns Extracted type string or null if no annotation found
23
+ */
24
+ export function extractEffectTypeOverride(field) {
25
+ if (!field.documentation)
26
+ return null;
27
+ // Match @customType annotation - handle balanced parentheses
28
+ const annotationMatch = field.documentation.match(/@customType\s*\(/);
29
+ if (!annotationMatch)
30
+ return null;
31
+ // Find the matching closing parenthesis
32
+ const startIdx = field.documentation.indexOf("@customType(") + "@customType(".length;
33
+ let parenCount = 1;
34
+ let endIdx = startIdx;
35
+ for (let i = startIdx; i < field.documentation.length && parenCount > 0; i++) {
36
+ if (field.documentation[i] === "(")
37
+ parenCount++;
38
+ if (field.documentation[i] === ")")
39
+ parenCount--;
40
+ if (parenCount === 0) {
41
+ endIdx = i;
42
+ break;
43
+ }
44
+ }
45
+ if (parenCount !== 0) {
46
+ return null;
47
+ }
48
+ const typeStr = field.documentation.substring(startIdx, endIdx).trim();
49
+ // Validate it's either a custom type or starts with Schema.
50
+ if (!(typeStr.startsWith("Schema.") || isCustomType(typeStr))) {
51
+ return null;
52
+ }
53
+ return typeStr;
54
+ }
55
+ /**
56
+ * Check if type string is a custom type reference
57
+ *
58
+ * Custom types are PascalCase identifiers without dots:
59
+ * - Valid: Vector1536, JSONBType, CustomEnum
60
+ * - Invalid: Schema.String, some.nested.type
61
+ *
62
+ * @param typeStr - Type string to check
63
+ * @returns true if it's a custom type reference
64
+ */
65
+ function isCustomType(typeStr) {
66
+ return /^[A-Z][A-Za-z0-9]*$/.test(typeStr);
67
+ }
68
+ /**
69
+ * Check if field has any custom type annotations
70
+ *
71
+ * @param fields - Array of Prisma fields
72
+ * @returns true if any field uses custom types in @effectType
73
+ */
74
+ export function hasCustomTypeAnnotations(fields) {
75
+ return fields.some((field) => {
76
+ const override = extractEffectTypeOverride(field);
77
+ return override && isCustomType(override);
78
+ });
79
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Code generation utilities
3
+ */
4
+ /**
5
+ * Generate standard file header for generated files
6
+ *
7
+ * @param timestamp - Optional timestamp (defaults to current time)
8
+ */
9
+ export declare function generateFileHeader(timestamp?: Date): string;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Code generation utilities
3
+ */
4
+ /**
5
+ * Generate standard file header for generated files
6
+ *
7
+ * @param timestamp - Optional timestamp (defaults to current time)
8
+ */
9
+ export function generateFileHeader(timestamp = new Date()) {
10
+ return `/**
11
+ * Generated: ${timestamp.toISOString()}
12
+ * DO NOT EDIT MANUALLY
13
+ */`;
14
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Naming utilities for consistent TypeScript identifier generation
3
+ */
4
+ /**
5
+ * Convert any string to PascalCase with optional suffix
6
+ * Handles: snake_case, camelCase, kebab-case, or mixed formats
7
+ *
8
+ * @param str - The string to convert
9
+ * @param suffix - Optional suffix to append (e.g., "Schema", "Type")
10
+ *
11
+ * @example
12
+ * toPascalCase('user') // 'User'
13
+ * toPascalCase('PRODUCT_STATUS') // 'ProductStatus'
14
+ * toPascalCase('PRODUCT_STATUS', 'Schema') // 'ProductStatusSchema'
15
+ * toPascalCase('USER_ROLE', 'Type') // 'UserRoleType'
16
+ */
17
+ export declare function toPascalCase(str: string, suffix?: string): string;
18
+ /**
19
+ * Convert PascalCase or camelCase string to snake_case
20
+ * Used for generating database column names from model names
21
+ *
22
+ * @param str - The string to convert
23
+ *
24
+ * @example
25
+ * toSnakeCase('User') // 'user'
26
+ * toSnakeCase('ProductTag') // 'product_tag'
27
+ * toSnakeCase('ProductStatus') // 'product_status'
28
+ */
29
+ export declare function toSnakeCase(str: string): string;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Naming utilities for consistent TypeScript identifier generation
3
+ */
4
+ /**
5
+ * Convert any string to PascalCase with optional suffix
6
+ * Handles: snake_case, camelCase, kebab-case, or mixed formats
7
+ *
8
+ * @param str - The string to convert
9
+ * @param suffix - Optional suffix to append (e.g., "Schema", "Type")
10
+ *
11
+ * @example
12
+ * toPascalCase('user') // 'User'
13
+ * toPascalCase('PRODUCT_STATUS') // 'ProductStatus'
14
+ * toPascalCase('PRODUCT_STATUS', 'Schema') // 'ProductStatusSchema'
15
+ * toPascalCase('USER_ROLE', 'Type') // 'UserRoleType'
16
+ */
17
+ export function toPascalCase(str, suffix) {
18
+ // Handle empty string
19
+ if (!str)
20
+ return str;
21
+ // Split on underscores, dashes, or spaces
22
+ const words = str.split(/[_-\s]+/);
23
+ let pascalCase;
24
+ // If no delimiters found, check if already camelCase/PascalCase
25
+ if (words.length === 1) {
26
+ // Split camelCase/PascalCase by capital letters
27
+ const camelWords = str.split(/(?=[A-Z])/);
28
+ if (camelWords.length > 1) {
29
+ pascalCase = camelWords
30
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
31
+ .join("");
32
+ }
33
+ else {
34
+ // Single word - just capitalize first letter
35
+ pascalCase = str.charAt(0).toUpperCase() + str.slice(1);
36
+ }
37
+ }
38
+ else {
39
+ // Convert each word to PascalCase
40
+ pascalCase = words
41
+ .filter((word) => word.length > 0) // Remove empty strings
42
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
43
+ .join("");
44
+ }
45
+ return suffix ? `${pascalCase}${suffix}` : pascalCase;
46
+ }
47
+ /**
48
+ * Convert PascalCase or camelCase string to snake_case
49
+ * Used for generating database column names from model names
50
+ *
51
+ * @param str - The string to convert
52
+ *
53
+ * @example
54
+ * toSnakeCase('User') // 'user'
55
+ * toSnakeCase('ProductTag') // 'product_tag'
56
+ * toSnakeCase('ProductStatus') // 'product_status'
57
+ */
58
+ export function toSnakeCase(str) {
59
+ if (!str)
60
+ return str;
61
+ return (str
62
+ // Insert underscore before uppercase letters (except at start)
63
+ .replace(/([A-Z])/g, "_$1")
64
+ // Remove leading underscore if present
65
+ .replace(/^_/, "")
66
+ // Convert to lowercase
67
+ .toLowerCase());
68
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Centralized Prisma type mappings
3
+ *
4
+ * Single source of truth for mapping Prisma scalar types to:
5
+ * - Effect Schema types (for schema generation)
6
+ * - TypeScript types (for Kysely interfaces)
7
+ *
8
+ * This eliminates duplication across effect/type.ts, kysely/type.ts, and effect/join-table.ts
9
+ */
10
+ /**
11
+ * Prisma scalar type mapping to Effect Schema types
12
+ * Uses const assertion for type safety
13
+ *
14
+ * Note: DateTime uses Schema.DateFromSelf (not Schema.Date) so that:
15
+ * - Type = Date (runtime)
16
+ * - Encoded = Date (database)
17
+ * This allows Kysely to work with native Date objects directly.
18
+ * Schema.Date would encode to string, requiring ISO string conversions.
19
+ */
20
+ export declare const PRISMA_TO_EFFECT_SCHEMA: {
21
+ readonly String: "Schema.String";
22
+ readonly Int: "Schema.Number";
23
+ readonly Float: "Schema.Number";
24
+ readonly BigInt: "Schema.BigInt";
25
+ readonly Decimal: "Schema.String";
26
+ readonly Boolean: "Schema.Boolean";
27
+ readonly DateTime: "Schema.DateFromSelf";
28
+ readonly Json: "Schema.Unknown";
29
+ readonly Bytes: "Schema.Uint8Array";
30
+ };
31
+ /**
32
+ * Prisma scalar type mapping to TypeScript types (for Kysely interfaces)
33
+ */
34
+ export declare const PRISMA_TO_TYPESCRIPT: {
35
+ readonly String: "string";
36
+ readonly Int: "number";
37
+ readonly Float: "number";
38
+ readonly Boolean: "boolean";
39
+ readonly DateTime: "Date";
40
+ readonly Json: "unknown";
41
+ readonly Bytes: "Buffer";
42
+ readonly Decimal: "string";
43
+ readonly BigInt: "string";
44
+ };
45
+ /**
46
+ * Type-safe key type for Prisma scalar types
47
+ */
48
+ export type PrismaScalarType = keyof typeof PRISMA_TO_EFFECT_SCHEMA;
49
+ /**
50
+ * Type guard to check if a string is a valid Prisma scalar type
51
+ */
52
+ export declare function isPrismaScalarType(type: string): type is PrismaScalarType;
53
+ /**
54
+ * Get Effect Schema type for a Prisma scalar type
55
+ * Returns undefined for non-scalar types (enums, relations)
56
+ */
57
+ export declare function getEffectSchemaType(type: string): "Schema.String" | "Schema.Number" | "Schema.BigInt" | "Schema.Boolean" | "Schema.DateFromSelf" | "Schema.Unknown" | "Schema.Uint8Array" | undefined;
58
+ /**
59
+ * Get TypeScript type for a Prisma scalar type
60
+ * Returns the input type unchanged for non-scalar types (enums, models)
61
+ */
62
+ export declare function getTypeScriptType(type: string): string;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Centralized Prisma type mappings
3
+ *
4
+ * Single source of truth for mapping Prisma scalar types to:
5
+ * - Effect Schema types (for schema generation)
6
+ * - TypeScript types (for Kysely interfaces)
7
+ *
8
+ * This eliminates duplication across effect/type.ts, kysely/type.ts, and effect/join-table.ts
9
+ */
10
+ /**
11
+ * Prisma scalar type mapping to Effect Schema types
12
+ * Uses const assertion for type safety
13
+ *
14
+ * Note: DateTime uses Schema.DateFromSelf (not Schema.Date) so that:
15
+ * - Type = Date (runtime)
16
+ * - Encoded = Date (database)
17
+ * This allows Kysely to work with native Date objects directly.
18
+ * Schema.Date would encode to string, requiring ISO string conversions.
19
+ */
20
+ export const PRISMA_TO_EFFECT_SCHEMA = {
21
+ String: "Schema.String",
22
+ Int: "Schema.Number",
23
+ Float: "Schema.Number",
24
+ BigInt: "Schema.BigInt",
25
+ Decimal: "Schema.String", // For precision
26
+ Boolean: "Schema.Boolean",
27
+ DateTime: "Schema.DateFromSelf", // Native Date type for Kysely compatibility
28
+ Json: "Schema.Unknown", // Safe unknown type
29
+ Bytes: "Schema.Uint8Array",
30
+ };
31
+ /**
32
+ * Prisma scalar type mapping to TypeScript types (for Kysely interfaces)
33
+ */
34
+ export const PRISMA_TO_TYPESCRIPT = {
35
+ String: "string",
36
+ Int: "number",
37
+ Float: "number",
38
+ Boolean: "boolean",
39
+ DateTime: "Date",
40
+ Json: "unknown",
41
+ Bytes: "Buffer",
42
+ Decimal: "string",
43
+ BigInt: "string", // Kysely convention
44
+ };
45
+ /**
46
+ * Type guard to check if a string is a valid Prisma scalar type
47
+ */
48
+ export function isPrismaScalarType(type) {
49
+ return type in PRISMA_TO_EFFECT_SCHEMA;
50
+ }
51
+ /**
52
+ * Get Effect Schema type for a Prisma scalar type
53
+ * Returns undefined for non-scalar types (enums, relations)
54
+ */
55
+ export function getEffectSchemaType(type) {
56
+ if (isPrismaScalarType(type)) {
57
+ return PRISMA_TO_EFFECT_SCHEMA[type];
58
+ }
59
+ return undefined;
60
+ }
61
+ /**
62
+ * Get TypeScript type for a Prisma scalar type
63
+ * Returns the input type unchanged for non-scalar types (enums, models)
64
+ */
65
+ export function getTypeScriptType(type) {
66
+ if (isPrismaScalarType(type)) {
67
+ return PRISMA_TO_TYPESCRIPT[type];
68
+ }
69
+ return type;
70
+ }
@@ -0,0 +1,10 @@
1
+ import * as Context from "effect/Context";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Layer from "effect/Layer";
4
+ declare const FormatterService_base: Context.TagClass<FormatterService, "FormatterService", {
5
+ readonly format: (code: string) => Effect.Effect<string, Error>;
6
+ }>;
7
+ export declare class FormatterService extends FormatterService_base {
8
+ static Live: Layer.Layer<FormatterService, never, never>;
9
+ }
10
+ export {};
@@ -0,0 +1,18 @@
1
+ import * as Context from "effect/Context";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Layer from "effect/Layer";
4
+ import { createFromBuffer } from "@dprint/formatter";
5
+ import { getPath } from "@dprint/typescript";
6
+ import * as fs from "node:fs";
7
+ export class FormatterService extends Context.Tag("FormatterService")() {
8
+ static Live = Layer.sync(FormatterService, () => {
9
+ const buffer = fs.readFileSync(getPath());
10
+ const formatter = createFromBuffer(buffer);
11
+ return {
12
+ format: (code) => Effect.try({
13
+ try: () => formatter.formatText({ filePath: "file.ts", fileText: code }),
14
+ catch: (error) => new Error(`Failed to format code: ${error}`),
15
+ }),
16
+ };
17
+ });
18
+ }
@@ -1,5 +1,5 @@
1
- import type { GeneratorOptions } from '@prisma/generator-helper';
2
- import * as Context from 'effect/Context';
1
+ import type { GeneratorOptions } from "@prisma/generator-helper";
2
+ import * as Context from "effect/Context";
3
3
  declare const GeneratorContext_base: Context.TagClass<GeneratorContext, "GeneratorContext", GeneratorOptions>;
4
4
  export declare class GeneratorContext extends GeneratorContext_base {
5
5
  }
@@ -1,3 +1,3 @@
1
- import * as Context from 'effect/Context';
2
- export class GeneratorContext extends Context.Tag('GeneratorContext')() {
1
+ import * as Context from "effect/Context";
2
+ export class GeneratorContext extends Context.Tag("GeneratorContext")() {
3
3
  }
@@ -1,14 +1,15 @@
1
- import * as FileSystem from '@effect/platform/FileSystem';
2
- import * as Path from '@effect/platform/Path';
3
- import * as Context from 'effect/Context';
4
- import * as Effect from 'effect/Effect';
5
- import * as Layer from 'effect/Layer';
6
- import { GeneratorContext } from './generator-context.js';
7
- import { RenderService } from './render-service.js';
1
+ import * as FileSystem from "@effect/platform/FileSystem";
2
+ import * as Path from "@effect/platform/Path";
3
+ import * as Context from "effect/Context";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Layer from "effect/Layer";
6
+ import { GeneratorContext } from "./generator-context.js";
7
+ import { RenderService } from "./render-service.js";
8
+ import { FormatterService } from "./formatter-service.js";
8
9
  declare const GeneratorService_base: Context.TagClass<GeneratorService, "GeneratorService", {
9
10
  readonly generate: Effect.Effect<void, Error, GeneratorContext>;
10
11
  }>;
11
12
  export declare class GeneratorService extends GeneratorService_base {
12
- static Live: Layer.Layer<GeneratorService, never, FileSystem.FileSystem | Path.Path | RenderService>;
13
+ static Live: Layer.Layer<GeneratorService, never, FileSystem.FileSystem | Path.Path | RenderService | FormatterService>;
13
14
  }
14
15
  export {};