1import * as Prisma from "@/Prisma";
2import * as PrismaPackage from "alchemy/Prisma";
3import * as NodeServices from "@effect/platform-node/NodeServices";
4import { describe, expect, it } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import * as FileSystem from "effect/FileSystem";
7import * as Path from "effect/Path";
8
9const 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
194const 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
227const internalPrismaDeepImports = [
228 "Credentials",
229 "Internal/DeploymentActions",
230 "Internal/DeploymentObserve",
231 "PrismaDevDatabase",
232 "Refs",
233] as const;
234
235const internalPrismaRootFiles = [
236 "Credentials",
237 "PrismaDevDatabase",
238 "Refs",
239] as const;
240
241const removedPrismaDeepImports = [
242 "ComputeApp",
243 "ComputeService",
244 "ComputeVersion",
245 "ComputeVersionObserve",
246] as const;
247
248const importPackagePath = (specifier: string) =>
249 import(/* @vite-ignore */ specifier);
250
251describe(
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);