diff --git a/integration-tests/prisma/.gitignore b/integration-tests/prisma/.gitignore new file mode 100644 index 000000000..a731db5ab --- /dev/null +++ b/integration-tests/prisma/.gitignore @@ -0,0 +1 @@ +*.schema diff --git a/integration-tests/prisma/dbschema/default.esdl b/integration-tests/prisma/dbschema/default.esdl new file mode 100644 index 000000000..eb4b769f7 --- /dev/null +++ b/integration-tests/prisma/dbschema/default.esdl @@ -0,0 +1,23 @@ +module default { + type GameSession { + multi link players: User { + constraint exclusive; + } + required property num: int32; + } + + abstract type Named { + required property name: str; + } + + type Post { + required link author: User; + required property body: str; + } + + type User extending Named; + + type UserGroup extending Named { + multi link users: User; + } +} diff --git a/integration-tests/prisma/dbschema/migrations/00001-m1b5kog.edgeql b/integration-tests/prisma/dbschema/migrations/00001-m1b5kog.edgeql new file mode 100644 index 000000000..e4cb1349e --- /dev/null +++ b/integration-tests/prisma/dbschema/migrations/00001-m1b5kog.edgeql @@ -0,0 +1,21 @@ +CREATE MIGRATION m1b5kogggyycxixgy2mcrqu3ntk3hhagdcospowiernk6ddu6op6ia + ONTO initial +{ + CREATE ABSTRACT TYPE default::Named { + CREATE REQUIRED PROPERTY name: std::str; + }; + CREATE TYPE default::User EXTENDING default::Named; + CREATE TYPE default::GameSession { + CREATE MULTI LINK players: default::User { + CREATE CONSTRAINT std::exclusive; + }; + CREATE REQUIRED PROPERTY num: std::int32; + }; + CREATE TYPE default::UserGroup EXTENDING default::Named { + CREATE MULTI LINK users: default::User; + }; + CREATE TYPE default::Post { + CREATE REQUIRED LINK author: default::User; + CREATE REQUIRED PROPERTY body: std::str; + }; +}; diff --git a/integration-tests/prisma/gel.toml b/integration-tests/prisma/gel.toml new file mode 100644 index 000000000..e9ced6501 --- /dev/null +++ b/integration-tests/prisma/gel.toml @@ -0,0 +1,2 @@ +[edgedb] +server-version = "nightly" diff --git a/integration-tests/prisma/globalSetup.ts b/integration-tests/prisma/globalSetup.ts new file mode 100644 index 000000000..4692a7fbb --- /dev/null +++ b/integration-tests/prisma/globalSetup.ts @@ -0,0 +1,9 @@ +import createClient from "edgedb"; + +export default async () => { + const client = createClient(); + + process.env._JEST_EDGEDB_VERSION = await client.queryRequiredSingleJSON( + `select sys::get_version()`, + ); +}; diff --git a/integration-tests/prisma/jest.config.js b/integration-tests/prisma/jest.config.js new file mode 100644 index 000000000..2c22ce297 --- /dev/null +++ b/integration-tests/prisma/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + preset: "ts-jest", + testEnvironment: "node", + testPathIgnorePatterns: ["./dist", "./esm", "./mts", "./cjs", "./deno"], + globalSetup: "./globalSetup.ts", + transform: {}, + globals: {}, +}; diff --git a/integration-tests/prisma/package.json b/integration-tests/prisma/package.json new file mode 100644 index 000000000..0dd685d7d --- /dev/null +++ b/integration-tests/prisma/package.json @@ -0,0 +1,32 @@ +{ + "private": true, + "name": "@edgedb/integration-prisma", + "version": "0.0.0", + "scripts": { + "typecheck": "echo 'Integration tests - lts, skipping typecheck...'", + "build": "echo 'Integration tests, no build output...'", + "generate": "pwd && npx @edgedb/generate prisma --file prisma.schema && npx prisma generate --schema=prisma.schema", + "test": "yarn test:ts", + "test:ts": "pwd && yarn generate && NODE_OPTIONS=\"--experimental-vm-modules\" jest --testPathIgnorePatterns='(esm/.*|mts/.*|cjs/.*|deno/.*)' --detectOpenHandles --forceExit", + "ci:integration-test": "tsx ./testRunner.ts", + "bench:types": "cd ../.. && tsx integration-tests/lts/bench.ts" + }, + "devDependencies": { + "@arktype/attest": "^0.7.8", + "@edgedb/generate": "*", + "@prisma/client": "^6.1.0", + "@tsconfig/node-lts": "^20.1.3", + "@types/jest": "^29.5.12", + "@types/node": "^20.12.13", + "conditional-type-checks": "^1.0.6", + "jest": "^29.7.0", + "prisma": "^6.1.0", + "superjson": "1.13.3", + "ts-jest": "^29.1.4", + "typescript": "^5.5.2" + }, + "dependencies": { + "edgedb": "*", + "fast-check": "^3.19.0" + } +} diff --git a/integration-tests/prisma/prisma.test.ts b/integration-tests/prisma/prisma.test.ts new file mode 100644 index 000000000..4b42f83cb --- /dev/null +++ b/integration-tests/prisma/prisma.test.ts @@ -0,0 +1,659 @@ +import assert from "node:assert/strict"; +import { spawnSync } from "child_process"; +import path from "path"; +import { adapter, Client, createClient } from "edgedb"; +import { execSync } from "child_process"; +import { PrismaClient, Prisma } from '@prisma/client'; + +// import { runCommand } from "../../driver/test/testUtil"; +import { setupTests, teardownTests } from "./setupTeardown"; + +let client: Client; +let prisma: PrismaClient; + +class Rollback {} + +describe("prisma", () => { + beforeAll(async () => { + const setup = await setupTests(); + ({ client } = setup); + + // the postgres DSN that prisma needs is nearly identical to the EdgeDB + // DSN, so we'll use it as the baseline + const dsn = spawnSync( + 'gel', + ['instance', 'credentials', '-I', 'prisma', '--insecure-dsn'] + ).stdout.toString(); + + prisma = new PrismaClient({ + datasources: { + db: { + url: dsn.replace(/^edgedb:/, 'postgresql:'), + }, + }, + }); + }); + + afterAll(async () => { + await teardownTests(client); + }); + + test("check read models 01", async () => { + const res = await prisma.user.findMany({orderBy: {name: 'asc'}}) + assert.deepEqual( + res.map((rec) => rec['name']), + ['Alice', 'Billie', 'Cameron', 'Dana', 'Elsa', 'Zoe'], + ) + }); + + test("check read models 02", async () => { + const res = await prisma.userGroup.findMany({orderBy: {name: 'asc'}}) + assert.deepEqual( + res.map((rec) => rec['name']), + ['blue', 'green', 'red'], + ) + }); + + test("check read models 03", async () => { + const res = await prisma.gameSession.findMany({orderBy: {num: 'asc'}}) + assert.deepEqual( + res.map((rec) => rec['num']), + [123, 456], + ) + }); + + test("check read models 04", async () => { + const res = await prisma.post.findMany({orderBy: {body: 'asc'}}) + assert.deepEqual( + res.map((rec) => rec['body']), + ['*magic stuff*', 'Hello', "I'm Alice", "I'm Cameron"], + ) + }); + + test("check read models 05", async () => { + const res = await prisma.named.findMany({orderBy: {name: 'asc'}}) + assert.deepEqual( + res.map((rec) => rec['name']), + [ + 'Alice', 'Billie', 'Cameron', 'Dana', 'Elsa', 'Zoe', + 'blue', 'green', 'red', + ], + ) + }); + + test("check read models 06", async () => { + const res = await prisma.post.findMany({ + select: { + body: true, + author: { + select: { + name: true, + } + } + }, + orderBy: {body: 'asc'}, + }) + assert.deepEqual( + res, + [ + {body: '*magic stuff*', author: {name: 'Elsa'}}, + {body: 'Hello', author: {name: 'Alice'}}, + {body: "I'm Alice", author: {name: 'Alice'}}, + {body: "I'm Cameron", author: {name: 'Cameron'}}, + ], + ) + }); + + test("check read models 07", async () => { + const res = await prisma.user.findMany({ + select: { + name: true, + backlink_via_author: { + select: { + body: true, + } + }, + }, + orderBy: {name: 'asc'}, + }) + assert.deepEqual( + res, + [ + { + name: 'Alice', + backlink_via_author: [ + {body: "Hello"}, {body: "I'm Alice"}, + ], + }, + { + name: 'Billie', + backlink_via_author: [], + }, + { + name: 'Cameron', + backlink_via_author: [ + {body: "I'm Cameron"}, + ], + }, + { + name: 'Dana', + backlink_via_author: [], + }, + { + name: 'Elsa', + backlink_via_author: [ + {body: "*magic stuff*"}, + ], + }, + { + name: 'Zoe', + backlink_via_author: [], + }, + ], + ) + }); + + test("check read models 08", async () => { + const res = await prisma.gameSession.findMany({ + select: { + num: true, + players: { + select: { + target: { + select: { + name: true, + }, + } + }, + }, + }, + orderBy: {num: 'asc'}, + }) + assert.deepEqual( + res, + [ + { + num: 123, + players: [ + {target: {name: 'Alice'}}, + {target: {name: 'Billie'}}, + ], + }, + { + num: 456, + players: [ + {target: {name: 'Dana'}}, + ], + }, + ], + ) + }); + + test("check read models 09", async () => { + const res = await prisma.user.findMany({ + select: { + name: true, + backlink_via_players: { + select: { + source: { + select: { + num: true, + } + } + } + } + }, + orderBy: {name: 'asc'}, + }) + assert.deepEqual( + res, + [ + { + name: 'Alice', + backlink_via_players: [{source: {num: 123}}], + }, + { + name: 'Billie', + backlink_via_players: [{source: {num: 123}}], + }, + { + name: 'Cameron', + backlink_via_players: [], + }, + { + name: 'Dana', + backlink_via_players: [{source: {num: 456}}], + }, + { + name: 'Elsa', + backlink_via_players: [], + }, + { + name: 'Zoe', + backlink_via_players: [], + }, + ], + ) + }); + + test("check read models 10", async () => { + const res = await prisma.userGroup.findMany({ + select: { + name: true, + users: { + select: { + target: { + select: { + name: true, + } + } + } + } + }, + orderBy: {name: 'asc'}, + }) + assert.deepEqual( + res, + [ + { + name: 'blue', + users: [], + }, + { + name: 'green', + users: [ + {target: {name: 'Alice'}}, + {target: {name: 'Billie'}}, + ], + }, + { + name: 'red', + users: [ + {target: {name: 'Alice'}}, + {target: {name: 'Billie'}}, + {target: {name: 'Cameron'}}, + {target: {name: 'Dana'}}, + ], + }, + ], + ) + }); + + test("check read models 11", async () => { + const res = await prisma.user.findMany({ + select: { + name: true, + backlink_via_users: { + select: { + source: { + select: { + name: true, + } + } + } + } + }, + orderBy: {name: 'asc'}, + }) + assert.deepEqual( + res, + [ + { + name: 'Alice', + backlink_via_users: [ + {source: {name: 'red'}}, + {source: {name: 'green'}}, + ], + }, + { + name: 'Billie', + backlink_via_users: [ + {source: {name: 'red'}}, + {source: {name: 'green'}}, + ], + }, + { + name: 'Cameron', + backlink_via_users: [ + {source: {name: 'red'}}, + ], + }, + { + name: 'Dana', + backlink_via_users: [ + {source: {name: 'red'}}, + ], + }, + { + name: 'Elsa', + backlink_via_users: [], + }, + { + name: 'Zoe', + backlink_via_users: [], + }, + ], + ) + }); + + test("check create models 01", async () => { + try { + await prisma.$transaction(async (tx) => { + await tx.user.create({ + data: { + name: 'Yvonne', + }, + }) + + const res = await tx.user.findFirst({ + where: { + name: 'Yvonne' + } + }) + + assert.equal(res!.name, 'Yvonne') + assert.ok(res!.id) + throw new Rollback() + }); + } catch (err) { + if (!(err instanceof Rollback)) { + throw err + } + } + }); + + test("check create models 02", async () => { + try { + await prisma.$transaction(async (tx) => { + await tx.userGroup.create({ + data: { + name: 'cyan', + users: { + create: [ + {target: {create: {name: 'Yvonne'}}}, + {target: {create: {name: 'Xander'}}}, + ], + } + }, + }) + + for (const name of ['Yvonne', 'Xander']) { + const res = await tx.user.findFirst({ + where: { + name: name + }, + include: { + backlink_via_users: { + include: { + source: true + } + } + } + }) + + assert.equal(res!.name, name) + assert.equal(res!.backlink_via_users[0].source.name, 'cyan') + assert.ok(res!.id) + } + + throw new Rollback() + }); + } catch (err) { + if (!(err instanceof Rollback)) { + throw err + } + } + }); + + test("check create models 03", async () => { + try { + await prisma.$transaction(async (tx) => { + // create user and then 2 posts + const user = await tx.user.create({ + data: { + name: 'Yvonne', + }, + }) + await tx.post.create({ + data: { + body: 'this is a test', + author_id: user.id, + } + }) + await tx.post.create({ + data: { + body: 'also a test', + author_id: user.id, + } + }) + + const res = await tx.post.findMany({ + where: { + author: { + name: 'Yvonne', + } + }, + select: { + body: true, + author: { + select: { + name: true, + } + } + }, + orderBy: { + body: 'asc', + } + }) + + assert.deepEqual( + res, + [{ + body: 'also a test', + author: {name: 'Yvonne'}, + }, { + body: 'this is a test', + author: {name: 'Yvonne'}, + }] + ) + + throw new Rollback() + }); + } catch (err) { + if (!(err instanceof Rollback)) { + throw err + } + } + }); + + test("check delete models 01", async () => { + try { + await prisma.$transaction(async (tx) => { + const user = await tx.user.findFirst({ + where: { + name: 'Zoe', + }, + }) + assert.ok(user?.id) + + // name is not unique so deleteMany is used + await tx.user.deleteMany({ + where: { + name: 'Zoe', + }, + }) + + const res = await tx.user.findMany({ + where: { + name: 'Zoe', + }, + }) + assert.deepEqual(res, []) + + throw new Rollback() + }); + } catch (err) { + if (!(err instanceof Rollback)) { + throw err + } + } + }); + + test("check delete models 02", async () => { + try { + await prisma.$transaction(async (tx) => { + const posts = await tx.post.findMany({ + where: { + author: { + name: 'Elsa', + }, + }, + }) + assert.equal(posts.length, 1) + assert.ok(posts[0]?.id) + + // name is not unique so deleteMany is used + await tx.post.delete({ + where: { + id: posts[0].id, + }, + }) + + const res = await tx.user.findFirst({ + where: { + name: 'Elsa', + }, + select: { + name: true, + backlink_via_author: true, + }, + }) + assert.deepEqual( + res, + { + name: 'Elsa', + backlink_via_author: [], + }, + ) + + throw new Rollback() + }); + } catch (err) { + if (!(err instanceof Rollback)) { + throw err + } + } + }); + + test("check delete models 03", async () => { + try { + await prisma.$transaction(async (tx) => { + const red = await tx.userGroup.findFirst({ + where: { + name: 'red', + }, + include: { + users: { + include: { + target: true + } + } + }, + }) + assert.deepEqual( + red!.users.map((rec) => rec['target']['name']), + ['Alice', 'Billie', 'Cameron', 'Dana'], + ) + + // drop Billie and Cameron from the group + for (const link of red!.users) { + if ( + link.target.name === 'Billie' || + link.target.name === 'Cameron' + ) { + await tx.userGroup_users.delete({ + where: { + source_id_target_id: { + source_id: link.source_id, + target_id: link.target_id, + }, + }, + }) + } + } + + const res = await tx.userGroup.findFirst({ + where: { + name: 'red', + }, + include: { + users: { + include: { + target: true + } + } + }, + }) + assert.deepEqual( + res!.users.map((rec) => rec['target']['name']), + ['Alice', 'Dana'], + ) + + throw new Rollback() + }); + } catch (err) { + if (!(err instanceof Rollback)) { + throw err + } + } + }); + + test("check update models 01", async () => { + // as long as we can update any model, it should be fine for all of them + // since in Prisma we reflect all things as models + try { + await prisma.$transaction(async (tx) => { + const user = await tx.user.findFirst({ + where: { + name: 'Alice', + }, + }) + const user_id = user!.id + assert.ok(user_id) + assert.equal(user?.name, 'Alice') + + // name is not unique so deleteMany is used + await tx.user.update({ + where: { + id: user.id, + }, + data: { + name: 'Xander' + }, + }) + + let res = await tx.user.findMany({ + where: { + name: 'Alice', + }, + }) + assert.deepEqual(res, []) + + res = await tx.user.findMany({ + where: { + name: 'Xander', + }, + }) + assert.equal(res.length, 1) + assert.equal(res[0]?.name, 'Xander') + assert.equal(res[0]?.id, user_id) + + throw new Rollback() + }); + } catch (err) { + if (!(err instanceof Rollback)) { + throw err + } + } + }); +}); diff --git a/integration-tests/prisma/setupTeardown.ts b/integration-tests/prisma/setupTeardown.ts new file mode 100644 index 000000000..71ee91e0f --- /dev/null +++ b/integration-tests/prisma/setupTeardown.ts @@ -0,0 +1,83 @@ +import * as tc from "conditional-type-checks"; +import { type Client, createClient } from "edgedb"; + +export { tc }; + +type depromisify = T extends Promise ? U : T; +export type TestData = depromisify>["data"]; + +export async function setupTests() { + const client = createClient(); + await cleanupData(client); + + await client.execute(` + insert User {name := 'Alice'}; + insert User {name := 'Billie'}; + insert User {name := 'Cameron'}; + insert User {name := 'Dana'}; + insert User {name := 'Elsa'}; + insert User {name := 'Zoe'}; + + insert UserGroup { + name := 'red', + users := (select User filter .name not in {'Elsa', 'Zoe'}), + }; + insert UserGroup { + name := 'green', + users := (select User filter .name in {'Alice', 'Billie'}), + }; + insert UserGroup { + name := 'blue', + }; + + insert GameSession { + num := 123, + players := (select User filter .name in {'Alice', 'Billie'}), + }; + insert GameSession { + num := 456, + players := (select User filter .name in {'Dana'}), + }; + + insert Post { + author := assert_single((select User filter .name = 'Alice')), + body := 'Hello', + }; + insert Post { + author := assert_single((select User filter .name = 'Alice')), + body := "I'm Alice", + }; + insert Post { + author := assert_single((select User filter .name = 'Cameron')), + body := "I'm Cameron", + }; + insert Post { + author := assert_single((select User filter .name = 'Elsa')), + body := '*magic stuff*', + }; + `); + + return { + // no data is needed + data: {}, + client, + }; +} + +async function cleanupData(client: Client) { + await client.execute('delete Object'); +} + +export async function teardownTests(client: Client) { + await cleanupData(client); + + await client.close(); +} + +export const versionGTE = (majorVer: number) => { + const version = JSON.parse(process.env._JEST_EDGEDB_VERSION!); + return version.major >= majorVer; +}; + +export const testIfVersionGTE = (majorVer: number) => + versionGTE(majorVer) ? test.skip : test; diff --git a/integration-tests/prisma/testRunner.ts b/integration-tests/prisma/testRunner.ts new file mode 100644 index 000000000..72711f2b2 --- /dev/null +++ b/integration-tests/prisma/testRunner.ts @@ -0,0 +1,40 @@ +import createClient from "edgedb"; + +import { + shutdown, + applyMigrations, + generateStatusFileName, + getServerCommand, + getWSLPath, + startServer, + runCommand, + configToEnv, +} from "../../packages/driver/test/testUtil"; + +(async function main() { + console.log("\nStarting EdgeDB test cluster..."); + + const statusFile = generateStatusFileName("node"); + console.log("Node status file:", statusFile); + + const { args } = getServerCommand(getWSLPath(statusFile)); + + const { proc, config } = await startServer(args, statusFile); + + console.log(`EdgeDB test cluster is up [port: ${config.port}]...`); + + const managementConn = await createClient(config).ensureConnected(); + + try { + await applyMigrations(config); + console.log(`\nRunning tests...`); + await runCommand("yarn", ["test:ts"], configToEnv(config)); + } catch (err) { + console.error(err); + process.exit(1); + } finally { + console.log("Shutting down EdgeDB test cluster..."); + await shutdown(proc, managementConn); + console.log("EdgeDB test cluster is down..."); + } +})(); diff --git a/integration-tests/prisma/tsconfig.json b/integration-tests/prisma/tsconfig.json new file mode 100644 index 000000000..1a9da497e --- /dev/null +++ b/integration-tests/prisma/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@tsconfig/node-lts/tsconfig.json", + "include": ["./**/*.ts"] +} diff --git a/yarn.lock b/yarn.lock index 7d9ac1267..05500fa87 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1394,6 +1394,47 @@ resolved "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz" integrity sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ== +"@prisma/client@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-6.1.0.tgz#179d3b70586e7be522f6f1f0a82cca01396f719a" + integrity sha512-AbQYc5+EJKm1Ydfq3KxwcGiy7wIbm4/QbjCKWWoNROtvy7d6a3gmAGkKjK0iUCzh+rHV8xDhD5Cge8ke/kiy5Q== + +"@prisma/debug@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-6.1.0.tgz#a27a1d144f72a3bc95061ecb0255e7554d9d59ec" + integrity sha512-0himsvcM4DGBTtvXkd2Tggv6sl2JyUYLzEGXXleFY+7Kp6rZeSS3hiTW9mwtUlXrwYbJP6pwlVNB7jYElrjWUg== + +"@prisma/engines-version@6.1.0-21.11f085a2012c0f4778414c8db2651556ee0ef959": + version "6.1.0-21.11f085a2012c0f4778414c8db2651556ee0ef959" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-6.1.0-21.11f085a2012c0f4778414c8db2651556ee0ef959.tgz#0b21ebf57362ffe35d0760c39855f90bbfa0f2fd" + integrity sha512-PdJqmYM2Fd8K0weOOtQThWylwjsDlTig+8Pcg47/jszMuLL9iLIaygC3cjWJLda69siRW4STlCTMSgOjZzvKPQ== + +"@prisma/engines@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-6.1.0.tgz#2195244a8ce33839a8131e4465624e21d1f8d042" + integrity sha512-GnYJbCiep3Vyr1P/415ReYrgJUjP79fBNc1wCo7NP6Eia0CzL2Ot9vK7Infczv3oK7JLrCcawOSAxFxNFsAERQ== + dependencies: + "@prisma/debug" "6.1.0" + "@prisma/engines-version" "6.1.0-21.11f085a2012c0f4778414c8db2651556ee0ef959" + "@prisma/fetch-engine" "6.1.0" + "@prisma/get-platform" "6.1.0" + +"@prisma/fetch-engine@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-6.1.0.tgz#2a5174787bf57c9b1d5d400bb923e0dc6a73a794" + integrity sha512-asdFi7TvPlEZ8CzSZ/+Du5wZ27q6OJbRSXh+S8ISZguu+S9KtS/gP7NeXceZyb1Jv1SM1S5YfiCv+STDsG6rrg== + dependencies: + "@prisma/debug" "6.1.0" + "@prisma/engines-version" "6.1.0-21.11f085a2012c0f4778414c8db2651556ee0ef959" + "@prisma/get-platform" "6.1.0" + +"@prisma/get-platform@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-6.1.0.tgz#d4394a24ef91af6675a92382ed40e6e6e07eeb13" + integrity sha512-ia8bNjboBoHkmKGGaWtqtlgQOhCi7+f85aOkPJKgNwWvYrT6l78KgojLekE8zMhVk0R9lWcifV0Pf8l3/15V0Q== + dependencies: + "@prisma/debug" "6.1.0" + "@remix-run/router@1.16.1": version "1.16.1" resolved "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz" @@ -3258,7 +3299,7 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2, fsevents@~2.3.2, fsevents@~2.3.3: +fsevents@2.3.3, fsevents@^2.3.2, fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -4776,6 +4817,15 @@ pretty-format@^29.0.0, pretty-format@^29.7.0: ansi-styles "^5.0.0" react-is "^18.0.0" +prisma@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-6.1.0.tgz#738f657fdd5ab8e6775f385db81bf7e61c70fbaf" + integrity sha512-aFI3Yi+ApUxkwCJJwyQSwpyzUX7YX3ihzuHNHOyv4GJg3X5tQsmRaJEnZ+ZyfHpMtnyahhmXVfbTZ+lS8ZtfKw== + dependencies: + "@prisma/engines" "6.1.0" + optionalDependencies: + fsevents "2.3.3" + prompts@^2.0.1: version "2.4.2" resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz"