diff --git a/examples/prisma-next-demo-sqlite/migrations/app/refs/db.contract.d.ts b/examples/prisma-next-demo-sqlite/migrations/app/refs/db.contract.d.ts new file mode 100644 index 0000000000..6e028057fb --- /dev/null +++ b/examples/prisma-next-demo-sqlite/migrations/app/refs/db.contract.d.ts @@ -0,0 +1,673 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma-next contract emit'. +// To regenerate, run: prisma-next contract emit +import type { CodecTypes as SqliteTypes } from '@prisma-next/adapter-sqlite/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:194cf0bce4a19c7191f9e4ae8670de21707c48678d7b8998d38708a506488c88'>; +export type ExecutionHash = + ExecutionHashBase<'sha256:70c6ceb0ed79d5888519ec84bf32e31a056283fa8d5c7b5c1ba65b709f8595c8'>; +export type ProfileHash = + ProfileHashBase<'sha256:3cc333ecad9f3f4c7229370a9d2c37e908cdce0f8d2e9fb132d50605b024eff2'>; + +export type CodecTypes = SqliteTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = Record; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? CodecTypes[CodecId]['output'] + : _Encoded; + +export type FieldOutputTypes = { + readonly Post: { + readonly id: CodecTypes['sql/char@1']['output']; + readonly title: CodecTypes['sqlite/text@1']['output']; + readonly userId: CodecTypes['sql/char@1']['output']; + readonly createdAt: CodecTypes['sqlite/datetime@1']['output']; + }; + readonly PostTag: { + readonly postId: CodecTypes['sql/char@1']['output']; + readonly tagId: CodecTypes['sql/char@1']['output']; + }; + readonly Tag: { + readonly id: CodecTypes['sql/char@1']['output']; + readonly label: CodecTypes['sqlite/text@1']['output']; + }; + readonly User: { + readonly id: CodecTypes['sql/char@1']['output']; + readonly email: CodecTypes['sqlite/text@1']['output']; + readonly displayName: CodecTypes['sqlite/text@1']['output']; + readonly createdAt: CodecTypes['sqlite/datetime@1']['output']; + }; +}; +export type FieldInputTypes = { + readonly Post: { + readonly id: CodecTypes['sql/char@1']['input']; + readonly title: CodecTypes['sqlite/text@1']['input']; + readonly userId: CodecTypes['sql/char@1']['input']; + readonly createdAt: CodecTypes['sqlite/datetime@1']['input']; + }; + readonly PostTag: { + readonly postId: CodecTypes['sql/char@1']['input']; + readonly tagId: CodecTypes['sql/char@1']['input']; + }; + readonly Tag: { + readonly id: CodecTypes['sql/char@1']['input']; + readonly label: CodecTypes['sqlite/text@1']['input']; + }; + readonly User: { + readonly id: CodecTypes['sql/char@1']['input']; + readonly email: CodecTypes['sqlite/text@1']['input']; + readonly displayName: CodecTypes['sqlite/text@1']['input']; + readonly createdAt: CodecTypes['sqlite/datetime@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 post: { + columns: { + readonly id: { + readonly nativeType: 'character'; + readonly codecId: 'sql/char@1'; + readonly nullable: false; + readonly typeParams: { readonly length: 36 }; + }; + readonly title: { + readonly nativeType: 'text'; + readonly codecId: 'sqlite/text@1'; + readonly nullable: false; + }; + readonly userId: { + readonly nativeType: 'character'; + readonly codecId: 'sql/char@1'; + readonly nullable: false; + readonly typeParams: { readonly length: 36 }; + }; + readonly createdAt: { + readonly nativeType: 'text'; + readonly codecId: 'sqlite/datetime@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: '__unbound__' & NamespaceId; + readonly tableName: 'post'; + readonly columns: readonly ['userId']; + }; + readonly target: { + readonly namespaceId: '__unbound__' & NamespaceId; + readonly tableName: 'user'; + readonly columns: readonly ['id']; + }; + readonly name: 'post_userId_fkey'; + readonly constraint: true; + readonly index: true; + }, + ]; + }; + readonly post_tag: { + columns: { + readonly postId: { + readonly nativeType: 'character'; + readonly codecId: 'sql/char@1'; + readonly nullable: false; + readonly typeParams: { readonly length: 36 }; + }; + readonly tagId: { + readonly nativeType: 'character'; + readonly codecId: 'sql/char@1'; + readonly nullable: false; + readonly typeParams: { readonly length: 36 }; + }; + }; + primaryKey: { + readonly columns: readonly ['postId', 'tagId']; + readonly name: 'post_tag_pkey'; + }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: '__unbound__' & NamespaceId; + readonly tableName: 'post_tag'; + readonly columns: readonly ['postId']; + }; + readonly target: { + readonly namespaceId: '__unbound__' & NamespaceId; + readonly tableName: 'post'; + readonly columns: readonly ['id']; + }; + readonly name: 'post_tag_postId_fkey'; + readonly constraint: true; + readonly index: true; + }, + { + readonly source: { + readonly namespaceId: '__unbound__' & NamespaceId; + readonly tableName: 'post_tag'; + readonly columns: readonly ['tagId']; + }; + readonly target: { + readonly namespaceId: '__unbound__' & NamespaceId; + readonly tableName: 'tag'; + readonly columns: readonly ['id']; + }; + readonly name: 'post_tag_tagId_fkey'; + readonly constraint: true; + readonly index: true; + }, + ]; + }; + readonly tag: { + columns: { + readonly id: { + readonly nativeType: 'character'; + readonly codecId: 'sql/char@1'; + readonly nullable: false; + readonly typeParams: { readonly length: 36 }; + }; + readonly label: { + readonly nativeType: 'text'; + readonly codecId: 'sqlite/text@1'; + readonly nullable: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + 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: 'sqlite/text@1'; + readonly nullable: false; + }; + readonly displayName: { + readonly nativeType: 'text'; + readonly codecId: 'sqlite/text@1'; + readonly nullable: false; + }; + readonly createdAt: { + readonly nativeType: 'text'; + readonly codecId: 'sqlite/datetime@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }, + { + readonly Post: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'sqlite/text@1' }; + }; + readonly userId: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'sqlite/datetime@1' }; + }; + }; + readonly relations: { + readonly user: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'User'; + }; + readonly cardinality: 'N:1'; + readonly on: { + readonly localFields: readonly ['userId']; + readonly targetFields: readonly ['id']; + }; + }; + readonly tags: { + readonly to: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Tag' }; + readonly cardinality: 'N:M'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['postId']; + }; + readonly through: { + readonly table: 'post_tag'; + readonly parentColumns: readonly ['postId']; + readonly childColumns: readonly ['tagId']; + readonly targetColumns: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'post'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly title: { readonly column: 'title' }; + readonly userId: { readonly column: 'userId' }; + readonly createdAt: { readonly column: 'createdAt' }; + }; + }; + }; + readonly PostTag: { + readonly fields: { + readonly postId: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly tagId: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'post_tag'; + readonly fields: { + readonly postId: { readonly column: 'postId' }; + readonly tagId: { readonly column: 'tagId' }; + }; + }; + }; + readonly Tag: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly label: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'sqlite/text@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + readonly cardinality: 'N:M'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['tagId']; + }; + readonly through: { + readonly table: 'post_tag'; + readonly parentColumns: readonly ['tagId']; + readonly childColumns: readonly ['postId']; + readonly targetColumns: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'tag'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly label: { readonly column: 'label' }; + }; + }; + }; + 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: 'sqlite/text@1' }; + }; + readonly displayName: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'sqlite/text@1' }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'sqlite/datetime@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['userId']; + }; + }; + }; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly displayName: { readonly column: 'displayName' }; + readonly createdAt: { readonly column: 'createdAt' }; + }; + }; + }; + } + >, + 'roots' | 'domain' +> & { + readonly target: 'sqlite'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly user: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'User' }; + readonly post: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Post' }; + readonly tag: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Tag' }; + readonly post_tag: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'PostTag'; + }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly Post: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'sqlite/text@1' }; + }; + readonly userId: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'sqlite/datetime@1' }; + }; + }; + readonly relations: { + readonly user: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'User'; + }; + readonly cardinality: 'N:1'; + readonly on: { + readonly localFields: readonly ['userId']; + readonly targetFields: readonly ['id']; + }; + }; + readonly tags: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Tag'; + }; + readonly cardinality: 'N:M'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['postId']; + }; + readonly through: { + readonly table: 'post_tag'; + readonly parentColumns: readonly ['postId']; + readonly childColumns: readonly ['tagId']; + readonly targetColumns: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'post'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly title: { readonly column: 'title' }; + readonly userId: { readonly column: 'userId' }; + readonly createdAt: { readonly column: 'createdAt' }; + }; + }; + }; + readonly PostTag: { + readonly fields: { + readonly postId: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly tagId: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'post_tag'; + readonly fields: { + readonly postId: { readonly column: 'postId' }; + readonly tagId: { readonly column: 'tagId' }; + }; + }; + }; + readonly Tag: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'sql/char@1'; + readonly typeParams: { readonly length: 36 }; + }; + }; + readonly label: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'sqlite/text@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + readonly cardinality: 'N:M'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['tagId']; + }; + readonly through: { + readonly table: 'post_tag'; + readonly parentColumns: readonly ['tagId']; + readonly childColumns: readonly ['postId']; + readonly targetColumns: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'tag'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly label: { readonly column: 'label' }; + }; + }; + }; + 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: 'sqlite/text@1' }; + }; + readonly displayName: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'sqlite/text@1' }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'sqlite/datetime@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['userId']; + }; + }; + }; + readonly storage: { + readonly table: 'user'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly displayName: { readonly column: 'displayName' }; + readonly createdAt: { readonly column: 'createdAt' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: { + readonly sql: { + readonly enums: false; + readonly foreignKeys: true; + readonly jsonAgg: true; + readonly lateral: false; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; + }; + readonly extensionPacks: {}; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly ref: { readonly table: 'post'; readonly column: 'id' }; + readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; + }, + { + readonly ref: { readonly table: 'tag'; readonly column: 'id' }; + readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; + }, + { + 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-sqlite/migrations/app/refs/db.contract.json b/examples/prisma-next-demo-sqlite/migrations/app/refs/db.contract.json new file mode 100644 index 0000000000..9461d82c1f --- /dev/null +++ b/examples/prisma-next-demo-sqlite/migrations/app/refs/db.contract.json @@ -0,0 +1,341 @@ +{ + "_generated": { + "message": "This file is automatically generated by \"prisma-next contract emit\".", + "regenerate": "To regenerate, run: prisma-next contract emit", + "warning": "⚠️ GENERATED FILE - DO NOT EDIT" + }, + "capabilities": { + "sql": { + "foreignKeys": true, + "jsonAgg": true, + "limit": true, + "orderBy": true, + "returning": true + } + }, + "domain": { + "namespaces": { + "__unbound__": { + "models": { + "Post": { + "fields": { + "createdAt": { + "nullable": false, + "type": { "codecId": "sqlite/datetime@1", "kind": "scalar" } + }, + "id": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { "length": 36 } + } + }, + "title": { + "nullable": false, + "type": { "codecId": "sqlite/text@1", "kind": "scalar" } + }, + "userId": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { "length": 36 } + } + } + }, + "relations": { + "tags": { + "cardinality": "N:M", + "on": { "localFields": ["id"], "targetFields": ["postId"] }, + "through": { + "childColumns": ["tagId"], + "parentColumns": ["postId"], + "table": "post_tag", + "targetColumns": ["id"] + }, + "to": { "model": "Tag", "namespace": "__unbound__" } + }, + "user": { + "cardinality": "N:1", + "on": { "localFields": ["userId"], "targetFields": ["id"] }, + "to": { "model": "User", "namespace": "__unbound__" } + } + }, + "storage": { + "fields": { + "createdAt": { "column": "createdAt" }, + "id": { "column": "id" }, + "title": { "column": "title" }, + "userId": { "column": "userId" } + }, + "table": "post" + } + }, + "PostTag": { + "fields": { + "postId": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { "length": 36 } + } + }, + "tagId": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { "length": 36 } + } + } + }, + "relations": {}, + "storage": { + "fields": { "postId": { "column": "postId" }, "tagId": { "column": "tagId" } }, + "table": "post_tag" + } + }, + "Tag": { + "fields": { + "id": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { "length": 36 } + } + }, + "label": { + "nullable": false, + "type": { "codecId": "sqlite/text@1", "kind": "scalar" } + } + }, + "relations": { + "posts": { + "cardinality": "N:M", + "on": { "localFields": ["id"], "targetFields": ["tagId"] }, + "through": { + "childColumns": ["postId"], + "parentColumns": ["tagId"], + "table": "post_tag", + "targetColumns": ["id"] + }, + "to": { "model": "Post", "namespace": "__unbound__" } + } + }, + "storage": { + "fields": { "id": { "column": "id" }, "label": { "column": "label" } }, + "table": "tag" + } + }, + "User": { + "fields": { + "createdAt": { + "nullable": false, + "type": { "codecId": "sqlite/datetime@1", "kind": "scalar" } + }, + "displayName": { + "nullable": false, + "type": { "codecId": "sqlite/text@1", "kind": "scalar" } + }, + "email": { + "nullable": false, + "type": { "codecId": "sqlite/text@1", "kind": "scalar" } + }, + "id": { + "nullable": false, + "type": { + "codecId": "sql/char@1", + "kind": "scalar", + "typeParams": { "length": 36 } + } + } + }, + "relations": { + "posts": { + "cardinality": "1:N", + "on": { "localFields": ["id"], "targetFields": ["userId"] }, + "to": { "model": "Post", "namespace": "__unbound__" } + } + }, + "storage": { + "fields": { + "createdAt": { "column": "createdAt" }, + "displayName": { "column": "displayName" }, + "email": { "column": "email" }, + "id": { "column": "id" } + }, + "table": "user" + } + } + } + } + } + }, + "execution": { + "executionHash": "sha256:70c6ceb0ed79d5888519ec84bf32e31a056283fa8d5c7b5c1ba65b709f8595c8", + "mutations": { + "defaults": [ + { + "onCreate": { "id": "uuidv4", "kind": "generator" }, + "ref": { "column": "id", "table": "post" } + }, + { + "onCreate": { "id": "uuidv4", "kind": "generator" }, + "ref": { "column": "id", "table": "tag" } + }, + { + "onCreate": { "id": "uuidv4", "kind": "generator" }, + "ref": { "column": "id", "table": "user" } + } + ] + } + }, + "extensionPacks": {}, + "meta": {}, + "profileHash": "sha256:3cc333ecad9f3f4c7229370a9d2c37e908cdce0f8d2e9fb132d50605b024eff2", + "roots": { + "post": { "model": "Post", "namespace": "__unbound__" }, + "post_tag": { "model": "PostTag", "namespace": "__unbound__" }, + "tag": { "model": "Tag", "namespace": "__unbound__" }, + "user": { "model": "User", "namespace": "__unbound__" } + }, + "schemaVersion": "1", + "storage": { + "namespaces": { + "__unbound__": { + "id": "__unbound__", + "tables": { + "post": { + "columns": { + "createdAt": { + "codecId": "sqlite/datetime@1", + "default": { "expression": "now()", "kind": "function" }, + "nativeType": "text", + "nullable": false + }, + "id": { + "codecId": "sql/char@1", + "nativeType": "character", + "nullable": false, + "typeParams": { "length": 36 } + }, + "title": { "codecId": "sqlite/text@1", "nativeType": "text", "nullable": false }, + "userId": { + "codecId": "sql/char@1", + "nativeType": "character", + "nullable": false, + "typeParams": { "length": 36 } + } + }, + "foreignKeys": [ + { + "constraint": true, + "index": true, + "name": "post_userId_fkey", + "source": { + "columns": ["userId"], + "namespaceId": "__unbound__", + "tableName": "post" + }, + "target": { "columns": ["id"], "namespaceId": "__unbound__", "tableName": "user" } + } + ], + "indexes": [], + "primaryKey": { "columns": ["id"] }, + "uniques": [] + }, + "post_tag": { + "columns": { + "postId": { + "codecId": "sql/char@1", + "nativeType": "character", + "nullable": false, + "typeParams": { "length": 36 } + }, + "tagId": { + "codecId": "sql/char@1", + "nativeType": "character", + "nullable": false, + "typeParams": { "length": 36 } + } + }, + "foreignKeys": [ + { + "constraint": true, + "index": true, + "name": "post_tag_postId_fkey", + "source": { + "columns": ["postId"], + "namespaceId": "__unbound__", + "tableName": "post_tag" + }, + "target": { "columns": ["id"], "namespaceId": "__unbound__", "tableName": "post" } + }, + { + "constraint": true, + "index": true, + "name": "post_tag_tagId_fkey", + "source": { + "columns": ["tagId"], + "namespaceId": "__unbound__", + "tableName": "post_tag" + }, + "target": { "columns": ["id"], "namespaceId": "__unbound__", "tableName": "tag" } + } + ], + "indexes": [], + "primaryKey": { "columns": ["postId", "tagId"], "name": "post_tag_pkey" }, + "uniques": [] + }, + "tag": { + "columns": { + "id": { + "codecId": "sql/char@1", + "nativeType": "character", + "nullable": false, + "typeParams": { "length": 36 } + }, + "label": { "codecId": "sqlite/text@1", "nativeType": "text", "nullable": false } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { "columns": ["id"] }, + "uniques": [] + }, + "user": { + "columns": { + "createdAt": { + "codecId": "sqlite/datetime@1", + "default": { "expression": "now()", "kind": "function" }, + "nativeType": "text", + "nullable": false + }, + "displayName": { + "codecId": "sqlite/text@1", + "nativeType": "text", + "nullable": false + }, + "email": { "codecId": "sqlite/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:194cf0bce4a19c7191f9e4ae8670de21707c48678d7b8998d38708a506488c88" + }, + "target": "sqlite", + "targetFamily": "sql" +} diff --git a/examples/prisma-next-demo-sqlite/migrations/app/refs/db.json b/examples/prisma-next-demo-sqlite/migrations/app/refs/db.json new file mode 100644 index 0000000000..90d46ecf92 --- /dev/null +++ b/examples/prisma-next-demo-sqlite/migrations/app/refs/db.json @@ -0,0 +1,4 @@ +{ + "hash": "sha256:194cf0bce4a19c7191f9e4ae8670de21707c48678d7b8998d38708a506488c88", + "invariants": [] +} diff --git a/examples/prisma-next-demo-sqlite/prisma/contract.ts b/examples/prisma-next-demo-sqlite/prisma/contract.ts index cb0ae3d28a..aeebb5bb45 100644 --- a/examples/prisma-next-demo-sqlite/prisma/contract.ts +++ b/examples/prisma-next-demo-sqlite/prisma/contract.ts @@ -20,6 +20,22 @@ export const contract = defineContract({}, ({ field, model }) => { }, }); + const Tag = model('Tag', { + fields: { + id: field.id.uuidv4(), + label: field.column(textColumn), + }, + }); + + const PostTag = model('PostTag', { + fields: { + postId: field.uuid(), + tagId: field.uuid(), + }, + }).attributes(({ fields, constraints }) => ({ + id: constraints.id([fields.postId, fields.tagId], { name: 'post_tag_pkey' }), + })); + return { models: { User: User.relations({ @@ -29,6 +45,11 @@ export const contract = defineContract({}, ({ field, model }) => { }), Post: Post.relations({ user: rel.belongsTo(User, { from: 'userId', to: 'id' }), + tags: rel.manyToMany(() => Tag, { + through: () => PostTag, + from: 'postId', + to: 'tagId', + }), }).sql(({ cols, constraints }) => ({ table: 'post', foreignKeys: [ @@ -37,6 +58,22 @@ export const contract = defineContract({}, ({ field, model }) => { }), ], })), + Tag: Tag.relations({ + posts: rel.manyToMany(() => Post, { + through: () => PostTag, + from: 'tagId', + to: 'postId', + }), + }).sql({ + table: 'tag', + }), + PostTag: PostTag.sql(({ cols, constraints }) => ({ + table: 'post_tag', + foreignKeys: [ + constraints.foreignKey(cols.postId, Post.refs.id, { name: 'post_tag_postId_fkey' }), + constraints.foreignKey(cols.tagId, Tag.refs.id, { name: 'post_tag_tagId_fkey' }), + ], + })), }, }; }); diff --git a/examples/prisma-next-demo-sqlite/scripts/seed.ts b/examples/prisma-next-demo-sqlite/scripts/seed.ts index 0995fee7f8..ebee198423 100644 --- a/examples/prisma-next-demo-sqlite/scripts/seed.ts +++ b/examples/prisma-next-demo-sqlite/scripts/seed.ts @@ -5,7 +5,8 @@ * * Run with: pnpm seed * - * Creates 2 users (alice, bob) and 3 posts. + * Creates 2 users (alice, bob), 3 posts, 3 tags, and junction rows demonstrating + * the Post ↔ Tag many-to-many relation. * * Prerequisites: * - SQLITE_PATH env var (defaults to ./demo.db) @@ -70,20 +71,73 @@ async function main() { console.log(`Created user: ${alice.email} (id: ${alice.id})`); console.log(`Created user: ${bob.email} (id: ${bob.id})`); - await runtime.execute( + const firstPostRows = await runtime.execute( db.sql.post .insert([{ title: 'First Post', userId: alice.id, createdAt: new Date() }]) + .returning('id', 'title') .build(), ); - await runtime.execute( + const secondPostRows = await runtime.execute( db.sql.post .insert([{ title: 'Second Post', userId: alice.id, createdAt: new Date() }]) + .returning('id', 'title') .build(), ); await runtime.execute( db.sql.post.insert([{ title: 'Third Post', userId: bob.id, createdAt: new Date() }]).build(), ); + const firstPost = firstPostRows[0] ?? null; + const secondPost = secondPostRows[0] ?? null; + if (!firstPost || !secondPost) { + throw new Error('Failed to create posts'); + } + + console.log(`Created post: ${firstPost.title} (id: ${firstPost.id})`); + console.log(`Created post: ${secondPost.title} (id: ${secondPost.id})`); + + const tagTypeScriptRows = await runtime.execute( + db.sql.tag + .insert([{ label: 'typescript' }]) + .returning('id', 'label') + .build(), + ); + const tagOrmRows = await runtime.execute( + db.sql.tag + .insert([{ label: 'orm' }]) + .returning('id', 'label') + .build(), + ); + const tagDemoRows = await runtime.execute( + db.sql.tag + .insert([{ label: 'demo' }]) + .returning('id', 'label') + .build(), + ); + + const tagTypeScript = tagTypeScriptRows[0] ?? null; + const tagOrm = tagOrmRows[0] ?? null; + const tagDemo = tagDemoRows[0] ?? null; + if (!tagTypeScript || !tagOrm || !tagDemo) { + throw new Error('Failed to create tags'); + } + + console.log(`Created tag: ${tagTypeScript.label} (id: ${tagTypeScript.id})`); + console.log(`Created tag: ${tagOrm.label} (id: ${tagOrm.id})`); + console.log(`Created tag: ${tagDemo.label} (id: ${tagDemo.id})`); + + await runtime.execute( + db.sql.post_tag + .insert([ + { postId: firstPost.id, tagId: tagTypeScript.id }, + { postId: firstPost.id, tagId: tagOrm.id }, + { postId: secondPost.id, tagId: tagOrm.id }, + { postId: secondPost.id, tagId: tagDemo.id }, + ]) + .build(), + ); + + console.log('Seeded post_tag junction rows.'); console.log('Seed completed successfully!'); } finally { await runtime.close(); diff --git a/examples/prisma-next-demo-sqlite/src/main.ts b/examples/prisma-next-demo-sqlite/src/main.ts index b42941ead0..9aa6819b6c 100644 --- a/examples/prisma-next-demo-sqlite/src/main.ts +++ b/examples/prisma-next-demo-sqlite/src/main.ts @@ -14,13 +14,28 @@ * each email — single lower(), single beforeCompile(), * repeated execute() * + * Many-to-many commands (Post ↔ Tag via PostTag junction): + * - post-tags Include a post's tags (N:M read) + * - posts-with-tag-some