| 1 | import * as Prisma from "@/Prisma"; |
| 2 | import * as PrismaPackage from "alchemy/Prisma"; |
| 3 | import * as NodeServices from "@effect/platform-node/NodeServices"; |
| 4 | import { describe, expect, it } from "alchemy-test"; |
| 5 | import * as Effect from "effect/Effect"; |
| 6 | import * as FileSystem from "effect/FileSystem"; |
| 7 | import * as Path from "effect/Path"; |
| 8 | |
| 9 | const publicExports = [ |
| 10 | "CheckViolationError", |
| 11 | "CliError", |
| 12 | "ConnectionError", |
| 13 | "Contract", |
| 14 | "ContractProvider", |
| 15 | "ForeignKeyViolationError", |
| 16 | "Migrate", |
| 17 | "MigrateProvider", |
| 18 | "NotNullViolationError", |
| 19 | "OrmError", |
| 20 | "QueryError", |
| 21 | "RollbackError", |
| 22 | "RuntimeError", |
| 23 | "UniqueViolationError", |
| 24 | "UnknownError", |
| 25 | "Website", |
| 26 | "wrapPrismaError", |
| 27 | |
| 28 | "providers", |
| 29 | "managementApi", |
| 30 | "Providers", |
| 31 | "PrismaEnvironment", |
| 32 | "PrismaAuth", |
| 33 | "PrismaClient", |
| 34 | "Project", |
| 35 | "ProjectProvider", |
| 36 | "Database", |
| 37 | "DatabaseProvider", |
| 38 | "Postgres", |
| 39 | "Connection", |
| 40 | "Connect", |
| 41 | "ConnectBinding", |
| 42 | "ConnectionProvider", |
| 43 | "connectEnvKeys", |
| 44 | "parsePostgresOrigin", |
| 45 | "Branch", |
| 46 | "BranchProvider", |
| 47 | "Bucket", |
| 48 | "BucketProvider", |
| 49 | "BucketAccessKey", |
| 50 | "BucketAccessKeyProvider", |
| 51 | "BucketProjectMismatchError", |
| 52 | "BucketError", |
| 53 | "AmbiguousBucketAccessKeyError", |
| 54 | "ReadBucket", |
| 55 | "ReadBucketBinding", |
| 56 | "readBucketOperations", |
| 57 | "makeReadBucketClient", |
| 58 | "WriteBucket", |
| 59 | "WriteBucketBinding", |
| 60 | "writeBucketOperations", |
| 61 | "makeWriteBucketClient", |
| 62 | "ReadWriteBucket", |
| 63 | "ReadWriteBucketBinding", |
| 64 | "makeReadWriteBucketClient", |
| 65 | "App", |
| 66 | "AppProvider", |
| 67 | "Deployment", |
| 68 | "DeploymentProvider", |
| 69 | "MAX_DEPLOYMENT_ARTIFACT_BYTES", |
| 70 | "CustomDomain", |
| 71 | "CustomDomainProvider", |
| 72 | "EnvironmentVariable", |
| 73 | "EnvironmentVariableProvider", |
| 74 | "SourceRepository", |
| 75 | "SourceRepositoryProvider", |
| 76 | "Compute", |
| 77 | "ComputeProvider", |
| 78 | "ComputeDevProvider", |
| 79 | "isCompute", |
| 80 | "KNOWN_REGION_IDS", |
| 81 | "REGIONS", |
| 82 | "PRISMA_AUTH_PROVIDER_NAME", |
| 83 | "PrismaStoredCredentials", |
| 84 | "PrismaApiDecodeError", |
| 85 | "PrismaApiError", |
| 86 | "PrismaClientLive", |
| 87 | "PrismaDeploymentFailed", |
| 88 | "PrismaDeploymentWaitInvalidOptions", |
| 89 | "PrismaDeploymentWaitTimeout", |
| 90 | "PrismaLogStreamError", |
| 91 | "listWorkspaces", |
| 92 | "getWorkspace", |
| 93 | "getCurrentPrincipal", |
| 94 | "listRegions", |
| 95 | "listPostgresRegions", |
| 96 | "listAccelerateRegions", |
| 97 | "listProjects", |
| 98 | "getProject", |
| 99 | "createProject", |
| 100 | "updateProject", |
| 101 | "deleteProject", |
| 102 | "transferProject", |
| 103 | "listDatabases", |
| 104 | "listProjectDatabases", |
| 105 | "getDatabase", |
| 106 | "createDatabase", |
| 107 | "createProjectDatabase", |
| 108 | "updateDatabase", |
| 109 | "deleteDatabase", |
| 110 | "listBackups", |
| 111 | "restoreDatabase", |
| 112 | "getDatabaseUsage", |
| 113 | "listConnections", |
| 114 | "listDatabaseConnections", |
| 115 | "getConnection", |
| 116 | "createConnection", |
| 117 | "createDatabaseConnection", |
| 118 | "deleteConnection", |
| 119 | "rotateConnection", |
| 120 | "listBranches", |
| 121 | "getBranch", |
| 122 | "createBranch", |
| 123 | "updateBranch", |
| 124 | "deleteBranch", |
| 125 | "listBuckets", |
| 126 | "getBucket", |
| 127 | "createBucket", |
| 128 | "deleteBucket", |
| 129 | "listBucketKeys", |
| 130 | "createBucketKey", |
| 131 | "deleteBucketKey", |
| 132 | "getCustomDomain", |
| 133 | "deleteCustomDomain", |
| 134 | "retryCustomDomain", |
| 135 | "listApps", |
| 136 | "getApp", |
| 137 | "createApp", |
| 138 | "updateApp", |
| 139 | "deleteApp", |
| 140 | "promoteApp", |
| 141 | "rollbackApp", |
| 142 | "listAppDomains", |
| 143 | "createAppDomain", |
| 144 | "listAppDeployments", |
| 145 | "createAppDeployment", |
| 146 | "getDeployment", |
| 147 | "deleteDeployment", |
| 148 | "startDeployment", |
| 149 | "stopDeployment", |
| 150 | "getDeploymentLogsRequest", |
| 151 | "getBuildLogsRequest", |
| 152 | "listEnvironmentVariables", |
| 153 | "getEnvironmentVariable", |
| 154 | "createEnvironmentVariable", |
| 155 | "updateEnvironmentVariable", |
| 156 | "deleteEnvironmentVariable", |
| 157 | "listIntegrations", |
| 158 | "listWorkspaceIntegrations", |
| 159 | "getIntegration", |
| 160 | "deleteIntegration", |
| 161 | "revokeWorkspaceIntegration", |
| 162 | "listScmInstallations", |
| 163 | "createScmInstallIntent", |
| 164 | "listScmInstallationRepositories", |
| 165 | "listSourceRepositories", |
| 166 | "getSourceRepository", |
| 167 | "createSourceRepository", |
| 168 | "deleteSourceRepository", |
| 169 | "COMPUTE_MANIFEST_VERSION", |
| 170 | "createComputeArchive", |
| 171 | "normalizeEntrypoint", |
| 172 | "runBuildCommand", |
| 173 | "runComputeAutoBuild", |
| 174 | "runComputeStaticBuild", |
| 175 | "parseDeploymentLogRecord", |
| 176 | "tailDeploymentLogs", |
| 177 | "waitForDeploymentStatus", |
| 178 | "waitForDeploymentUrl", |
| 179 | "destroyDeployment", |
| 180 | "destroyApp", |
| 181 | "destroyProjectApps", |
| 182 | "toDeploymentUrl", |
| 183 | "syncComputeEnvironment", |
| 184 | "readUploadArtifact", |
| 185 | "uploadArtifact", |
| 186 | "validateDeploymentArtifactBytes", |
| 187 | "extractConnectionSecrets", |
| 188 | "requestBody", |
| 189 | "isNotFound", |
| 190 | "isConflict", |
| 191 | "fromProfile", |
| 192 | ] as const; |
| 193 | |
| 194 | const publicPrismaDeepImports = [ |
| 195 | "App", |
| 196 | "AuthProvider", |
| 197 | "Branch", |
| 198 | "Bucket", |
| 199 | "BucketBinding", |
| 200 | "BucketAccessKey", |
| 201 | "BucketTypes", |
| 202 | "Client", |
| 203 | "Compute", |
| 204 | "ComputeArchive", |
| 205 | "ComputeBuild", |
| 206 | "ComputeLifecycle", |
| 207 | "Connect", |
| 208 | "Connection", |
| 209 | "CustomDomain", |
| 210 | "Database", |
| 211 | "Deployment", |
| 212 | "EnvironmentVariable", |
| 213 | "Operations", |
| 214 | "Postgres", |
| 215 | "PostgresOrigin", |
| 216 | "PrismaEnvironment", |
| 217 | "PrismaLogs", |
| 218 | "Project", |
| 219 | "Providers", |
| 220 | "ReadBucket", |
| 221 | "ReadWriteBucket", |
| 222 | "SourceRepository", |
| 223 | "Types", |
| 224 | "WriteBucket", |
| 225 | ] as const; |
| 226 | |
| 227 | const internalPrismaDeepImports = [ |
| 228 | "Credentials", |
| 229 | "Internal/DeploymentActions", |
| 230 | "Internal/DeploymentObserve", |
| 231 | "PrismaDevDatabase", |
| 232 | "Refs", |
| 233 | ] as const; |
| 234 | |
| 235 | const internalPrismaRootFiles = [ |
| 236 | "Credentials", |
| 237 | "PrismaDevDatabase", |
| 238 | "Refs", |
| 239 | ] as const; |
| 240 | |
| 241 | const removedPrismaDeepImports = [ |
| 242 | "ComputeApp", |
| 243 | "ComputeService", |
| 244 | "ComputeVersion", |
| 245 | "ComputeVersionObserve", |
| 246 | ] as const; |
| 247 | |
| 248 | const importPackagePath = (specifier: string) => |
| 249 | import(/* @vite-ignore */ specifier); |
| 250 | |
| 251 | describe( |
| 252 | "Prisma public surface", |
| 253 | { tags: ["unit", "provider:prisma", "local"] }, |
| 254 | () => { |
| 255 | it("exports the user-facing resources, providers, and operations", () => { |
| 256 | const expected = [...publicExports].sort(); |
| 257 | |
| 258 | expect(Object.keys(Prisma).sort()).toEqual(expected); |
| 259 | expect(Prisma.KNOWN_REGION_IDS).toContain("us-east-1"); |
| 260 | expect(Prisma.REGIONS[0]).toEqual({ |
| 261 | id: Prisma.KNOWN_REGION_IDS[0], |
| 262 | displayName: Prisma.KNOWN_REGION_IDS[0], |
| 263 | }); |
| 264 | }); |
| 265 | |
| 266 | it("exports the same surface through the alchemy/Prisma package path", () => { |
| 267 | const expected = [...publicExports].sort(); |
| 268 | |
| 269 | expect(Object.keys(PrismaPackage).sort()).toEqual(expected); |
| 270 | }); |
| 271 | |
| 272 | it( |
| 273 | "supports deep Prisma package path imports", |
| 274 | async () => { |
| 275 | for (const moduleName of publicPrismaDeepImports) { |
| 276 | const module = await importPackagePath( |
| 277 | `alchemy/Prisma/${moduleName}`, |
| 278 | ); |
| 279 | expect(Object.keys(module as object).length).toBeGreaterThan(0); |
| 280 | } |
| 281 | |
| 282 | const [compute, operations, client, archive, types, postgres] = |
| 283 | await Promise.all([ |
| 284 | import("alchemy/Prisma/Compute"), |
| 285 | import("alchemy/Prisma/Operations"), |
| 286 | import("alchemy/Prisma/Client"), |
| 287 | import("alchemy/Prisma/ComputeArchive"), |
| 288 | import("alchemy/Prisma/Types"), |
| 289 | import("alchemy/Prisma/Postgres"), |
| 290 | ]); |
| 291 | |
| 292 | expect(compute.Compute.Type).toBe("Prisma.Compute"); |
| 293 | expect(typeof operations.listProjects).toBe("function"); |
| 294 | expect(typeof client.PrismaApiError).toBe("function"); |
| 295 | expect(archive.COMPUTE_MANIFEST_VERSION).toBe("1"); |
| 296 | expect(types.KNOWN_REGION_IDS).toContain("us-east-1"); |
| 297 | expect(postgres.Postgres.Type).toBe("Prisma.Database"); |
| 298 | expect(Prisma.Postgres).toBe(Prisma.Database); |
| 299 | }, |
| 300 | { tags: ["provider:prisma:database"] }, |
| 301 | ); |
| 302 | |
| 303 | it("does not expose removed Compute deep imports", async () => { |
| 304 | for (const moduleName of removedPrismaDeepImports) { |
| 305 | await expect( |
| 306 | importPackagePath(`alchemy/Prisma/${moduleName}`), |
| 307 | ).rejects.toThrow(); |
| 308 | } |
| 309 | }); |
| 310 | |
| 311 | it("does not expose internal Prisma deep imports", async () => { |
| 312 | for (const moduleName of internalPrismaDeepImports) { |
| 313 | await expect( |
| 314 | importPackagePath(`alchemy/Prisma/${moduleName}`), |
| 315 | ).rejects.toThrow(); |
| 316 | } |
| 317 | }); |
| 318 | |
| 319 | it.effect("keeps Prisma root modules intentionally classified", () => |
| 320 | Effect.gen(function* () { |
| 321 | const fs = yield* FileSystem.FileSystem; |
| 322 | const path = yield* Path.Path; |
| 323 | const prismaSourceRoot = path.resolve( |
| 324 | import.meta.dirname, |
| 325 | "../../src/Prisma", |
| 326 | ); |
| 327 | const files = (yield* fs.readDirectory(prismaSourceRoot)) |
| 328 | .filter((file) => file.endsWith(".ts")) |
| 329 | .sort(); |
| 330 | |
| 331 | expect(files).toEqual( |
| 332 | [ |
| 333 | "index.ts", |
| 334 | ...publicPrismaDeepImports.map((moduleName) => `${moduleName}.ts`), |
| 335 | ...internalPrismaRootFiles.map((moduleName) => `${moduleName}.ts`), |
| 336 | ].sort(), |
| 337 | ); |
| 338 | }).pipe(Effect.provide(NodeServices.layer)), |
| 339 | ); |
| 340 | |
| 341 | it.effect( |
| 342 | "blocks internal Prisma deep imports in the package export map", |
| 343 | () => |
| 344 | Effect.gen(function* () { |
| 345 | const fs = yield* FileSystem.FileSystem; |
| 346 | const path = yield* Path.Path; |
| 347 | const packageJsonPath = path.resolve( |
| 348 | import.meta.dirname, |
| 349 | "../../package.json", |
| 350 | ); |
| 351 | const packageJson = JSON.parse( |
| 352 | yield* fs.readFileString(packageJsonPath), |
| 353 | ); |
| 354 | |
| 355 | for (const moduleName of internalPrismaDeepImports) { |
| 356 | const exportPath = moduleName.startsWith("Internal/") |
| 357 | ? "./Prisma/Internal/*" |
| 358 | : `./Prisma/${moduleName}`; |
| 359 | expect(packageJson.exports[exportPath]).toBeNull(); |
| 360 | } |
| 361 | }).pipe(Effect.provide(NodeServices.layer)), |
| 362 | ); |
| 363 | |
| 364 | it.effect("pins Prisma package export map entries", () => |
| 365 | Effect.gen(function* () { |
| 366 | const fs = yield* FileSystem.FileSystem; |
| 367 | const path = yield* Path.Path; |
| 368 | const packageJsonPath = path.resolve( |
| 369 | import.meta.dirname, |
| 370 | "../../package.json", |
| 371 | ); |
| 372 | const packageJson = JSON.parse( |
| 373 | yield* fs.readFileString(packageJsonPath), |
| 374 | ); |
| 375 | |
| 376 | expect(packageJson.exports["./Prisma"]).toBe("./src/Prisma/index.ts"); |
| 377 | expect(packageJson.exports["./Prisma/ORM"]).toBe( |
| 378 | "./src/Prisma/ORM/index.ts", |
| 379 | ); |
| 380 | // Deep Prisma modules use the package-wide source export wildcard. |
| 381 | expect(packageJson.exports["./Prisma/*"]).toBeUndefined(); |
| 382 | expect(packageJson.exports["./*"]).toBe("./src/*.ts"); |
| 383 | for (const moduleName of removedPrismaDeepImports) { |
| 384 | expect(packageJson.exports[`./Prisma/${moduleName}`]).toBeNull(); |
| 385 | } |
| 386 | }).pipe(Effect.provide(NodeServices.layer)), |
| 387 | ); |
| 388 | }, |
| 389 | ); |