diff --git a/examples/prisma-next-demo/diamond-contract/c1.d.ts b/examples/prisma-next-demo/diamond-contract/c1.d.ts new file mode 100644 index 0000000000..9f1fb581d1 --- /dev/null +++ b/examples/prisma-next-demo/diamond-contract/c1.d.ts @@ -0,0 +1,197 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma-next contract emit'. +// To regenerate, run: prisma-next contract emit +import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; +import type { + Bit, + Char, + CodecTypes as PgTypes, + Interval, + JsonValue, + Numeric, + Time, + Timestamp, + Timestamptz, + Timetz, + VarBit, + Varchar, +} from '@prisma-next/target-postgres/codec-types'; + +import type { + ContractWithTypeMaps, + TypeMaps as TypeMapsType, +} from '@prisma-next/sql-contract/types'; +import type { + Contract as ContractType, + ContractModelsMap, + ExecutionHashBase, + NamespaceId, + ProfileHashBase, + StorageHashBase, +} from '@prisma-next/contract/types'; + +export type StorageHash = + StorageHashBase<'sha256:789dd79ab5ab725be1b6ced088109b803a4d62f9874f932eb384a868d94360a4'>; +export type ExecutionHash = + ExecutionHashBase<'sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545'>; +export type ProfileHash = + ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; + +export type CodecTypes = PgTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgAdapterQueryOps; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? CodecTypes[CodecId]['output'] + : _Encoded; + +export type FieldOutputTypes = { + readonly user: { readonly id: Char<36>; readonly email: CodecTypes['pg/text@1']['output'] }; +}; +export type FieldInputTypes = { + readonly user: { + readonly id: CodecTypes['sql/char@1']['input']; + readonly email: CodecTypes['pg/text@1']['input']; + }; +}; +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes +>; + +type ContractBase = Omit< + ContractType< + { + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'sql-namespace'; + readonly tables: { + readonly user: { + columns: { + readonly id: { + readonly nativeType: 'character'; + readonly codecId: 'sql/char@1'; + readonly nullable: false; + readonly typeParams: { readonly length: 36 }; + }; + readonly email: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }, + { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + }; + }; + }; + } + >, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly user: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'user' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; + readonly sql: { + readonly defaultInInsert: true; + readonly enums: true; + readonly lateral: true; + readonly returning: true; + }; + }; + readonly extensionPacks: {}; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly ref: { readonly table: 'user'; readonly column: 'id' }; + readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; + }, + ]; + }; + }; + readonly meta: {}; + + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; +export type Models = ContractModelsMap; diff --git a/examples/prisma-next-demo/diamond-contract/c1.json b/examples/prisma-next-demo/diamond-contract/c1.json new file mode 100644 index 0000000000..56b1859c7f --- /dev/null +++ b/examples/prisma-next-demo/diamond-contract/c1.json @@ -0,0 +1,127 @@ +{ + "schemaVersion": "1", + "targetFamily": "sql", + "target": "postgres", + "profileHash": "sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb", + "roots": { + "user": { + "model": "user", + "namespace": "__unbound__" + } + }, + "domain": { + "namespaces": { + "__unbound__": { + "models": { + "user": { + "fields": { + "email": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { + "length": 36 + } + } + } + }, + "relations": {}, + "storage": { + "fields": { + "email": { + "column": "email" + }, + "id": { + "column": "id" + } + }, + "table": "user" + } + } + } + } + } + }, + "storage": { + "namespaces": { + "__unbound__": { + "id": "__unbound__", + "kind": "postgres-unbound-schema", + "tables": { + "user": { + "columns": { + "email": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "id": { + "codecId": "sql/char@1", + "nativeType": "character", + "nullable": false, + "typeParams": { + "length": 36 + } + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": ["id"] + }, + "uniques": [] + } + } + } + }, + "storageHash": "sha256:789dd79ab5ab725be1b6ced088109b803a4d62f9874f932eb384a868d94360a4" + }, + "execution": { + "executionHash": "sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545", + "mutations": { + "defaults": [ + { + "onCreate": { + "id": "uuidv4", + "kind": "generator" + }, + "ref": { + "column": "id", + "table": "user" + } + } + ] + } + }, + "capabilities": { + "postgres": { + "distinctOn": true, + "jsonAgg": true, + "lateral": true, + "limit": true, + "orderBy": true, + "returning": true + }, + "sql": { + "defaultInInsert": true, + "enums": true, + "lateral": true, + "returning": true + } + }, + "extensionPacks": {}, + "meta": {}, + "_generated": { + "warning": "⚠️ GENERATED FILE - DO NOT EDIT", + "message": "This file is automatically generated by \"prisma-next contract emit\".", + "regenerate": "To regenerate, run: prisma-next contract emit" + } +} diff --git a/examples/prisma-next-demo/diamond-contract/c1.prisma b/examples/prisma-next-demo/diamond-contract/c1.prisma new file mode 100644 index 0000000000..10c38f7db8 --- /dev/null +++ b/examples/prisma-next-demo/diamond-contract/c1.prisma @@ -0,0 +1,6 @@ +// use prisma-next + +model user { + id String @id @default(uuid()) + email String +} diff --git a/examples/prisma-next-demo/diamond-contract/c2.d.ts b/examples/prisma-next-demo/diamond-contract/c2.d.ts new file mode 100644 index 0000000000..1d0b5a21b9 --- /dev/null +++ b/examples/prisma-next-demo/diamond-contract/c2.d.ts @@ -0,0 +1,217 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma-next contract emit'. +// To regenerate, run: prisma-next contract emit +import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; +import type { + Bit, + Char, + CodecTypes as PgTypes, + Interval, + JsonValue, + Numeric, + Time, + Timestamp, + Timestamptz, + Timetz, + VarBit, + Varchar, +} from '@prisma-next/target-postgres/codec-types'; + +import type { + ContractWithTypeMaps, + TypeMaps as TypeMapsType, +} from '@prisma-next/sql-contract/types'; +import type { + Contract as ContractType, + ContractModelsMap, + ExecutionHashBase, + NamespaceId, + ProfileHashBase, + StorageHashBase, +} from '@prisma-next/contract/types'; + +export type StorageHash = + StorageHashBase<'sha256:93be6c200743261baf55f0586b1380a1c0ade3c48730c09a8fec71ba419c2464'>; +export type ExecutionHash = + ExecutionHashBase<'sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545'>; +export type ProfileHash = + ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; + +export type CodecTypes = PgTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgAdapterQueryOps; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? CodecTypes[CodecId]['output'] + : _Encoded; + +export type FieldOutputTypes = { + readonly user: { + readonly id: Char<36>; + readonly email: CodecTypes['pg/text@1']['output']; + readonly phone: CodecTypes['pg/text@1']['output'] | null; + }; +}; +export type FieldInputTypes = { + readonly user: { + readonly id: CodecTypes['sql/char@1']['input']; + readonly email: CodecTypes['pg/text@1']['input']; + readonly phone: CodecTypes['pg/text@1']['input'] | null; + }; +}; +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes +>; + +type ContractBase = Omit< + ContractType< + { + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'sql-namespace'; + readonly tables: { + readonly user: { + columns: { + readonly id: { + readonly nativeType: 'character'; + readonly codecId: 'sql/char@1'; + readonly nullable: false; + readonly typeParams: { readonly length: 36 }; + }; + readonly email: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly phone: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }, + { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly phone: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly phone: { readonly column: 'phone' }; + }; + }; + }; + } + >, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly user: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'user' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly phone: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly phone: { readonly column: 'phone' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; + readonly sql: { + readonly defaultInInsert: true; + readonly enums: true; + readonly lateral: true; + readonly returning: true; + }; + }; + readonly extensionPacks: {}; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly ref: { readonly table: 'user'; readonly column: 'id' }; + readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; + }, + ]; + }; + }; + readonly meta: {}; + + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; +export type Models = ContractModelsMap; diff --git a/examples/prisma-next-demo/diamond-contract/c2.json b/examples/prisma-next-demo/diamond-contract/c2.json new file mode 100644 index 0000000000..56e6c8de2b --- /dev/null +++ b/examples/prisma-next-demo/diamond-contract/c2.json @@ -0,0 +1,142 @@ +{ + "schemaVersion": "1", + "targetFamily": "sql", + "target": "postgres", + "profileHash": "sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb", + "roots": { + "user": { + "model": "user", + "namespace": "__unbound__" + } + }, + "domain": { + "namespaces": { + "__unbound__": { + "models": { + "user": { + "fields": { + "email": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { + "length": 36 + } + } + }, + "phone": { + "nullable": true, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + }, + "relations": {}, + "storage": { + "fields": { + "email": { + "column": "email" + }, + "id": { + "column": "id" + }, + "phone": { + "column": "phone" + } + }, + "table": "user" + } + } + } + } + } + }, + "storage": { + "namespaces": { + "__unbound__": { + "id": "__unbound__", + "kind": "postgres-unbound-schema", + "tables": { + "user": { + "columns": { + "email": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "id": { + "codecId": "sql/char@1", + "nativeType": "character", + "nullable": false, + "typeParams": { + "length": 36 + } + }, + "phone": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": true + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": ["id"] + }, + "uniques": [] + } + } + } + }, + "storageHash": "sha256:93be6c200743261baf55f0586b1380a1c0ade3c48730c09a8fec71ba419c2464" + }, + "execution": { + "executionHash": "sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545", + "mutations": { + "defaults": [ + { + "onCreate": { + "id": "uuidv4", + "kind": "generator" + }, + "ref": { + "column": "id", + "table": "user" + } + } + ] + } + }, + "capabilities": { + "postgres": { + "distinctOn": true, + "jsonAgg": true, + "lateral": true, + "limit": true, + "orderBy": true, + "returning": true + }, + "sql": { + "defaultInInsert": true, + "enums": true, + "lateral": true, + "returning": true + } + }, + "extensionPacks": {}, + "meta": {}, + "_generated": { + "warning": "⚠️ GENERATED FILE - DO NOT EDIT", + "message": "This file is automatically generated by \"prisma-next contract emit\".", + "regenerate": "To regenerate, run: prisma-next contract emit" + } +} diff --git a/examples/prisma-next-demo/diamond-contract/c2.prisma b/examples/prisma-next-demo/diamond-contract/c2.prisma new file mode 100644 index 0000000000..f31ab58c1e --- /dev/null +++ b/examples/prisma-next-demo/diamond-contract/c2.prisma @@ -0,0 +1,7 @@ +// use prisma-next + +model user { + id String @id @default(uuid()) + email String + phone String? +} diff --git a/examples/prisma-next-demo/diamond-contract/c3.d.ts b/examples/prisma-next-demo/diamond-contract/c3.d.ts new file mode 100644 index 0000000000..c2fef632f6 --- /dev/null +++ b/examples/prisma-next-demo/diamond-contract/c3.d.ts @@ -0,0 +1,217 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma-next contract emit'. +// To regenerate, run: prisma-next contract emit +import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; +import type { + Bit, + Char, + CodecTypes as PgTypes, + Interval, + JsonValue, + Numeric, + Time, + Timestamp, + Timestamptz, + Timetz, + VarBit, + Varchar, +} from '@prisma-next/target-postgres/codec-types'; + +import type { + ContractWithTypeMaps, + TypeMaps as TypeMapsType, +} from '@prisma-next/sql-contract/types'; +import type { + Contract as ContractType, + ContractModelsMap, + ExecutionHashBase, + NamespaceId, + ProfileHashBase, + StorageHashBase, +} from '@prisma-next/contract/types'; + +export type StorageHash = + StorageHashBase<'sha256:7e3fa7fbe98974385451444c229337c87ec90c547a9214f81700f86b5af3563d'>; +export type ExecutionHash = + ExecutionHashBase<'sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545'>; +export type ProfileHash = + ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; + +export type CodecTypes = PgTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgAdapterQueryOps; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? CodecTypes[CodecId]['output'] + : _Encoded; + +export type FieldOutputTypes = { + readonly user: { + readonly id: Char<36>; + readonly email: CodecTypes['pg/text@1']['output']; + readonly avatar: CodecTypes['pg/text@1']['output'] | null; + }; +}; +export type FieldInputTypes = { + readonly user: { + readonly id: CodecTypes['sql/char@1']['input']; + readonly email: CodecTypes['pg/text@1']['input']; + readonly avatar: CodecTypes['pg/text@1']['input'] | null; + }; +}; +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes +>; + +type ContractBase = Omit< + ContractType< + { + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'sql-namespace'; + readonly tables: { + readonly user: { + columns: { + readonly id: { + readonly nativeType: 'character'; + readonly codecId: 'sql/char@1'; + readonly nullable: false; + readonly typeParams: { readonly length: 36 }; + }; + readonly email: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly avatar: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }, + { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly avatar: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly avatar: { readonly column: 'avatar' }; + }; + }; + }; + } + >, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly user: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'user' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly avatar: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly avatar: { readonly column: 'avatar' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; + readonly sql: { + readonly defaultInInsert: true; + readonly enums: true; + readonly lateral: true; + readonly returning: true; + }; + }; + readonly extensionPacks: {}; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly ref: { readonly table: 'user'; readonly column: 'id' }; + readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; + }, + ]; + }; + }; + readonly meta: {}; + + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; +export type Models = ContractModelsMap; diff --git a/examples/prisma-next-demo/diamond-contract/c3.json b/examples/prisma-next-demo/diamond-contract/c3.json new file mode 100644 index 0000000000..1e44db94bc --- /dev/null +++ b/examples/prisma-next-demo/diamond-contract/c3.json @@ -0,0 +1,142 @@ +{ + "schemaVersion": "1", + "targetFamily": "sql", + "target": "postgres", + "profileHash": "sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb", + "roots": { + "user": { + "model": "user", + "namespace": "__unbound__" + } + }, + "domain": { + "namespaces": { + "__unbound__": { + "models": { + "user": { + "fields": { + "avatar": { + "nullable": true, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "email": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { + "length": 36 + } + } + } + }, + "relations": {}, + "storage": { + "fields": { + "avatar": { + "column": "avatar" + }, + "email": { + "column": "email" + }, + "id": { + "column": "id" + } + }, + "table": "user" + } + } + } + } + } + }, + "storage": { + "namespaces": { + "__unbound__": { + "id": "__unbound__", + "kind": "postgres-unbound-schema", + "tables": { + "user": { + "columns": { + "avatar": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": true + }, + "email": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "id": { + "codecId": "sql/char@1", + "nativeType": "character", + "nullable": false, + "typeParams": { + "length": 36 + } + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": ["id"] + }, + "uniques": [] + } + } + } + }, + "storageHash": "sha256:7e3fa7fbe98974385451444c229337c87ec90c547a9214f81700f86b5af3563d" + }, + "execution": { + "executionHash": "sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545", + "mutations": { + "defaults": [ + { + "onCreate": { + "id": "uuidv4", + "kind": "generator" + }, + "ref": { + "column": "id", + "table": "user" + } + } + ] + } + }, + "capabilities": { + "postgres": { + "distinctOn": true, + "jsonAgg": true, + "lateral": true, + "limit": true, + "orderBy": true, + "returning": true + }, + "sql": { + "defaultInInsert": true, + "enums": true, + "lateral": true, + "returning": true + } + }, + "extensionPacks": {}, + "meta": {}, + "_generated": { + "warning": "⚠️ GENERATED FILE - DO NOT EDIT", + "message": "This file is automatically generated by \"prisma-next contract emit\".", + "regenerate": "To regenerate, run: prisma-next contract emit" + } +} diff --git a/examples/prisma-next-demo/diamond-contract/c3.prisma b/examples/prisma-next-demo/diamond-contract/c3.prisma new file mode 100644 index 0000000000..a6a19fa7fb --- /dev/null +++ b/examples/prisma-next-demo/diamond-contract/c3.prisma @@ -0,0 +1,7 @@ +// use prisma-next + +model user { + id String @id @default(uuid()) + email String + avatar String? +} diff --git a/examples/prisma-next-demo/diamond-contract/c5.d.ts b/examples/prisma-next-demo/diamond-contract/c5.d.ts new file mode 100644 index 0000000000..9dc3794b7c --- /dev/null +++ b/examples/prisma-next-demo/diamond-contract/c5.d.ts @@ -0,0 +1,234 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma-next contract emit'. +// To regenerate, run: prisma-next contract emit +import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; +import type { + Bit, + Char, + CodecTypes as PgTypes, + Interval, + JsonValue, + Numeric, + Time, + Timestamp, + Timestamptz, + Timetz, + VarBit, + Varchar, +} from '@prisma-next/target-postgres/codec-types'; + +import type { + ContractWithTypeMaps, + TypeMaps as TypeMapsType, +} from '@prisma-next/sql-contract/types'; +import type { + Contract as ContractType, + ContractModelsMap, + ExecutionHashBase, + NamespaceId, + ProfileHashBase, + StorageHashBase, +} from '@prisma-next/contract/types'; + +export type StorageHash = + StorageHashBase<'sha256:f9a41d77df6eae57bcd25ab25df31e6e905aad034a5b813f408bf8e78e9f384a'>; +export type ExecutionHash = + ExecutionHashBase<'sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545'>; +export type ProfileHash = + ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; + +export type CodecTypes = PgTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgAdapterQueryOps; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? CodecTypes[CodecId]['output'] + : _Encoded; + +export type FieldOutputTypes = { + readonly user: { + readonly id: Char<36>; + readonly email: CodecTypes['pg/text@1']['output']; + readonly phone: CodecTypes['pg/text@1']['output'] | null; + readonly avatar: CodecTypes['pg/text@1']['output'] | null; + }; +}; +export type FieldInputTypes = { + readonly user: { + readonly id: CodecTypes['sql/char@1']['input']; + readonly email: CodecTypes['pg/text@1']['input']; + readonly phone: CodecTypes['pg/text@1']['input'] | null; + readonly avatar: CodecTypes['pg/text@1']['input'] | null; + }; +}; +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes +>; + +type ContractBase = Omit< + ContractType< + { + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'sql-namespace'; + readonly tables: { + readonly user: { + columns: { + readonly id: { + readonly nativeType: 'character'; + readonly codecId: 'sql/char@1'; + readonly nullable: false; + readonly typeParams: { readonly length: 36 }; + }; + readonly email: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly phone: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + }; + readonly avatar: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }, + { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly phone: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly avatar: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly phone: { readonly column: 'phone' }; + readonly avatar: { readonly column: 'avatar' }; + }; + }; + }; + } + >, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly user: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'user' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly phone: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly avatar: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly phone: { readonly column: 'phone' }; + readonly avatar: { readonly column: 'avatar' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; + readonly sql: { + readonly defaultInInsert: true; + readonly enums: true; + readonly lateral: true; + readonly returning: true; + }; + }; + readonly extensionPacks: {}; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly ref: { readonly table: 'user'; readonly column: 'id' }; + readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; + }, + ]; + }; + }; + readonly meta: {}; + + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; +export type Models = ContractModelsMap; diff --git a/examples/prisma-next-demo/diamond-contract/c5.json b/examples/prisma-next-demo/diamond-contract/c5.json new file mode 100644 index 0000000000..49fdedcde0 --- /dev/null +++ b/examples/prisma-next-demo/diamond-contract/c5.json @@ -0,0 +1,157 @@ +{ + "schemaVersion": "1", + "targetFamily": "sql", + "target": "postgres", + "profileHash": "sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb", + "roots": { + "user": { + "model": "user", + "namespace": "__unbound__" + } + }, + "domain": { + "namespaces": { + "__unbound__": { + "models": { + "user": { + "fields": { + "avatar": { + "nullable": true, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "email": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { + "length": 36 + } + } + }, + "phone": { + "nullable": true, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + }, + "relations": {}, + "storage": { + "fields": { + "avatar": { + "column": "avatar" + }, + "email": { + "column": "email" + }, + "id": { + "column": "id" + }, + "phone": { + "column": "phone" + } + }, + "table": "user" + } + } + } + } + } + }, + "storage": { + "namespaces": { + "__unbound__": { + "id": "__unbound__", + "kind": "postgres-unbound-schema", + "tables": { + "user": { + "columns": { + "avatar": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": true + }, + "email": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "id": { + "codecId": "sql/char@1", + "nativeType": "character", + "nullable": false, + "typeParams": { + "length": 36 + } + }, + "phone": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": true + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": ["id"] + }, + "uniques": [] + } + } + } + }, + "storageHash": "sha256:f9a41d77df6eae57bcd25ab25df31e6e905aad034a5b813f408bf8e78e9f384a" + }, + "execution": { + "executionHash": "sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545", + "mutations": { + "defaults": [ + { + "onCreate": { + "id": "uuidv4", + "kind": "generator" + }, + "ref": { + "column": "id", + "table": "user" + } + } + ] + } + }, + "capabilities": { + "postgres": { + "distinctOn": true, + "jsonAgg": true, + "lateral": true, + "limit": true, + "orderBy": true, + "returning": true + }, + "sql": { + "defaultInInsert": true, + "enums": true, + "lateral": true, + "returning": true + } + }, + "extensionPacks": {}, + "meta": {}, + "_generated": { + "warning": "⚠️ GENERATED FILE - DO NOT EDIT", + "message": "This file is automatically generated by \"prisma-next contract emit\".", + "regenerate": "To regenerate, run: prisma-next contract emit" + } +} diff --git a/examples/prisma-next-demo/diamond-contract/c5.prisma b/examples/prisma-next-demo/diamond-contract/c5.prisma new file mode 100644 index 0000000000..c2742f6b24 --- /dev/null +++ b/examples/prisma-next-demo/diamond-contract/c5.prisma @@ -0,0 +1,8 @@ +// use prisma-next + +model user { + id String @id @default(uuid()) + email String + phone String? + avatar String? +} diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260301T1000_init/end-contract.d.ts b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260301T1000_init/end-contract.d.ts new file mode 100644 index 0000000000..9f1fb581d1 --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260301T1000_init/end-contract.d.ts @@ -0,0 +1,197 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma-next contract emit'. +// To regenerate, run: prisma-next contract emit +import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; +import type { + Bit, + Char, + CodecTypes as PgTypes, + Interval, + JsonValue, + Numeric, + Time, + Timestamp, + Timestamptz, + Timetz, + VarBit, + Varchar, +} from '@prisma-next/target-postgres/codec-types'; + +import type { + ContractWithTypeMaps, + TypeMaps as TypeMapsType, +} from '@prisma-next/sql-contract/types'; +import type { + Contract as ContractType, + ContractModelsMap, + ExecutionHashBase, + NamespaceId, + ProfileHashBase, + StorageHashBase, +} from '@prisma-next/contract/types'; + +export type StorageHash = + StorageHashBase<'sha256:789dd79ab5ab725be1b6ced088109b803a4d62f9874f932eb384a868d94360a4'>; +export type ExecutionHash = + ExecutionHashBase<'sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545'>; +export type ProfileHash = + ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; + +export type CodecTypes = PgTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgAdapterQueryOps; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? CodecTypes[CodecId]['output'] + : _Encoded; + +export type FieldOutputTypes = { + readonly user: { readonly id: Char<36>; readonly email: CodecTypes['pg/text@1']['output'] }; +}; +export type FieldInputTypes = { + readonly user: { + readonly id: CodecTypes['sql/char@1']['input']; + readonly email: CodecTypes['pg/text@1']['input']; + }; +}; +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes +>; + +type ContractBase = Omit< + ContractType< + { + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'sql-namespace'; + readonly tables: { + readonly user: { + columns: { + readonly id: { + readonly nativeType: 'character'; + readonly codecId: 'sql/char@1'; + readonly nullable: false; + readonly typeParams: { readonly length: 36 }; + }; + readonly email: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }, + { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + }; + }; + }; + } + >, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly user: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'user' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; + readonly sql: { + readonly defaultInInsert: true; + readonly enums: true; + readonly lateral: true; + readonly returning: true; + }; + }; + readonly extensionPacks: {}; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly ref: { readonly table: 'user'; readonly column: 'id' }; + readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; + }, + ]; + }; + }; + readonly meta: {}; + + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; +export type Models = ContractModelsMap; diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260301T1000_init/end-contract.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260301T1000_init/end-contract.json new file mode 100644 index 0000000000..56b1859c7f --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260301T1000_init/end-contract.json @@ -0,0 +1,127 @@ +{ + "schemaVersion": "1", + "targetFamily": "sql", + "target": "postgres", + "profileHash": "sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb", + "roots": { + "user": { + "model": "user", + "namespace": "__unbound__" + } + }, + "domain": { + "namespaces": { + "__unbound__": { + "models": { + "user": { + "fields": { + "email": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { + "length": 36 + } + } + } + }, + "relations": {}, + "storage": { + "fields": { + "email": { + "column": "email" + }, + "id": { + "column": "id" + } + }, + "table": "user" + } + } + } + } + } + }, + "storage": { + "namespaces": { + "__unbound__": { + "id": "__unbound__", + "kind": "postgres-unbound-schema", + "tables": { + "user": { + "columns": { + "email": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "id": { + "codecId": "sql/char@1", + "nativeType": "character", + "nullable": false, + "typeParams": { + "length": 36 + } + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": ["id"] + }, + "uniques": [] + } + } + } + }, + "storageHash": "sha256:789dd79ab5ab725be1b6ced088109b803a4d62f9874f932eb384a868d94360a4" + }, + "execution": { + "executionHash": "sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545", + "mutations": { + "defaults": [ + { + "onCreate": { + "id": "uuidv4", + "kind": "generator" + }, + "ref": { + "column": "id", + "table": "user" + } + } + ] + } + }, + "capabilities": { + "postgres": { + "distinctOn": true, + "jsonAgg": true, + "lateral": true, + "limit": true, + "orderBy": true, + "returning": true + }, + "sql": { + "defaultInInsert": true, + "enums": true, + "lateral": true, + "returning": true + } + }, + "extensionPacks": {}, + "meta": {}, + "_generated": { + "warning": "⚠️ GENERATED FILE - DO NOT EDIT", + "message": "This file is automatically generated by \"prisma-next contract emit\".", + "regenerate": "To regenerate, run: prisma-next contract emit" + } +} diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260301T1000_init/migration.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260301T1000_init/migration.json index bbafacdac9..636f122768 100644 --- a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260301T1000_init/migration.json +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260301T1000_init/migration.json @@ -1,7 +1,7 @@ { "from": null, - "to": "sha256:ef9de2767997cc2d57d1a4b6b0a4907d54b94793afd5309225803b14be0463bc", + "to": "sha256:789dd79ab5ab725be1b6ced088109b803a4d62f9874f932eb384a868d94360a4", "providedInvariants": [], - "createdAt": "2026-03-24T15:13:45.632Z", - "migrationHash": "sha256:515bb9837e76359e6f11a0295d80cde8178488d6896fed3591313e207dfb4e8f" + "createdAt": "2026-06-01T17:08:43.960Z", + "migrationHash": "sha256:7f3c2ab0f84053e64264f8ec943d386d314887a8a3ab561597ecde48eb9b7dcc" } diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260301T1000_init/migration.ts b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260301T1000_init/migration.ts new file mode 100755 index 0000000000..acd7519b24 --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260301T1000_init/migration.ts @@ -0,0 +1,27 @@ +#!/usr/bin/env -S node +import { createTable, Migration, MigrationCLI } from '@prisma-next/postgres/migration'; + +export default class M extends Migration { + override describe() { + return { + from: null, + to: 'sha256:789dd79ab5ab725be1b6ced088109b803a4d62f9874f932eb384a868d94360a4', + }; + } + + override get operations() { + return [ + createTable( + '__unbound__', + 'user', + [ + { name: 'email', typeSql: 'text', defaultSql: '', nullable: false }, + { name: 'id', typeSql: 'character(36)', defaultSql: '', nullable: false }, + ], + { columns: ['id'] }, + ), + ]; + } +} + +MigrationCLI.run(import.meta.url, M); diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260301T1000_init/ops.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260301T1000_init/ops.json index 9e60da815b..f1afcd8eff 100644 --- a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260301T1000_init/ops.json +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260301T1000_init/ops.json @@ -1,40 +1,13 @@ [ - { - "id": "extension.vector", - "label": "Enable extension \"vector\"", - "summary": "Ensures the vector extension is available for pgvector operations", - "operationClass": "additive", - "target": { - "id": "postgres" - }, - "precheck": [ - { - "description": "verify extension \"vector\" is not already enabled", - "sql": "SELECT NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector')" - } - ], - "execute": [ - { - "description": "create extension \"vector\"", - "sql": "CREATE EXTENSION IF NOT EXISTS vector" - } - ], - "postcheck": [ - { - "description": "confirm extension \"vector\" is enabled", - "sql": "SELECT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector')" - } - ] - }, { "id": "table.user", - "label": "Create table user", - "summary": "Creates table user with required columns", + "label": "Create table \"user\"", + "summary": "Creates table \"user\"", "operationClass": "additive", "target": { "id": "postgres", "details": { - "schema": "public", + "schema": "__unbound__", "objectType": "table", "name": "user" } @@ -42,19 +15,19 @@ "precheck": [ { "description": "ensure table \"user\" does not exist", - "sql": "SELECT to_regclass('\"public\".\"user\"') IS NULL" + "sql": "SELECT to_regclass('\"user\"') IS NULL" } ], "execute": [ { "description": "create table \"user\"", - "sql": "CREATE TABLE \"public\".\"user\" (\n \"id\" uuid NOT NULL,\n \"email\" text NOT NULL,\n \"createdAt\" timestamptz DEFAULT (now()) NOT NULL,\n PRIMARY KEY (\"id\")\n)" + "sql": "CREATE TABLE \"user\" (\n \"email\" text NOT NULL,\n \"id\" character(36) NOT NULL,\n PRIMARY KEY (\"id\")\n)" } ], "postcheck": [ { "description": "verify table \"user\" exists", - "sql": "SELECT to_regclass('\"public\".\"user\"') IS NOT NULL" + "sql": "SELECT to_regclass('\"user\"') IS NOT NULL" } ] } diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/end-contract.d.ts b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/end-contract.d.ts new file mode 100644 index 0000000000..1d0b5a21b9 --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/end-contract.d.ts @@ -0,0 +1,217 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma-next contract emit'. +// To regenerate, run: prisma-next contract emit +import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; +import type { + Bit, + Char, + CodecTypes as PgTypes, + Interval, + JsonValue, + Numeric, + Time, + Timestamp, + Timestamptz, + Timetz, + VarBit, + Varchar, +} from '@prisma-next/target-postgres/codec-types'; + +import type { + ContractWithTypeMaps, + TypeMaps as TypeMapsType, +} from '@prisma-next/sql-contract/types'; +import type { + Contract as ContractType, + ContractModelsMap, + ExecutionHashBase, + NamespaceId, + ProfileHashBase, + StorageHashBase, +} from '@prisma-next/contract/types'; + +export type StorageHash = + StorageHashBase<'sha256:93be6c200743261baf55f0586b1380a1c0ade3c48730c09a8fec71ba419c2464'>; +export type ExecutionHash = + ExecutionHashBase<'sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545'>; +export type ProfileHash = + ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; + +export type CodecTypes = PgTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgAdapterQueryOps; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? CodecTypes[CodecId]['output'] + : _Encoded; + +export type FieldOutputTypes = { + readonly user: { + readonly id: Char<36>; + readonly email: CodecTypes['pg/text@1']['output']; + readonly phone: CodecTypes['pg/text@1']['output'] | null; + }; +}; +export type FieldInputTypes = { + readonly user: { + readonly id: CodecTypes['sql/char@1']['input']; + readonly email: CodecTypes['pg/text@1']['input']; + readonly phone: CodecTypes['pg/text@1']['input'] | null; + }; +}; +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes +>; + +type ContractBase = Omit< + ContractType< + { + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'sql-namespace'; + readonly tables: { + readonly user: { + columns: { + readonly id: { + readonly nativeType: 'character'; + readonly codecId: 'sql/char@1'; + readonly nullable: false; + readonly typeParams: { readonly length: 36 }; + }; + readonly email: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly phone: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }, + { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly phone: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly phone: { readonly column: 'phone' }; + }; + }; + }; + } + >, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly user: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'user' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly phone: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly phone: { readonly column: 'phone' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; + readonly sql: { + readonly defaultInInsert: true; + readonly enums: true; + readonly lateral: true; + readonly returning: true; + }; + }; + readonly extensionPacks: {}; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly ref: { readonly table: 'user'; readonly column: 'id' }; + readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; + }, + ]; + }; + }; + readonly meta: {}; + + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; +export type Models = ContractModelsMap; diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/end-contract.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/end-contract.json new file mode 100644 index 0000000000..56e6c8de2b --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/end-contract.json @@ -0,0 +1,142 @@ +{ + "schemaVersion": "1", + "targetFamily": "sql", + "target": "postgres", + "profileHash": "sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb", + "roots": { + "user": { + "model": "user", + "namespace": "__unbound__" + } + }, + "domain": { + "namespaces": { + "__unbound__": { + "models": { + "user": { + "fields": { + "email": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { + "length": 36 + } + } + }, + "phone": { + "nullable": true, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + }, + "relations": {}, + "storage": { + "fields": { + "email": { + "column": "email" + }, + "id": { + "column": "id" + }, + "phone": { + "column": "phone" + } + }, + "table": "user" + } + } + } + } + } + }, + "storage": { + "namespaces": { + "__unbound__": { + "id": "__unbound__", + "kind": "postgres-unbound-schema", + "tables": { + "user": { + "columns": { + "email": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "id": { + "codecId": "sql/char@1", + "nativeType": "character", + "nullable": false, + "typeParams": { + "length": 36 + } + }, + "phone": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": true + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": ["id"] + }, + "uniques": [] + } + } + } + }, + "storageHash": "sha256:93be6c200743261baf55f0586b1380a1c0ade3c48730c09a8fec71ba419c2464" + }, + "execution": { + "executionHash": "sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545", + "mutations": { + "defaults": [ + { + "onCreate": { + "id": "uuidv4", + "kind": "generator" + }, + "ref": { + "column": "id", + "table": "user" + } + } + ] + } + }, + "capabilities": { + "postgres": { + "distinctOn": true, + "jsonAgg": true, + "lateral": true, + "limit": true, + "orderBy": true, + "returning": true + }, + "sql": { + "defaultInInsert": true, + "enums": true, + "lateral": true, + "returning": true + } + }, + "extensionPacks": {}, + "meta": {}, + "_generated": { + "warning": "⚠️ GENERATED FILE - DO NOT EDIT", + "message": "This file is automatically generated by \"prisma-next contract emit\".", + "regenerate": "To regenerate, run: prisma-next contract emit" + } +} diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/migration.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/migration.json index 9bacb18f90..0e1cec1257 100644 --- a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/migration.json +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/migration.json @@ -1,7 +1,7 @@ { - "from": "sha256:ef9de2767997cc2d57d1a4b6b0a4907d54b94793afd5309225803b14be0463bc", - "to": "sha256:73e3abef69c5c5b9566ad1bf0e267d86863556fae8f9fdefdc46002ee3b308a9", + "from": "sha256:789dd79ab5ab725be1b6ced088109b803a4d62f9874f932eb384a868d94360a4", + "to": "sha256:93be6c200743261baf55f0586b1380a1c0ade3c48730c09a8fec71ba419c2464", "providedInvariants": [], - "createdAt": "2026-03-24T15:13:45.632Z", - "migrationHash": "sha256:610322b30573e14adad5315e82309b3dfa4d5f913014bb1b3fa32e97b2da1625" + "createdAt": "2026-06-01T17:08:51.109Z", + "migrationHash": "sha256:dcbe0ac01bb62f46acb736b191d213c9a913089a16ae58c3f201288361c6078f" } diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/migration.ts b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/migration.ts new file mode 100755 index 0000000000..8d69314a95 --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/migration.ts @@ -0,0 +1,24 @@ +#!/usr/bin/env -S node +import { addColumn, Migration, MigrationCLI } from '@prisma-next/postgres/migration'; + +export default class M extends Migration { + override describe() { + return { + from: 'sha256:789dd79ab5ab725be1b6ced088109b803a4d62f9874f932eb384a868d94360a4', + to: 'sha256:93be6c200743261baf55f0586b1380a1c0ade3c48730c09a8fec71ba419c2464', + }; + } + + override get operations() { + return [ + addColumn('__unbound__', 'user', { + name: 'phone', + typeSql: 'text', + defaultSql: '', + nullable: true, + }), + ]; + } +} + +MigrationCLI.run(import.meta.url, M); diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/ops.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/ops.json index 2097127eaf..7701389e2e 100644 --- a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/ops.json +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/ops.json @@ -1,33 +1,33 @@ [ { "id": "column.user.phone", - "label": "Add column phone to user", - "summary": "Adds column phone to table user", + "label": "Add column \"phone\" to \"user\"", "operationClass": "additive", "target": { "id": "postgres", "details": { - "schema": "public", - "objectType": "table", - "name": "user" + "schema": "__unbound__", + "objectType": "column", + "name": "phone", + "table": "user" } }, "precheck": [ { "description": "ensure column \"phone\" is missing", - "sql": "SELECT NOT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = 'public'\n AND table_name = 'user'\n AND column_name = 'phone'\n)" + "sql": "SELECT NOT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = current_schema()\n AND table_name = 'user'\n AND column_name = 'phone'\n)" } ], "execute": [ { "description": "add column \"phone\"", - "sql": "ALTER TABLE \"public\".\"user\" ADD COLUMN \"phone\" text" + "sql": "ALTER TABLE \"user\" ADD COLUMN \"phone\" text" } ], "postcheck": [ { "description": "verify column \"phone\" exists", - "sql": "SELECT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = 'public'\n AND table_name = 'user'\n AND column_name = 'phone'\n)" + "sql": "SELECT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = current_schema()\n AND table_name = 'user'\n AND column_name = 'phone'\n)" } ] } diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/start-contract.d.ts b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/start-contract.d.ts new file mode 100644 index 0000000000..9f1fb581d1 --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/start-contract.d.ts @@ -0,0 +1,197 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma-next contract emit'. +// To regenerate, run: prisma-next contract emit +import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; +import type { + Bit, + Char, + CodecTypes as PgTypes, + Interval, + JsonValue, + Numeric, + Time, + Timestamp, + Timestamptz, + Timetz, + VarBit, + Varchar, +} from '@prisma-next/target-postgres/codec-types'; + +import type { + ContractWithTypeMaps, + TypeMaps as TypeMapsType, +} from '@prisma-next/sql-contract/types'; +import type { + Contract as ContractType, + ContractModelsMap, + ExecutionHashBase, + NamespaceId, + ProfileHashBase, + StorageHashBase, +} from '@prisma-next/contract/types'; + +export type StorageHash = + StorageHashBase<'sha256:789dd79ab5ab725be1b6ced088109b803a4d62f9874f932eb384a868d94360a4'>; +export type ExecutionHash = + ExecutionHashBase<'sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545'>; +export type ProfileHash = + ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; + +export type CodecTypes = PgTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgAdapterQueryOps; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? CodecTypes[CodecId]['output'] + : _Encoded; + +export type FieldOutputTypes = { + readonly user: { readonly id: Char<36>; readonly email: CodecTypes['pg/text@1']['output'] }; +}; +export type FieldInputTypes = { + readonly user: { + readonly id: CodecTypes['sql/char@1']['input']; + readonly email: CodecTypes['pg/text@1']['input']; + }; +}; +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes +>; + +type ContractBase = Omit< + ContractType< + { + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'sql-namespace'; + readonly tables: { + readonly user: { + columns: { + readonly id: { + readonly nativeType: 'character'; + readonly codecId: 'sql/char@1'; + readonly nullable: false; + readonly typeParams: { readonly length: 36 }; + }; + readonly email: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }, + { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + }; + }; + }; + } + >, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly user: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'user' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; + readonly sql: { + readonly defaultInInsert: true; + readonly enums: true; + readonly lateral: true; + readonly returning: true; + }; + }; + readonly extensionPacks: {}; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly ref: { readonly table: 'user'; readonly column: 'id' }; + readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; + }, + ]; + }; + }; + readonly meta: {}; + + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; +export type Models = ContractModelsMap; diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/start-contract.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/start-contract.json new file mode 100644 index 0000000000..56b1859c7f --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1000_alice_add_phone/start-contract.json @@ -0,0 +1,127 @@ +{ + "schemaVersion": "1", + "targetFamily": "sql", + "target": "postgres", + "profileHash": "sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb", + "roots": { + "user": { + "model": "user", + "namespace": "__unbound__" + } + }, + "domain": { + "namespaces": { + "__unbound__": { + "models": { + "user": { + "fields": { + "email": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { + "length": 36 + } + } + } + }, + "relations": {}, + "storage": { + "fields": { + "email": { + "column": "email" + }, + "id": { + "column": "id" + } + }, + "table": "user" + } + } + } + } + } + }, + "storage": { + "namespaces": { + "__unbound__": { + "id": "__unbound__", + "kind": "postgres-unbound-schema", + "tables": { + "user": { + "columns": { + "email": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "id": { + "codecId": "sql/char@1", + "nativeType": "character", + "nullable": false, + "typeParams": { + "length": 36 + } + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": ["id"] + }, + "uniques": [] + } + } + } + }, + "storageHash": "sha256:789dd79ab5ab725be1b6ced088109b803a4d62f9874f932eb384a868d94360a4" + }, + "execution": { + "executionHash": "sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545", + "mutations": { + "defaults": [ + { + "onCreate": { + "id": "uuidv4", + "kind": "generator" + }, + "ref": { + "column": "id", + "table": "user" + } + } + ] + } + }, + "capabilities": { + "postgres": { + "distinctOn": true, + "jsonAgg": true, + "lateral": true, + "limit": true, + "orderBy": true, + "returning": true + }, + "sql": { + "defaultInInsert": true, + "enums": true, + "lateral": true, + "returning": true + } + }, + "extensionPacks": {}, + "meta": {}, + "_generated": { + "warning": "⚠️ GENERATED FILE - DO NOT EDIT", + "message": "This file is automatically generated by \"prisma-next contract emit\".", + "regenerate": "To regenerate, run: prisma-next contract emit" + } +} diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/end-contract.d.ts b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/end-contract.d.ts new file mode 100644 index 0000000000..c2fef632f6 --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/end-contract.d.ts @@ -0,0 +1,217 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma-next contract emit'. +// To regenerate, run: prisma-next contract emit +import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; +import type { + Bit, + Char, + CodecTypes as PgTypes, + Interval, + JsonValue, + Numeric, + Time, + Timestamp, + Timestamptz, + Timetz, + VarBit, + Varchar, +} from '@prisma-next/target-postgres/codec-types'; + +import type { + ContractWithTypeMaps, + TypeMaps as TypeMapsType, +} from '@prisma-next/sql-contract/types'; +import type { + Contract as ContractType, + ContractModelsMap, + ExecutionHashBase, + NamespaceId, + ProfileHashBase, + StorageHashBase, +} from '@prisma-next/contract/types'; + +export type StorageHash = + StorageHashBase<'sha256:7e3fa7fbe98974385451444c229337c87ec90c547a9214f81700f86b5af3563d'>; +export type ExecutionHash = + ExecutionHashBase<'sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545'>; +export type ProfileHash = + ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; + +export type CodecTypes = PgTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgAdapterQueryOps; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? CodecTypes[CodecId]['output'] + : _Encoded; + +export type FieldOutputTypes = { + readonly user: { + readonly id: Char<36>; + readonly email: CodecTypes['pg/text@1']['output']; + readonly avatar: CodecTypes['pg/text@1']['output'] | null; + }; +}; +export type FieldInputTypes = { + readonly user: { + readonly id: CodecTypes['sql/char@1']['input']; + readonly email: CodecTypes['pg/text@1']['input']; + readonly avatar: CodecTypes['pg/text@1']['input'] | null; + }; +}; +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes +>; + +type ContractBase = Omit< + ContractType< + { + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'sql-namespace'; + readonly tables: { + readonly user: { + columns: { + readonly id: { + readonly nativeType: 'character'; + readonly codecId: 'sql/char@1'; + readonly nullable: false; + readonly typeParams: { readonly length: 36 }; + }; + readonly email: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly avatar: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }, + { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly avatar: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly avatar: { readonly column: 'avatar' }; + }; + }; + }; + } + >, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly user: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'user' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly avatar: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly avatar: { readonly column: 'avatar' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; + readonly sql: { + readonly defaultInInsert: true; + readonly enums: true; + readonly lateral: true; + readonly returning: true; + }; + }; + readonly extensionPacks: {}; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly ref: { readonly table: 'user'; readonly column: 'id' }; + readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; + }, + ]; + }; + }; + readonly meta: {}; + + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; +export type Models = ContractModelsMap; diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/end-contract.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/end-contract.json new file mode 100644 index 0000000000..1e44db94bc --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/end-contract.json @@ -0,0 +1,142 @@ +{ + "schemaVersion": "1", + "targetFamily": "sql", + "target": "postgres", + "profileHash": "sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb", + "roots": { + "user": { + "model": "user", + "namespace": "__unbound__" + } + }, + "domain": { + "namespaces": { + "__unbound__": { + "models": { + "user": { + "fields": { + "avatar": { + "nullable": true, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "email": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { + "length": 36 + } + } + } + }, + "relations": {}, + "storage": { + "fields": { + "avatar": { + "column": "avatar" + }, + "email": { + "column": "email" + }, + "id": { + "column": "id" + } + }, + "table": "user" + } + } + } + } + } + }, + "storage": { + "namespaces": { + "__unbound__": { + "id": "__unbound__", + "kind": "postgres-unbound-schema", + "tables": { + "user": { + "columns": { + "avatar": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": true + }, + "email": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "id": { + "codecId": "sql/char@1", + "nativeType": "character", + "nullable": false, + "typeParams": { + "length": 36 + } + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": ["id"] + }, + "uniques": [] + } + } + } + }, + "storageHash": "sha256:7e3fa7fbe98974385451444c229337c87ec90c547a9214f81700f86b5af3563d" + }, + "execution": { + "executionHash": "sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545", + "mutations": { + "defaults": [ + { + "onCreate": { + "id": "uuidv4", + "kind": "generator" + }, + "ref": { + "column": "id", + "table": "user" + } + } + ] + } + }, + "capabilities": { + "postgres": { + "distinctOn": true, + "jsonAgg": true, + "lateral": true, + "limit": true, + "orderBy": true, + "returning": true + }, + "sql": { + "defaultInInsert": true, + "enums": true, + "lateral": true, + "returning": true + } + }, + "extensionPacks": {}, + "meta": {}, + "_generated": { + "warning": "⚠️ GENERATED FILE - DO NOT EDIT", + "message": "This file is automatically generated by \"prisma-next contract emit\".", + "regenerate": "To regenerate, run: prisma-next contract emit" + } +} diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/migration.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/migration.json index eff0fc7d65..c66a344f86 100644 --- a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/migration.json +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/migration.json @@ -1,7 +1,7 @@ { - "from": "sha256:ef9de2767997cc2d57d1a4b6b0a4907d54b94793afd5309225803b14be0463bc", - "to": "sha256:6656a6e7b5665395337bd335dbd4d03fcacbb3e93f9f57180a64068e65d847f7", + "from": "sha256:789dd79ab5ab725be1b6ced088109b803a4d62f9874f932eb384a868d94360a4", + "to": "sha256:7e3fa7fbe98974385451444c229337c87ec90c547a9214f81700f86b5af3563d", "providedInvariants": [], - "createdAt": "2026-03-24T15:13:45.633Z", - "migrationHash": "sha256:e77ee7b0cddf094a0264d6c4086ebb622521dc422d1671bdca8c8692a92bbe88" + "createdAt": "2026-06-01T17:08:51.779Z", + "migrationHash": "sha256:6b609733306d3bb702cd97f6ee989348bd2a995913d02d15bc315eb8acda81ae" } diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/migration.ts b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/migration.ts new file mode 100755 index 0000000000..800d340da7 --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/migration.ts @@ -0,0 +1,24 @@ +#!/usr/bin/env -S node +import { addColumn, Migration, MigrationCLI } from '@prisma-next/postgres/migration'; + +export default class M extends Migration { + override describe() { + return { + from: 'sha256:789dd79ab5ab725be1b6ced088109b803a4d62f9874f932eb384a868d94360a4', + to: 'sha256:7e3fa7fbe98974385451444c229337c87ec90c547a9214f81700f86b5af3563d', + }; + } + + override get operations() { + return [ + addColumn('__unbound__', 'user', { + name: 'avatar', + typeSql: 'text', + defaultSql: '', + nullable: true, + }), + ]; + } +} + +MigrationCLI.run(import.meta.url, M); diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/ops.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/ops.json index 4c972d729a..47219818cf 100644 --- a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/ops.json +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/ops.json @@ -1,33 +1,33 @@ [ { - "id": "column.user.avatarUrl", - "label": "Add column avatarUrl to user", - "summary": "Adds column avatarUrl to table user", + "id": "column.user.avatar", + "label": "Add column \"avatar\" to \"user\"", "operationClass": "additive", "target": { "id": "postgres", "details": { - "schema": "public", - "objectType": "table", - "name": "user" + "schema": "__unbound__", + "objectType": "column", + "name": "avatar", + "table": "user" } }, "precheck": [ { - "description": "ensure column \"avatarUrl\" is missing", - "sql": "SELECT NOT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = 'public'\n AND table_name = 'user'\n AND column_name = 'avatarUrl'\n)" + "description": "ensure column \"avatar\" is missing", + "sql": "SELECT NOT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = current_schema()\n AND table_name = 'user'\n AND column_name = 'avatar'\n)" } ], "execute": [ { - "description": "add column \"avatarUrl\"", - "sql": "ALTER TABLE \"public\".\"user\" ADD COLUMN \"avatarUrl\" text" + "description": "add column \"avatar\"", + "sql": "ALTER TABLE \"user\" ADD COLUMN \"avatar\" text" } ], "postcheck": [ { - "description": "verify column \"avatarUrl\" exists", - "sql": "SELECT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = 'public'\n AND table_name = 'user'\n AND column_name = 'avatarUrl'\n)" + "description": "verify column \"avatar\" exists", + "sql": "SELECT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = current_schema()\n AND table_name = 'user'\n AND column_name = 'avatar'\n)" } ] } diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/start-contract.d.ts b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/start-contract.d.ts new file mode 100644 index 0000000000..9f1fb581d1 --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/start-contract.d.ts @@ -0,0 +1,197 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma-next contract emit'. +// To regenerate, run: prisma-next contract emit +import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; +import type { + Bit, + Char, + CodecTypes as PgTypes, + Interval, + JsonValue, + Numeric, + Time, + Timestamp, + Timestamptz, + Timetz, + VarBit, + Varchar, +} from '@prisma-next/target-postgres/codec-types'; + +import type { + ContractWithTypeMaps, + TypeMaps as TypeMapsType, +} from '@prisma-next/sql-contract/types'; +import type { + Contract as ContractType, + ContractModelsMap, + ExecutionHashBase, + NamespaceId, + ProfileHashBase, + StorageHashBase, +} from '@prisma-next/contract/types'; + +export type StorageHash = + StorageHashBase<'sha256:789dd79ab5ab725be1b6ced088109b803a4d62f9874f932eb384a868d94360a4'>; +export type ExecutionHash = + ExecutionHashBase<'sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545'>; +export type ProfileHash = + ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; + +export type CodecTypes = PgTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgAdapterQueryOps; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? CodecTypes[CodecId]['output'] + : _Encoded; + +export type FieldOutputTypes = { + readonly user: { readonly id: Char<36>; readonly email: CodecTypes['pg/text@1']['output'] }; +}; +export type FieldInputTypes = { + readonly user: { + readonly id: CodecTypes['sql/char@1']['input']; + readonly email: CodecTypes['pg/text@1']['input']; + }; +}; +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes +>; + +type ContractBase = Omit< + ContractType< + { + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'sql-namespace'; + readonly tables: { + readonly user: { + columns: { + readonly id: { + readonly nativeType: 'character'; + readonly codecId: 'sql/char@1'; + readonly nullable: false; + readonly typeParams: { readonly length: 36 }; + }; + readonly email: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }, + { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + }; + }; + }; + } + >, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly user: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'user' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; + readonly sql: { + readonly defaultInInsert: true; + readonly enums: true; + readonly lateral: true; + readonly returning: true; + }; + }; + readonly extensionPacks: {}; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly ref: { readonly table: 'user'; readonly column: 'id' }; + readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; + }, + ]; + }; + }; + readonly meta: {}; + + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; +export type Models = ContractModelsMap; diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/start-contract.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/start-contract.json new file mode 100644 index 0000000000..56b1859c7f --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260302T1100_bob_add_avatar/start-contract.json @@ -0,0 +1,127 @@ +{ + "schemaVersion": "1", + "targetFamily": "sql", + "target": "postgres", + "profileHash": "sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb", + "roots": { + "user": { + "model": "user", + "namespace": "__unbound__" + } + }, + "domain": { + "namespaces": { + "__unbound__": { + "models": { + "user": { + "fields": { + "email": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { + "length": 36 + } + } + } + }, + "relations": {}, + "storage": { + "fields": { + "email": { + "column": "email" + }, + "id": { + "column": "id" + } + }, + "table": "user" + } + } + } + } + } + }, + "storage": { + "namespaces": { + "__unbound__": { + "id": "__unbound__", + "kind": "postgres-unbound-schema", + "tables": { + "user": { + "columns": { + "email": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "id": { + "codecId": "sql/char@1", + "nativeType": "character", + "nullable": false, + "typeParams": { + "length": 36 + } + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": ["id"] + }, + "uniques": [] + } + } + } + }, + "storageHash": "sha256:789dd79ab5ab725be1b6ced088109b803a4d62f9874f932eb384a868d94360a4" + }, + "execution": { + "executionHash": "sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545", + "mutations": { + "defaults": [ + { + "onCreate": { + "id": "uuidv4", + "kind": "generator" + }, + "ref": { + "column": "id", + "table": "user" + } + } + ] + } + }, + "capabilities": { + "postgres": { + "distinctOn": true, + "jsonAgg": true, + "lateral": true, + "limit": true, + "orderBy": true, + "returning": true + }, + "sql": { + "defaultInInsert": true, + "enums": true, + "lateral": true, + "returning": true + } + }, + "extensionPacks": {}, + "meta": {}, + "_generated": { + "warning": "⚠️ GENERATED FILE - DO NOT EDIT", + "message": "This file is automatically generated by \"prisma-next contract emit\".", + "regenerate": "To regenerate, run: prisma-next contract emit" + } +} diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/end-contract.d.ts b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/end-contract.d.ts new file mode 100644 index 0000000000..9dc3794b7c --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/end-contract.d.ts @@ -0,0 +1,234 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma-next contract emit'. +// To regenerate, run: prisma-next contract emit +import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; +import type { + Bit, + Char, + CodecTypes as PgTypes, + Interval, + JsonValue, + Numeric, + Time, + Timestamp, + Timestamptz, + Timetz, + VarBit, + Varchar, +} from '@prisma-next/target-postgres/codec-types'; + +import type { + ContractWithTypeMaps, + TypeMaps as TypeMapsType, +} from '@prisma-next/sql-contract/types'; +import type { + Contract as ContractType, + ContractModelsMap, + ExecutionHashBase, + NamespaceId, + ProfileHashBase, + StorageHashBase, +} from '@prisma-next/contract/types'; + +export type StorageHash = + StorageHashBase<'sha256:f9a41d77df6eae57bcd25ab25df31e6e905aad034a5b813f408bf8e78e9f384a'>; +export type ExecutionHash = + ExecutionHashBase<'sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545'>; +export type ProfileHash = + ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; + +export type CodecTypes = PgTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgAdapterQueryOps; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? CodecTypes[CodecId]['output'] + : _Encoded; + +export type FieldOutputTypes = { + readonly user: { + readonly id: Char<36>; + readonly email: CodecTypes['pg/text@1']['output']; + readonly phone: CodecTypes['pg/text@1']['output'] | null; + readonly avatar: CodecTypes['pg/text@1']['output'] | null; + }; +}; +export type FieldInputTypes = { + readonly user: { + readonly id: CodecTypes['sql/char@1']['input']; + readonly email: CodecTypes['pg/text@1']['input']; + readonly phone: CodecTypes['pg/text@1']['input'] | null; + readonly avatar: CodecTypes['pg/text@1']['input'] | null; + }; +}; +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes +>; + +type ContractBase = Omit< + ContractType< + { + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'sql-namespace'; + readonly tables: { + readonly user: { + columns: { + readonly id: { + readonly nativeType: 'character'; + readonly codecId: 'sql/char@1'; + readonly nullable: false; + readonly typeParams: { readonly length: 36 }; + }; + readonly email: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly phone: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + }; + readonly avatar: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }, + { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly phone: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly avatar: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly phone: { readonly column: 'phone' }; + readonly avatar: { readonly column: 'avatar' }; + }; + }; + }; + } + >, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly user: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'user' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly phone: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly avatar: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly phone: { readonly column: 'phone' }; + readonly avatar: { readonly column: 'avatar' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; + readonly sql: { + readonly defaultInInsert: true; + readonly enums: true; + readonly lateral: true; + readonly returning: true; + }; + }; + readonly extensionPacks: {}; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly ref: { readonly table: 'user'; readonly column: 'id' }; + readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; + }, + ]; + }; + }; + readonly meta: {}; + + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; +export type Models = ContractModelsMap; diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/end-contract.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/end-contract.json new file mode 100644 index 0000000000..49fdedcde0 --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/end-contract.json @@ -0,0 +1,157 @@ +{ + "schemaVersion": "1", + "targetFamily": "sql", + "target": "postgres", + "profileHash": "sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb", + "roots": { + "user": { + "model": "user", + "namespace": "__unbound__" + } + }, + "domain": { + "namespaces": { + "__unbound__": { + "models": { + "user": { + "fields": { + "avatar": { + "nullable": true, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "email": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { + "length": 36 + } + } + }, + "phone": { + "nullable": true, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + }, + "relations": {}, + "storage": { + "fields": { + "avatar": { + "column": "avatar" + }, + "email": { + "column": "email" + }, + "id": { + "column": "id" + }, + "phone": { + "column": "phone" + } + }, + "table": "user" + } + } + } + } + } + }, + "storage": { + "namespaces": { + "__unbound__": { + "id": "__unbound__", + "kind": "postgres-unbound-schema", + "tables": { + "user": { + "columns": { + "avatar": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": true + }, + "email": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "id": { + "codecId": "sql/char@1", + "nativeType": "character", + "nullable": false, + "typeParams": { + "length": 36 + } + }, + "phone": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": true + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": ["id"] + }, + "uniques": [] + } + } + } + }, + "storageHash": "sha256:f9a41d77df6eae57bcd25ab25df31e6e905aad034a5b813f408bf8e78e9f384a" + }, + "execution": { + "executionHash": "sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545", + "mutations": { + "defaults": [ + { + "onCreate": { + "id": "uuidv4", + "kind": "generator" + }, + "ref": { + "column": "id", + "table": "user" + } + } + ] + } + }, + "capabilities": { + "postgres": { + "distinctOn": true, + "jsonAgg": true, + "lateral": true, + "limit": true, + "orderBy": true, + "returning": true + }, + "sql": { + "defaultInInsert": true, + "enums": true, + "lateral": true, + "returning": true + } + }, + "extensionPacks": {}, + "meta": {}, + "_generated": { + "warning": "⚠️ GENERATED FILE - DO NOT EDIT", + "message": "This file is automatically generated by \"prisma-next contract emit\".", + "regenerate": "To regenerate, run: prisma-next contract emit" + } +} diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/migration.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/migration.json index 13f2fcb93d..61083b1fc2 100644 --- a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/migration.json +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/migration.json @@ -1,7 +1,7 @@ { - "from": "sha256:73e3abef69c5c5b9566ad1bf0e267d86863556fae8f9fdefdc46002ee3b308a9", - "to": "sha256:3b2d98ddf921fb8bcc05553d3349f85dec8e78c0ced66250cd95e24d95286a04", + "from": "sha256:93be6c200743261baf55f0586b1380a1c0ade3c48730c09a8fec71ba419c2464", + "to": "sha256:f9a41d77df6eae57bcd25ab25df31e6e905aad034a5b813f408bf8e78e9f384a", "providedInvariants": [], - "createdAt": "2026-03-24T15:13:45.633Z", - "migrationHash": "sha256:3bba7f22507680adbd164e55458955b9c98a7daeb3530e16aee8d16ce655e755" + "createdAt": "2026-06-01T17:08:52.439Z", + "migrationHash": "sha256:8db9c876a4c89a04634f133a34ba3921f90a668da356236cea035ce777db7760" } diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/migration.ts b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/migration.ts new file mode 100755 index 0000000000..eeb73fdc53 --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/migration.ts @@ -0,0 +1,24 @@ +#!/usr/bin/env -S node +import { addColumn, Migration, MigrationCLI } from '@prisma-next/postgres/migration'; + +export default class M extends Migration { + override describe() { + return { + from: 'sha256:93be6c200743261baf55f0586b1380a1c0ade3c48730c09a8fec71ba419c2464', + to: 'sha256:f9a41d77df6eae57bcd25ab25df31e6e905aad034a5b813f408bf8e78e9f384a', + }; + } + + override get operations() { + return [ + addColumn('__unbound__', 'user', { + name: 'avatar', + typeSql: 'text', + defaultSql: '', + nullable: true, + }), + ]; + } +} + +MigrationCLI.run(import.meta.url, M); diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/ops.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/ops.json index 06ba0980d5..47219818cf 100644 --- a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/ops.json +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/ops.json @@ -1,97 +1,33 @@ [ { - "id": "table.post", - "label": "Create table post", - "summary": "Creates table post with required columns", + "id": "column.user.avatar", + "label": "Add column \"avatar\" to \"user\"", "operationClass": "additive", "target": { "id": "postgres", "details": { - "schema": "public", - "objectType": "table", - "name": "post" + "schema": "__unbound__", + "objectType": "column", + "name": "avatar", + "table": "user" } }, "precheck": [ { - "description": "ensure table \"post\" does not exist", - "sql": "SELECT to_regclass('\"public\".\"post\"') IS NULL" + "description": "ensure column \"avatar\" is missing", + "sql": "SELECT NOT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = current_schema()\n AND table_name = 'user'\n AND column_name = 'avatar'\n)" } ], "execute": [ { - "description": "create table \"post\"", - "sql": "CREATE TABLE \"public\".\"post\" (\n \"id\" uuid NOT NULL,\n \"title\" text NOT NULL,\n \"userId\" uuid NOT NULL,\n \"createdAt\" timestamptz DEFAULT (now()) NOT NULL,\n PRIMARY KEY (\"id\")\n)" + "description": "add column \"avatar\"", + "sql": "ALTER TABLE \"user\" ADD COLUMN \"avatar\" text" } ], "postcheck": [ { - "description": "verify table \"post\" exists", - "sql": "SELECT to_regclass('\"public\".\"post\"') IS NOT NULL" - } - ] - }, - { - "id": "column.user.avatarUrl", - "label": "Add column avatarUrl to user", - "summary": "Adds column avatarUrl to table user", - "operationClass": "additive", - "target": { - "id": "postgres", - "details": { - "schema": "public", - "objectType": "table", - "name": "user" - } - }, - "precheck": [ - { - "description": "ensure column \"avatarUrl\" is missing", - "sql": "SELECT NOT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = 'public'\n AND table_name = 'user'\n AND column_name = 'avatarUrl'\n)" - } - ], - "execute": [ - { - "description": "add column \"avatarUrl\"", - "sql": "ALTER TABLE \"public\".\"user\" ADD COLUMN \"avatarUrl\" text" - } - ], - "postcheck": [ - { - "description": "verify column \"avatarUrl\" exists", - "sql": "SELECT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = 'public'\n AND table_name = 'user'\n AND column_name = 'avatarUrl'\n)" - } - ] - }, - { - "id": "column.user.bio", - "label": "Add column bio to user", - "summary": "Adds column bio to table user", - "operationClass": "additive", - "target": { - "id": "postgres", - "details": { - "schema": "public", - "objectType": "table", - "name": "user" - } - }, - "precheck": [ - { - "description": "ensure column \"bio\" is missing", - "sql": "SELECT NOT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = 'public'\n AND table_name = 'user'\n AND column_name = 'bio'\n)" - } - ], - "execute": [ - { - "description": "add column \"bio\"", - "sql": "ALTER TABLE \"public\".\"user\" ADD COLUMN \"bio\" text" - } - ], - "postcheck": [ - { - "description": "verify column \"bio\" exists", - "sql": "SELECT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = 'public'\n AND table_name = 'user'\n AND column_name = 'bio'\n)" + "description": "verify column \"avatar\" exists", + "sql": "SELECT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = current_schema()\n AND table_name = 'user'\n AND column_name = 'avatar'\n)" } ] } diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/start-contract.d.ts b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/start-contract.d.ts new file mode 100644 index 0000000000..1d0b5a21b9 --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/start-contract.d.ts @@ -0,0 +1,217 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma-next contract emit'. +// To regenerate, run: prisma-next contract emit +import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; +import type { + Bit, + Char, + CodecTypes as PgTypes, + Interval, + JsonValue, + Numeric, + Time, + Timestamp, + Timestamptz, + Timetz, + VarBit, + Varchar, +} from '@prisma-next/target-postgres/codec-types'; + +import type { + ContractWithTypeMaps, + TypeMaps as TypeMapsType, +} from '@prisma-next/sql-contract/types'; +import type { + Contract as ContractType, + ContractModelsMap, + ExecutionHashBase, + NamespaceId, + ProfileHashBase, + StorageHashBase, +} from '@prisma-next/contract/types'; + +export type StorageHash = + StorageHashBase<'sha256:93be6c200743261baf55f0586b1380a1c0ade3c48730c09a8fec71ba419c2464'>; +export type ExecutionHash = + ExecutionHashBase<'sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545'>; +export type ProfileHash = + ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; + +export type CodecTypes = PgTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgAdapterQueryOps; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? CodecTypes[CodecId]['output'] + : _Encoded; + +export type FieldOutputTypes = { + readonly user: { + readonly id: Char<36>; + readonly email: CodecTypes['pg/text@1']['output']; + readonly phone: CodecTypes['pg/text@1']['output'] | null; + }; +}; +export type FieldInputTypes = { + readonly user: { + readonly id: CodecTypes['sql/char@1']['input']; + readonly email: CodecTypes['pg/text@1']['input']; + readonly phone: CodecTypes['pg/text@1']['input'] | null; + }; +}; +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes +>; + +type ContractBase = Omit< + ContractType< + { + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'sql-namespace'; + readonly tables: { + readonly user: { + columns: { + readonly id: { + readonly nativeType: 'character'; + readonly codecId: 'sql/char@1'; + readonly nullable: false; + readonly typeParams: { readonly length: 36 }; + }; + readonly email: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly phone: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }, + { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly phone: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly phone: { readonly column: 'phone' }; + }; + }; + }; + } + >, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly user: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'user' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly phone: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly phone: { readonly column: 'phone' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; + readonly sql: { + readonly defaultInInsert: true; + readonly enums: true; + readonly lateral: true; + readonly returning: true; + }; + }; + readonly extensionPacks: {}; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly ref: { readonly table: 'user'; readonly column: 'id' }; + readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; + }, + ]; + }; + }; + readonly meta: {}; + + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; +export type Models = ContractModelsMap; diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/start-contract.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/start-contract.json new file mode 100644 index 0000000000..56e6c8de2b --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1000_merge_alice/start-contract.json @@ -0,0 +1,142 @@ +{ + "schemaVersion": "1", + "targetFamily": "sql", + "target": "postgres", + "profileHash": "sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb", + "roots": { + "user": { + "model": "user", + "namespace": "__unbound__" + } + }, + "domain": { + "namespaces": { + "__unbound__": { + "models": { + "user": { + "fields": { + "email": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { + "length": 36 + } + } + }, + "phone": { + "nullable": true, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + }, + "relations": {}, + "storage": { + "fields": { + "email": { + "column": "email" + }, + "id": { + "column": "id" + }, + "phone": { + "column": "phone" + } + }, + "table": "user" + } + } + } + } + } + }, + "storage": { + "namespaces": { + "__unbound__": { + "id": "__unbound__", + "kind": "postgres-unbound-schema", + "tables": { + "user": { + "columns": { + "email": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "id": { + "codecId": "sql/char@1", + "nativeType": "character", + "nullable": false, + "typeParams": { + "length": 36 + } + }, + "phone": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": true + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": ["id"] + }, + "uniques": [] + } + } + } + }, + "storageHash": "sha256:93be6c200743261baf55f0586b1380a1c0ade3c48730c09a8fec71ba419c2464" + }, + "execution": { + "executionHash": "sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545", + "mutations": { + "defaults": [ + { + "onCreate": { + "id": "uuidv4", + "kind": "generator" + }, + "ref": { + "column": "id", + "table": "user" + } + } + ] + } + }, + "capabilities": { + "postgres": { + "distinctOn": true, + "jsonAgg": true, + "lateral": true, + "limit": true, + "orderBy": true, + "returning": true + }, + "sql": { + "defaultInInsert": true, + "enums": true, + "lateral": true, + "returning": true + } + }, + "extensionPacks": {}, + "meta": {}, + "_generated": { + "warning": "⚠️ GENERATED FILE - DO NOT EDIT", + "message": "This file is automatically generated by \"prisma-next contract emit\".", + "regenerate": "To regenerate, run: prisma-next contract emit" + } +} diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/end-contract.d.ts b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/end-contract.d.ts new file mode 100644 index 0000000000..9dc3794b7c --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/end-contract.d.ts @@ -0,0 +1,234 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma-next contract emit'. +// To regenerate, run: prisma-next contract emit +import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; +import type { + Bit, + Char, + CodecTypes as PgTypes, + Interval, + JsonValue, + Numeric, + Time, + Timestamp, + Timestamptz, + Timetz, + VarBit, + Varchar, +} from '@prisma-next/target-postgres/codec-types'; + +import type { + ContractWithTypeMaps, + TypeMaps as TypeMapsType, +} from '@prisma-next/sql-contract/types'; +import type { + Contract as ContractType, + ContractModelsMap, + ExecutionHashBase, + NamespaceId, + ProfileHashBase, + StorageHashBase, +} from '@prisma-next/contract/types'; + +export type StorageHash = + StorageHashBase<'sha256:f9a41d77df6eae57bcd25ab25df31e6e905aad034a5b813f408bf8e78e9f384a'>; +export type ExecutionHash = + ExecutionHashBase<'sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545'>; +export type ProfileHash = + ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; + +export type CodecTypes = PgTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgAdapterQueryOps; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? CodecTypes[CodecId]['output'] + : _Encoded; + +export type FieldOutputTypes = { + readonly user: { + readonly id: Char<36>; + readonly email: CodecTypes['pg/text@1']['output']; + readonly phone: CodecTypes['pg/text@1']['output'] | null; + readonly avatar: CodecTypes['pg/text@1']['output'] | null; + }; +}; +export type FieldInputTypes = { + readonly user: { + readonly id: CodecTypes['sql/char@1']['input']; + readonly email: CodecTypes['pg/text@1']['input']; + readonly phone: CodecTypes['pg/text@1']['input'] | null; + readonly avatar: CodecTypes['pg/text@1']['input'] | null; + }; +}; +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes +>; + +type ContractBase = Omit< + ContractType< + { + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'sql-namespace'; + readonly tables: { + readonly user: { + columns: { + readonly id: { + readonly nativeType: 'character'; + readonly codecId: 'sql/char@1'; + readonly nullable: false; + readonly typeParams: { readonly length: 36 }; + }; + readonly email: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly phone: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + }; + readonly avatar: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }, + { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly phone: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly avatar: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly phone: { readonly column: 'phone' }; + readonly avatar: { readonly column: 'avatar' }; + }; + }; + }; + } + >, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly user: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'user' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly phone: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly avatar: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly phone: { readonly column: 'phone' }; + readonly avatar: { readonly column: 'avatar' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; + readonly sql: { + readonly defaultInInsert: true; + readonly enums: true; + readonly lateral: true; + readonly returning: true; + }; + }; + readonly extensionPacks: {}; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly ref: { readonly table: 'user'; readonly column: 'id' }; + readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; + }, + ]; + }; + }; + readonly meta: {}; + + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; +export type Models = ContractModelsMap; diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/end-contract.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/end-contract.json new file mode 100644 index 0000000000..49fdedcde0 --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/end-contract.json @@ -0,0 +1,157 @@ +{ + "schemaVersion": "1", + "targetFamily": "sql", + "target": "postgres", + "profileHash": "sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb", + "roots": { + "user": { + "model": "user", + "namespace": "__unbound__" + } + }, + "domain": { + "namespaces": { + "__unbound__": { + "models": { + "user": { + "fields": { + "avatar": { + "nullable": true, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "email": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { + "length": 36 + } + } + }, + "phone": { + "nullable": true, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + }, + "relations": {}, + "storage": { + "fields": { + "avatar": { + "column": "avatar" + }, + "email": { + "column": "email" + }, + "id": { + "column": "id" + }, + "phone": { + "column": "phone" + } + }, + "table": "user" + } + } + } + } + } + }, + "storage": { + "namespaces": { + "__unbound__": { + "id": "__unbound__", + "kind": "postgres-unbound-schema", + "tables": { + "user": { + "columns": { + "avatar": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": true + }, + "email": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "id": { + "codecId": "sql/char@1", + "nativeType": "character", + "nullable": false, + "typeParams": { + "length": 36 + } + }, + "phone": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": true + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": ["id"] + }, + "uniques": [] + } + } + } + }, + "storageHash": "sha256:f9a41d77df6eae57bcd25ab25df31e6e905aad034a5b813f408bf8e78e9f384a" + }, + "execution": { + "executionHash": "sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545", + "mutations": { + "defaults": [ + { + "onCreate": { + "id": "uuidv4", + "kind": "generator" + }, + "ref": { + "column": "id", + "table": "user" + } + } + ] + } + }, + "capabilities": { + "postgres": { + "distinctOn": true, + "jsonAgg": true, + "lateral": true, + "limit": true, + "orderBy": true, + "returning": true + }, + "sql": { + "defaultInInsert": true, + "enums": true, + "lateral": true, + "returning": true + } + }, + "extensionPacks": {}, + "meta": {}, + "_generated": { + "warning": "⚠️ GENERATED FILE - DO NOT EDIT", + "message": "This file is automatically generated by \"prisma-next contract emit\".", + "regenerate": "To regenerate, run: prisma-next contract emit" + } +} diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/migration.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/migration.json index 39dbd9d46c..6bb08e6f79 100644 --- a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/migration.json +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/migration.json @@ -1,7 +1,7 @@ { - "from": "sha256:6656a6e7b5665395337bd335dbd4d03fcacbb3e93f9f57180a64068e65d847f7", - "to": "sha256:3b2d98ddf921fb8bcc05553d3349f85dec8e78c0ced66250cd95e24d95286a04", + "from": "sha256:7e3fa7fbe98974385451444c229337c87ec90c547a9214f81700f86b5af3563d", + "to": "sha256:f9a41d77df6eae57bcd25ab25df31e6e905aad034a5b813f408bf8e78e9f384a", "providedInvariants": [], - "createdAt": "2026-03-24T15:13:45.633Z", - "migrationHash": "sha256:08d3817ae294a5ad8341628372f1c4e3000648b161aa2ece686781ef2499d97e" + "createdAt": "2026-06-01T17:08:53.056Z", + "migrationHash": "sha256:573189e2bb11787d3c44ad45aabee862117a987fbd7c70e3580a66fc2b04a4c4" } diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/migration.ts b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/migration.ts new file mode 100755 index 0000000000..85df7f9115 --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/migration.ts @@ -0,0 +1,24 @@ +#!/usr/bin/env -S node +import { addColumn, Migration, MigrationCLI } from '@prisma-next/postgres/migration'; + +export default class M extends Migration { + override describe() { + return { + from: 'sha256:7e3fa7fbe98974385451444c229337c87ec90c547a9214f81700f86b5af3563d', + to: 'sha256:f9a41d77df6eae57bcd25ab25df31e6e905aad034a5b813f408bf8e78e9f384a', + }; + } + + override get operations() { + return [ + addColumn('__unbound__', 'user', { + name: 'phone', + typeSql: 'text', + defaultSql: '', + nullable: true, + }), + ]; + } +} + +MigrationCLI.run(import.meta.url, M); diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/ops.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/ops.json index f1d780df5e..7701389e2e 100644 --- a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/ops.json +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/ops.json @@ -1,97 +1,33 @@ [ - { - "id": "table.post", - "label": "Create table post", - "summary": "Creates table post with required columns", - "operationClass": "additive", - "target": { - "id": "postgres", - "details": { - "schema": "public", - "objectType": "table", - "name": "post" - } - }, - "precheck": [ - { - "description": "ensure table \"post\" does not exist", - "sql": "SELECT to_regclass('\"public\".\"post\"') IS NULL" - } - ], - "execute": [ - { - "description": "create table \"post\"", - "sql": "CREATE TABLE \"public\".\"post\" (\n \"id\" uuid NOT NULL,\n \"title\" text NOT NULL,\n \"userId\" uuid NOT NULL,\n \"createdAt\" timestamptz DEFAULT (now()) NOT NULL,\n PRIMARY KEY (\"id\")\n)" - } - ], - "postcheck": [ - { - "description": "verify table \"post\" exists", - "sql": "SELECT to_regclass('\"public\".\"post\"') IS NOT NULL" - } - ] - }, - { - "id": "column.user.bio", - "label": "Add column bio to user", - "summary": "Adds column bio to table user", - "operationClass": "additive", - "target": { - "id": "postgres", - "details": { - "schema": "public", - "objectType": "table", - "name": "user" - } - }, - "precheck": [ - { - "description": "ensure column \"bio\" is missing", - "sql": "SELECT NOT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = 'public'\n AND table_name = 'user'\n AND column_name = 'bio'\n)" - } - ], - "execute": [ - { - "description": "add column \"bio\"", - "sql": "ALTER TABLE \"public\".\"user\" ADD COLUMN \"bio\" text" - } - ], - "postcheck": [ - { - "description": "verify column \"bio\" exists", - "sql": "SELECT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = 'public'\n AND table_name = 'user'\n AND column_name = 'bio'\n)" - } - ] - }, { "id": "column.user.phone", - "label": "Add column phone to user", - "summary": "Adds column phone to table user", + "label": "Add column \"phone\" to \"user\"", "operationClass": "additive", "target": { "id": "postgres", "details": { - "schema": "public", - "objectType": "table", - "name": "user" + "schema": "__unbound__", + "objectType": "column", + "name": "phone", + "table": "user" } }, "precheck": [ { "description": "ensure column \"phone\" is missing", - "sql": "SELECT NOT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = 'public'\n AND table_name = 'user'\n AND column_name = 'phone'\n)" + "sql": "SELECT NOT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = current_schema()\n AND table_name = 'user'\n AND column_name = 'phone'\n)" } ], "execute": [ { "description": "add column \"phone\"", - "sql": "ALTER TABLE \"public\".\"user\" ADD COLUMN \"phone\" text" + "sql": "ALTER TABLE \"user\" ADD COLUMN \"phone\" text" } ], "postcheck": [ { "description": "verify column \"phone\" exists", - "sql": "SELECT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = 'public'\n AND table_name = 'user'\n AND column_name = 'phone'\n)" + "sql": "SELECT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_schema = current_schema()\n AND table_name = 'user'\n AND column_name = 'phone'\n)" } ] } diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/start-contract.d.ts b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/start-contract.d.ts new file mode 100644 index 0000000000..c2fef632f6 --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/start-contract.d.ts @@ -0,0 +1,217 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma-next contract emit'. +// To regenerate, run: prisma-next contract emit +import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; +import type { + Bit, + Char, + CodecTypes as PgTypes, + Interval, + JsonValue, + Numeric, + Time, + Timestamp, + Timestamptz, + Timetz, + VarBit, + Varchar, +} from '@prisma-next/target-postgres/codec-types'; + +import type { + ContractWithTypeMaps, + TypeMaps as TypeMapsType, +} from '@prisma-next/sql-contract/types'; +import type { + Contract as ContractType, + ContractModelsMap, + ExecutionHashBase, + NamespaceId, + ProfileHashBase, + StorageHashBase, +} from '@prisma-next/contract/types'; + +export type StorageHash = + StorageHashBase<'sha256:7e3fa7fbe98974385451444c229337c87ec90c547a9214f81700f86b5af3563d'>; +export type ExecutionHash = + ExecutionHashBase<'sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545'>; +export type ProfileHash = + ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; + +export type CodecTypes = PgTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgAdapterQueryOps; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? CodecTypes[CodecId]['output'] + : _Encoded; + +export type FieldOutputTypes = { + readonly user: { + readonly id: Char<36>; + readonly email: CodecTypes['pg/text@1']['output']; + readonly avatar: CodecTypes['pg/text@1']['output'] | null; + }; +}; +export type FieldInputTypes = { + readonly user: { + readonly id: CodecTypes['sql/char@1']['input']; + readonly email: CodecTypes['pg/text@1']['input']; + readonly avatar: CodecTypes['pg/text@1']['input'] | null; + }; +}; +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes +>; + +type ContractBase = Omit< + ContractType< + { + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'sql-namespace'; + readonly tables: { + readonly user: { + columns: { + readonly id: { + readonly nativeType: 'character'; + readonly codecId: 'sql/char@1'; + readonly nullable: false; + readonly typeParams: { readonly length: 36 }; + }; + readonly email: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly avatar: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }, + { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly avatar: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly avatar: { readonly column: 'avatar' }; + }; + }; + }; + } + >, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly user: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'user' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly user: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly avatar: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly avatar: { readonly column: 'avatar' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; + readonly sql: { + readonly defaultInInsert: true; + readonly enums: true; + readonly lateral: true; + readonly returning: true; + }; + }; + readonly extensionPacks: {}; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly ref: { readonly table: 'user'; readonly column: 'id' }; + readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; + }, + ]; + }; + }; + readonly meta: {}; + + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; +export type Models = ContractModelsMap; diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/start-contract.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/start-contract.json new file mode 100644 index 0000000000..1e44db94bc --- /dev/null +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/20260303T1100_merge_bob/start-contract.json @@ -0,0 +1,142 @@ +{ + "schemaVersion": "1", + "targetFamily": "sql", + "target": "postgres", + "profileHash": "sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb", + "roots": { + "user": { + "model": "user", + "namespace": "__unbound__" + } + }, + "domain": { + "namespaces": { + "__unbound__": { + "models": { + "user": { + "fields": { + "avatar": { + "nullable": true, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "email": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { + "length": 36 + } + } + } + }, + "relations": {}, + "storage": { + "fields": { + "avatar": { + "column": "avatar" + }, + "email": { + "column": "email" + }, + "id": { + "column": "id" + } + }, + "table": "user" + } + } + } + } + } + }, + "storage": { + "namespaces": { + "__unbound__": { + "id": "__unbound__", + "kind": "postgres-unbound-schema", + "tables": { + "user": { + "columns": { + "avatar": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": true + }, + "email": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "id": { + "codecId": "sql/char@1", + "nativeType": "character", + "nullable": false, + "typeParams": { + "length": 36 + } + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": ["id"] + }, + "uniques": [] + } + } + } + }, + "storageHash": "sha256:7e3fa7fbe98974385451444c229337c87ec90c547a9214f81700f86b5af3563d" + }, + "execution": { + "executionHash": "sha256:5230f959b26fc69f483bc89f66df7c95e0e93400e0f4cb14311e4cbf963cd545", + "mutations": { + "defaults": [ + { + "onCreate": { + "id": "uuidv4", + "kind": "generator" + }, + "ref": { + "column": "id", + "table": "user" + } + } + ] + } + }, + "capabilities": { + "postgres": { + "distinctOn": true, + "jsonAgg": true, + "lateral": true, + "limit": true, + "orderBy": true, + "returning": true + }, + "sql": { + "defaultInInsert": true, + "enums": true, + "lateral": true, + "returning": true + } + }, + "extensionPacks": {}, + "meta": {}, + "_generated": { + "warning": "⚠️ GENERATED FILE - DO NOT EDIT", + "message": "This file is automatically generated by \"prisma-next contract emit\".", + "regenerate": "To regenerate, run: prisma-next contract emit" + } +} diff --git a/examples/prisma-next-demo/migration-fixtures/diamond/app/refs/prod.json b/examples/prisma-next-demo/migration-fixtures/diamond/app/refs/prod.json index a0b04be0c3..6c8e5d790c 100644 --- a/examples/prisma-next-demo/migration-fixtures/diamond/app/refs/prod.json +++ b/examples/prisma-next-demo/migration-fixtures/diamond/app/refs/prod.json @@ -1,4 +1,4 @@ { - "hash": "sha256:3b2d98ddf921fb8bcc05553d3349f85dec8e78c0ced66250cd95e24d95286a04", + "hash": "sha256:f9a41d77df6eae57bcd25ab25df31e6e905aad034a5b813f408bf8e78e9f384a", "invariants": [] } diff --git a/examples/prisma-next-demo/prisma-next.diamond.config.ts b/examples/prisma-next-demo/prisma-next.diamond.config.ts new file mode 100644 index 0000000000..a5f1f28a77 --- /dev/null +++ b/examples/prisma-next-demo/prisma-next.diamond.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from '@prisma-next/postgres/config'; + +export default defineConfig({ + contract: './diamond-contract/c5.prisma', + db: { + connection: 'postgresql://diamond:diamond@localhost:5432/diamond', + }, + migrations: { + dir: './migration-fixtures/diamond', + }, +}); diff --git a/projects/migration-graph-rendering/slices/diamond-fixture-regeneration/code-review.md b/projects/migration-graph-rendering/slices/diamond-fixture-regeneration/code-review.md new file mode 100644 index 0000000000..dde19861e7 --- /dev/null +++ b/projects/migration-graph-rendering/slices/diamond-fixture-regeneration/code-review.md @@ -0,0 +1,96 @@ +# Code review — regenerated `diamond` migration fixture + +**Commit:** `7a4d3100043edb57e0f7c20e64c6346cbdb6d502` on `regenerate-diamond-migration-fixture` +**Verdict:** **SATISFIED** — the diamond fixture is a complete, internally-consistent, extension-free 5-node diamond that converges at C5; `migration check` passes and both graph renders show a clean 5-node/5-edge diamond. Scope is confined to the three intended path globs and the main `prisma-next.config.ts` is untouched. + +## Hash legend + +| Label | storageHash (short) | +|---|---| +| C1 (init) | `789dd79` | +| C2 (alice_add_phone) | `93be6c2` | +| C3 (bob_add_avatar) | `7e3fa7f` | +| C5 (merge_alice / merge_bob converge) | `f9a41d7` | + +## Scoreboard + +| AC | Result | Evidence | +|---|---|---| +| 1. Full artifact set per node | **PASS** | All 5 nodes present. Each carries the same artifact kinds `showcase` uses: `migration.json`, `ops.json`, `end-contract.json`, `end-contract.d.ts`, `migration.ts`. Branched/merge nodes (alice, bob, merge_alice, merge_bob) additionally carry `start-contract.{json,d.ts}`; `init` does not. This matches `showcase` exactly — its `init` has the same 5-file set, while its branched/merge nodes (`alice_phone`, `merge_alice`, `merge_bob`) carry `start-contract.*`. Legitimate planner output for non-root edges; does not break the read path (check + graph both succeed). | +| 2. Diamond topology + convergence | **PASS** | `∅→C1`; `C1(789dd79)→C2(93be6c2)` (alice), `C1(789dd79)→C3(7e3fa7f)` (bob); `C2→C5(f9a41d7)` (merge_alice), `C3→C5(f9a41d7)` (merge_bob). `merge_alice.to == merge_bob.to == refs/prod.json.hash == f9a41d7`. C1 has two distinct children (`93be6c2` ≠ `7e3fa7f`). | +| 3. Internal consistency | **PASS** | Every `end-contract.json` storageHash == its `migration.json` `to` (init 789dd79, alice 93be6c2, bob 7e3fa7f, merge_alice/merge_bob f9a41d7). Bonus: each `start-contract.json` storageHash == its `from`. `pnpm exec prisma-next migration check --config ./prisma-next.diamond.config.ts` → `{ "ok": true, "failures": [], "summary": "All checks passed" }`. | +| 4. Renders | **PASS** | Both `--tree --format pretty` and default `--format pretty` render a 5-node/5-edge diamond with no errors (outputs below). | +| 5. No extensions / no synthetic remnants | **PASS** | No `vector`/`pgvector`/extension-space packages anywhere in the fixture; the only "extension" hits are `extensionPacks: {}` (empty) — genuine no-extension planner output. Ops are app-space only (`target.id: "postgres"`, schema `__unbound__`, operating on the `user` table/columns). Old synthetic hashes gone: pre-commit `prod.json` was `3b2d98d…` and `init.to` was `ef9de27…`; both replaced by real planned hashes. | +| 6. Scope | **PASS** | `git show --stat` touches only `diamond-contract/**`, `prisma-next.diamond.config.ts`, and `migration-fixtures/diamond/**`. `prisma-next.config.ts` is touched 0 times. New config mirrors `prisma-next.showcase.config.ts`: working contract `./diamond-contract/c5.prisma` (C5 source: `user` with id/email/phone/avatar), `migrations.dir → ./migration-fixtures/diamond`. | +| 7. `migration.ts` validity | **PASS** | `merge_alice/migration.ts` is a genuine generated artifact: shebang, `import … from '@prisma-next/postgres/migration'`, `class M extends Migration` with `describe()` (from `93be6c2` → to `f9a41d7`, matching migration.json) and `operations()` returning a real `addColumn('__unbound__', 'user', { name: 'avatar', … })`, then `MigrationCLI.run(...)`. `end-contract.d.ts` carries the `GENERATED FILE` header, real imports, and `StorageHash` brand `f9a41d7…` matching `migration.to`. Not stubs. | + +## `migration check` result + +``` +{ + "ok": true, + "failures": [], + "summary": "All checks passed" +} +``` + +## Graph render — `--tree --format pretty` + +``` +prisma-next migration graph → Show the migration graph topology +│ +│ config: prisma-next.diamond.config.ts +│ migrations: migration-fixtures/diamond/app +└ + +* f9a41d7 (prod, contract) ++-\ +|^| 20260303T1000_merge_alice 93be6c2 -> f9a41d7 +| |^ 20260303T1100_merge_bob 7e3fa7f -> f9a41d7 +* | 93be6c2 +|^| 20260302T1000_alice_add_phone 789dd79 -> 93be6c2 +| * 7e3fa7f +| |^ 20260302T1100_bob_add_avatar 789dd79 -> 7e3fa7f ++-/ +* 789dd79 +|^ 20260301T1000_init - -> 789dd79 +- + +5 node(s), 5 edge(s) +``` + +## Graph render — default `--format pretty` + +``` +prisma-next migration graph → Show the migration graph topology +│ +│ config: prisma-next.diamond.config.ts +│ migrations: migration-fixtures/diamond/app +└ + +│ +│ ○ ∅ +│ │ +│ │ 20260301T1000_init +│ │ +│ ▾ +│ ┌─────────○ 789dd79─┐ +│ │ │ +│ 20260302T1100_bob_add_avatar │ │ 20260302T1000_alice_add_phone +│ │ │ +│ ▾ ▾ +│ ○ 7e3fa7f ○ 93be6c2 +│ │ │ +│ 20260303T1100_merge_bob │ │ 20260303T1000_merge_alice +│ │ │ +│ └─────────▾─────────┘ +│ ○ f9a41d7 prod ◆ contract +│ +│ +│ 5 node(s), 5 edge(s) +``` + +## Findings + +- No blocking issues. No source/fixture files were modified during this review. +- Minor observation (out of scope, not introduced by this commit): the worktree's main `examples/prisma-next-demo/prisma-next.config.ts` currently has `migrations.dir: 'migration-fixtures/diamond'` and a `pgvector` extension while pointing `contract` at `./src/prisma/contract.prisma`. That is an uncommitted working-tree state unrelated to this commit (the commit does not touch that file). The dedicated `prisma-next.diamond.config.ts` is the correct, self-contained entry point for the diamond fixture and is what AC4/AC3 were validated against. diff --git a/projects/migration-graph-rendering/slices/diamond-fixture-regeneration/spec.md b/projects/migration-graph-rendering/slices/diamond-fixture-regeneration/spec.md new file mode 100644 index 0000000000..2dfcd2513d --- /dev/null +++ b/projects/migration-graph-rendering/slices/diamond-fixture-regeneration/spec.md @@ -0,0 +1,82 @@ +# Slice: Regenerate the `diamond` migration fixture as a complete, renderable set + +## Problem + +`examples/prisma-next-demo/migration-fixtures/diamond` is a hand-authored, +topology-only fixture: each node has only `migration.json` + `ops.json` with +**synthetic** hashes, no `end-contract.json` / `end-contract.d.ts` / `migration.ts`. +The live read/graph path (`readGraphNodeEndContract` in +`packages/1-framework/3-tooling/migration/src/aggregate/aggregate.ts`) requires +each node's `end-contract.json` + `.d.ts` to exist and deserialize, so pointing +the CLI at `diamond` fails with "missing destination contract snapshot". Because +the hashes are synthetic with no backing contract content, you cannot just "add" +end-contracts — the whole fixture must be regenerated so all artifacts agree. + +Nothing reads this fixture by path except a (deliberately not-committed) demo +config edit; the `describe('diamond')` unit test uses an in-memory graph. So the +fixture can be regenerated freely. + +## Design of record + +Regenerate the diamond **offline** with `prisma-next migration plan` (the command +is explicitly "fully offline — no database connection is needed"), driving a +branching/merging DAG via `--from --to `. + +Topology (preserve node dir names + timestamps so the diamond stays meaningful): + +``` + ∅ → C1 (20260301T1000_init) + / \ + C1 → C2 C1 → C3 (alice_add_phone / bob_add_avatar) + \ / + C2 → C5 C3 → C5 (merge_alice / merge_bob) refs/prod.json → C5 +``` + +Contract states (simple Prisma-style sources, mirror `showcase-contract/showcase.prisma`; +**no extensions** — keep it app-space only, drop the old `vector` extension to avoid +cross-space migration packages): + +- **C1** (`init`): `model user { id String @id @default(uuid()); email String }` +- **C2** (`alice_add_phone`): C1 + `phone String?` +- **C3** (`bob_add_avatar`): C1 + `avatar String?` +- **C5** (merge target): C1 + `phone String?` + `avatar String?` + +`merge_alice` (C2→C5 adds avatar) and `merge_bob` (C3→C5 adds phone) both land on +the same end contract C5, so they share a `to` hash — that is the convergence. + +Add `examples/prisma-next-demo/prisma-next.diamond.config.ts` mirroring +`prisma-next.showcase.config.ts` (contract → the C5 source, `migrations.dir` → +`./migration-fixtures/diamond`) so the fixture is renderable via `--config` +without touching the main `prisma-next.config.ts`. + +## Scope + +**In:** +- `examples/prisma-next-demo/migration-fixtures/diamond/app/**` — regenerate all 5 + packages with the full showcase artifact set (`migration.json`, `ops.json`, + `end-contract.json`, `end-contract.d.ts`, `migration.ts`), canonical dir names. +- `examples/prisma-next-demo/migration-fixtures/diamond/app/refs/prod.json` → C5 hash. +- New `examples/prisma-next-demo/diamond-contract/*` source(s). +- New `examples/prisma-next-demo/prisma-next.diamond.config.ts`. + +**Out:** main `prisma-next.config.ts`, `showcase` fixture, the lane-colors PR +(#674), any CLI source, anything outside the demo example. + +## Done when + +- `pnpm exec prisma-next migration graph --config ./prisma-next.diamond.config.ts` + (run from `examples/prisma-next-demo`) renders the diamond — 5 nodes, C1 fork to + two branches converging at C5 — with no errors, in both `--tree` and default modes. +- `prisma-next migration check --config ./prisma-next.diamond.config.ts` passes + (every `end-contract.json` storageHash matches its `migration.json` `to`). +- `migration.json`/`ops.json`/`end-contract.*` are mutually consistent and the + `from`/`to` chain forms the diamond; `refs/prod.json` resolves to C5. +- No change to the main demo config; `merge_bob` present; no extension-space packages. + +## Notes + +- Generated package dirs get a "now" timestamp prefix + `--name` slug; rename each + to the canonical `YYYYMMDDT…_slug` (identity is content-hash based, so renaming + is safe). Regenerated hashes will differ from the old synthetic ones — expected. +- This is a separate work item from the lane-colors slice; own branch + (`regenerate-diamond-migration-fixture`) off `main`, own PR.