@prisma/compute-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +638 -0
  2. package/dist/api-client.d.ts +181 -0
  3. package/dist/api-client.d.ts.map +1 -0
  4. package/dist/api-client.js +193 -0
  5. package/dist/api-client.js.map +1 -0
  6. package/dist/archive.d.ts +11 -0
  7. package/dist/archive.d.ts.map +1 -0
  8. package/dist/archive.js +80 -0
  9. package/dist/archive.js.map +1 -0
  10. package/dist/build-strategy.d.ts +34 -0
  11. package/dist/build-strategy.d.ts.map +1 -0
  12. package/dist/build-strategy.js +33 -0
  13. package/dist/build-strategy.js.map +1 -0
  14. package/dist/bun-build.d.ts +14 -0
  15. package/dist/bun-build.d.ts.map +1 -0
  16. package/dist/bun-build.js +128 -0
  17. package/dist/bun-build.js.map +1 -0
  18. package/dist/callbacks.d.ts +44 -0
  19. package/dist/callbacks.d.ts.map +1 -0
  20. package/dist/callbacks.js +2 -0
  21. package/dist/callbacks.js.map +1 -0
  22. package/dist/compute-client.d.ts +136 -0
  23. package/dist/compute-client.d.ts.map +1 -0
  24. package/dist/compute-client.js +562 -0
  25. package/dist/compute-client.js.map +1 -0
  26. package/dist/errors.d.ts +101 -0
  27. package/dist/errors.d.ts.map +1 -0
  28. package/dist/errors.js +56 -0
  29. package/dist/errors.js.map +1 -0
  30. package/dist/index.d.ts +12 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +10 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/polling.d.ts +22 -0
  35. package/dist/polling.d.ts.map +1 -0
  36. package/dist/polling.js +64 -0
  37. package/dist/polling.js.map +1 -0
  38. package/dist/types.d.ts +46 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +17 -0
  41. package/dist/types.js.map +1 -0
  42. package/package.json +43 -0
  43. package/src/api-client.ts +330 -0
  44. package/src/archive.ts +111 -0
  45. package/src/build-strategy.ts +66 -0
  46. package/src/bun-build.ts +184 -0
  47. package/src/callbacks.ts +50 -0
  48. package/src/compute-client.ts +985 -0
  49. package/src/errors.ts +148 -0
  50. package/src/index.ts +75 -0
  51. package/src/polling.ts +108 -0
  52. package/src/types.ts +65 -0
package/README.md ADDED
@@ -0,0 +1,638 @@
1
+ # @prisma/compute-sdk
2
+
3
+ TypeScript SDK for deploying and managing applications on [Prisma Compute](https://www.prisma.io/blog/prisma-compute).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @prisma/compute-sdk @prisma/management-api-sdk
9
+ ```
10
+
11
+ `@prisma/management-api-sdk` is a peer dependency that provides the authenticated API client.
12
+
13
+ ## Prerequisites
14
+
15
+ You need an authenticated `ManagementApiClient` from `@prisma/management-api-sdk`. There are two ways to create one:
16
+
17
+ ### Using a service token
18
+
19
+ ```ts
20
+ import { createManagementApiClient } from "@prisma/management-api-sdk";
21
+
22
+ const apiClient = createManagementApiClient({
23
+ token: process.env.PRISMA_API_TOKEN,
24
+ });
25
+ ```
26
+
27
+ ### Using OAuth
28
+
29
+ ```ts
30
+ import { createManagementApiSdk } from "@prisma/management-api-sdk";
31
+
32
+ const sdk = createManagementApiSdk({
33
+ clientId: "your-client-id",
34
+ redirectUri: "http://localhost:3000/callback",
35
+ tokenStorage: yourTokenStorageImpl, // implements TokenStorage interface
36
+ });
37
+
38
+ // sdk.client is a ManagementApiClient with automatic token refresh
39
+ const apiClient = sdk.client;
40
+ ```
41
+
42
+ See the [`@prisma/management-api-sdk` documentation](https://www.npmjs.com/package/@prisma/management-api-sdk) for full details on authentication setup.
43
+
44
+ ## Quick start
45
+
46
+ ```ts
47
+ import { ComputeClient, PreBuilt, Ok } from "@prisma/compute-sdk";
48
+ import { createManagementApiClient } from "@prisma/management-api-sdk";
49
+
50
+ const apiClient = createManagementApiClient({
51
+ token: process.env.PRISMA_API_TOKEN,
52
+ });
53
+
54
+ const compute = new ComputeClient(apiClient);
55
+
56
+ // Deploy a pre-built application
57
+ const result = await compute.deploy({
58
+ strategy: new PreBuilt({
59
+ appPath: "./dist",
60
+ entrypoint: "index.js",
61
+ }),
62
+ projectId: "your-project-id",
63
+ serviceName: "my-app",
64
+ region: "us-east-1",
65
+ });
66
+
67
+ if (result.isOk()) {
68
+ console.log(`Deployed to ${result.value.deploymentUrl}`);
69
+ } else {
70
+ console.error(`Deploy failed: ${result.error.message}`);
71
+ }
72
+ ```
73
+
74
+ ## API reference
75
+
76
+ ### `ComputeClient`
77
+
78
+ The main entry point for all operations. Created from a `ManagementApiClient`:
79
+
80
+ ```ts
81
+ import { ComputeClient } from "@prisma/compute-sdk";
82
+
83
+ const compute = new ComputeClient(apiClient);
84
+ ```
85
+
86
+ All methods return `Promise<Result<T, ComputeError>>` — a discriminated union that is either `Ok` with a value or `Err` with a typed error. See [Error handling](#error-handling) for details.
87
+
88
+ ---
89
+
90
+ #### `deploy(options): Promise<Result<DeployResult, ComputeError>>`
91
+
92
+ Builds, uploads, and deploys an application version.
93
+
94
+ ```ts
95
+ const result = await compute.deploy({
96
+ // Required: how to produce the deployable artifact
97
+ strategy: new PreBuilt({ appPath: "./dist", entrypoint: "index.js" }),
98
+
99
+ // Target (provide serviceId OR projectId + serviceName + region)
100
+ projectId: "proj_abc",
101
+ serviceName: "my-app",
102
+ region: "us-east-1",
103
+ // OR:
104
+ serviceId: "svc_xyz",
105
+
106
+ // Optional
107
+ envVars: { DATABASE_URL: "postgresql://..." },
108
+ portMapping: { http: 3000 },
109
+ timeoutSeconds: 120, // max time to wait for "running" status
110
+ pollIntervalMs: 1000, // how often to check status
111
+ signal: abortController.signal,
112
+ progress: { /* DeployProgress callbacks */ },
113
+ interaction: { /* DeployInteraction callbacks */ },
114
+ });
115
+
116
+ if (result.isOk()) {
117
+ const { deploymentUrl, versionId, serviceId, resolvedConfig } = result.value;
118
+ }
119
+ ```
120
+
121
+ **Returns** `DeployResult`:
122
+
123
+ | Field | Type | Description |
124
+ | ---------------- | ---------------- | ---------------------------------------- |
125
+ | `projectId` | `string` | Project ID |
126
+ | `serviceId` | `string` | Service ID |
127
+ | `serviceName` | `string` | Service display name |
128
+ | `region` | `string` | Region identifier |
129
+ | `versionId` | `string` | Created version ID |
130
+ | `deploymentUrl` | `string` | Live URL of the deployment |
131
+ | `resolvedConfig` | `ResolvedConfig` | Final resolved configuration |
132
+
133
+ ---
134
+
135
+ #### `updateEnv(options): Promise<Result<UpdateEnvResult, ComputeError>>`
136
+
137
+ Creates a new version with updated environment variables and/or port mapping, reusing the code from the most recent deployment.
138
+
139
+ ```ts
140
+ const result = await compute.updateEnv({
141
+ serviceId: "svc_xyz",
142
+ envVars: { DATABASE_URL: "postgresql://new-url..." },
143
+ portMapping: { http: 8080 },
144
+ });
145
+ ```
146
+
147
+ The service must have at least one existing version. If not, this returns a `NoExistingVersionError`.
148
+
149
+ ---
150
+
151
+ #### `destroyVersion(options): Promise<Result<DestroyVersionResult, ComputeError>>`
152
+
153
+ Stops (if running) and deletes a single version.
154
+
155
+ ```ts
156
+ const result = await compute.destroyVersion({
157
+ versionId: "ver_abc",
158
+ // OR provide serviceId + interaction.selectVersion for interactive selection
159
+ });
160
+ ```
161
+
162
+ **Returns** `DestroyVersionResult`:
163
+
164
+ | Field | Type | Description |
165
+ | ---------------- | --------- | ---------------------------------------- |
166
+ | `versionId` | `string` | The destroyed version ID |
167
+ | `previousStatus` | `string` | Status before destruction |
168
+ | `stopped` | `boolean` | Whether a stop was required |
169
+ | `deleted` | `boolean` | Whether deletion succeeded |
170
+
171
+ ---
172
+
173
+ #### `destroyService(options): Promise<Result<DestroyServiceResult, ComputeError>>`
174
+
175
+ Stops all running versions, deletes all versions, and optionally deletes the service itself.
176
+
177
+ ```ts
178
+ const result = await compute.destroyService({
179
+ serviceId: "svc_xyz",
180
+ keepService: false, // set to true to keep the service record
181
+ });
182
+ ```
183
+
184
+ If some versions fail to stop or delete, returns a `DestroyAggregateError` with details on which succeeded and which failed.
185
+
186
+ ---
187
+
188
+ #### `listProjects(options?): Promise<Result<ProjectInfo[], ComputeError>>`
189
+
190
+ ```ts
191
+ const result = await compute.listProjects();
192
+ if (result.isOk()) {
193
+ for (const project of result.value) {
194
+ console.log(`${project.name} (${project.id})`);
195
+ }
196
+ }
197
+ ```
198
+
199
+ ---
200
+
201
+ #### `listServices(options): Promise<Result<ServiceInfo[], ComputeError>>`
202
+
203
+ ```ts
204
+ const result = await compute.listServices({ projectId: "proj_abc" });
205
+ ```
206
+
207
+ ---
208
+
209
+ #### `createService(options): Promise<Result<ServiceInfo, ComputeError>>`
210
+
211
+ ```ts
212
+ const result = await compute.createService({
213
+ projectId: "proj_abc",
214
+ serviceName: "my-new-service",
215
+ region: "eu-west-3",
216
+ });
217
+ ```
218
+
219
+ ---
220
+
221
+ #### `showService(options): Promise<Result<ServiceDetail, ComputeError>>`
222
+
223
+ ```ts
224
+ const result = await compute.showService({ serviceId: "svc_xyz" });
225
+ if (result.isOk()) {
226
+ console.log(`Latest version: ${result.value.latestVersionId}`);
227
+ }
228
+ ```
229
+
230
+ ---
231
+
232
+ #### `deleteService(options): Promise<Result<void, ComputeError>>`
233
+
234
+ Deletes a service record. The service should have no versions (use `destroyService` to clean up versions first).
235
+
236
+ ```ts
237
+ await compute.deleteService({ serviceId: "svc_xyz" });
238
+ ```
239
+
240
+ ---
241
+
242
+ #### `listVersions(options): Promise<Result<VersionInfo[], ComputeError>>`
243
+
244
+ ```ts
245
+ const result = await compute.listVersions({ serviceId: "svc_xyz" });
246
+ ```
247
+
248
+ ---
249
+
250
+ #### `showVersion(options): Promise<Result<VersionDetail, ComputeError>>`
251
+
252
+ ```ts
253
+ const result = await compute.showVersion({ versionId: "ver_abc" });
254
+ if (result.isOk()) {
255
+ console.log(`Status: ${result.value.status}`);
256
+ console.log(`URL: https://${result.value.previewDomain}`);
257
+ }
258
+ ```
259
+
260
+ ---
261
+
262
+ #### `startVersion(options): Promise<Result<void, ComputeError>>`
263
+
264
+ ```ts
265
+ await compute.startVersion({ versionId: "ver_abc" });
266
+ ```
267
+
268
+ ---
269
+
270
+ #### `stopVersion(options): Promise<Result<void, ComputeError>>`
271
+
272
+ ```ts
273
+ await compute.stopVersion({ versionId: "ver_abc" });
274
+ ```
275
+
276
+ ---
277
+
278
+ #### `deleteVersion(options): Promise<Result<void, ComputeError>>`
279
+
280
+ ```ts
281
+ await compute.deleteVersion({ versionId: "ver_abc" });
282
+ ```
283
+
284
+ ## Build strategies
285
+
286
+ A build strategy produces a deployable artifact (a directory with an entrypoint file). The SDK ships with two built-in strategies:
287
+
288
+ ### `PreBuilt`
289
+
290
+ Use when your application is already built (e.g., output of `tsc`, `esbuild`, or any other bundler):
291
+
292
+ ```ts
293
+ import { PreBuilt } from "@prisma/compute-sdk";
294
+
295
+ const strategy = new PreBuilt({
296
+ appPath: "./dist", // absolute or relative path to the build output
297
+ entrypoint: "index.js", // relative to appPath
298
+ });
299
+ ```
300
+
301
+ `PreBuilt` validates that the entrypoint exists and is a relative path that doesn't escape the application directory. It performs no copying or transformation.
302
+
303
+ ### `BunBuild`
304
+
305
+ Use when you want the SDK to bundle your application using [Bun](https://bun.sh):
306
+
307
+ ```ts
308
+ import { BunBuild } from "@prisma/compute-sdk";
309
+
310
+ const strategy = new BunBuild({
311
+ appPath: "./my-app", // path to your application source
312
+ entrypoint: "src/index.ts", // optional: resolved from package.json "main" if omitted
313
+ });
314
+ ```
315
+
316
+ `BunBuild` runs `bun build` with `--target bun --sourcemap=external`, manages a temporary output directory, and cleans it up after the archive is created. Requires Bun to be installed on the machine.
317
+
318
+ ### Custom strategies
319
+
320
+ Implement the `BuildStrategy` interface to use any build tool:
321
+
322
+ ```ts
323
+ import type { BuildStrategy, BuildArtifact } from "@prisma/compute-sdk";
324
+
325
+ class MyCustomBuild implements BuildStrategy {
326
+ async execute(): Promise<BuildArtifact> {
327
+ // Run your build process...
328
+ return {
329
+ directory: "/path/to/output", // absolute path to the built files
330
+ entrypoint: "index.js", // relative to directory, posix separators
331
+ cleanup: async () => { // optional: called after archiving
332
+ // clean up temp files
333
+ },
334
+ };
335
+ }
336
+ }
337
+ ```
338
+
339
+ ## Error handling
340
+
341
+ All `ComputeClient` methods return `Result<T, E>` from the [`better-result`](https://www.npmjs.com/package/better-result) library instead of throwing exceptions. This gives you exhaustive, type-safe error handling.
342
+
343
+ ### Checking results
344
+
345
+ ```ts
346
+ const result = await compute.deploy({ /* ... */ });
347
+
348
+ // Pattern 1: isOk / isErr
349
+ if (result.isOk()) {
350
+ console.log(result.value.deploymentUrl);
351
+ } else {
352
+ console.error(result.error.message);
353
+ }
354
+
355
+ // Pattern 2: match
356
+ result.match({
357
+ Ok: (value) => console.log(value.deploymentUrl),
358
+ Err: (error) => console.error(error.message),
359
+ });
360
+ ```
361
+
362
+ ### Error types
363
+
364
+ Every error extends `TaggedError` and has a `_tag` discriminant for pattern matching:
365
+
366
+ | Error class | `_tag` | Description |
367
+ | ------------------------ | ------------------------- | ------------------------------------------------------------- |
368
+ | `AuthenticationError` | `"AuthenticationError"` | API returned HTTP 401 |
369
+ | `ApiError` | `"ApiError"` | API returned a non-401 error |
370
+ | `MissingArgumentError` | `"MissingArgumentError"` | A required argument was not provided |
371
+ | `BuildError` | `"BuildError"` | Build strategy failed |
372
+ | `ArtifactError` | `"ArtifactError"` | Archive creation or upload failed |
373
+ | `TimeoutError` | `"TimeoutError"` | Version didn't reach target status in time |
374
+ | `VersionFailedError` | `"VersionFailedError"` | Version transitioned to `"failed"` status |
375
+ | `NoExistingVersionError` | `"NoExistingVersionError"`| `updateEnv` called on a service with no prior deployments |
376
+ | `CancelledError` | `"CancelledError"` | Operation cancelled via `AbortSignal` |
377
+ | `DestroyAggregateError` | `"DestroyAggregateError"` | Some versions failed during `destroyService` |
378
+
379
+ ### Matching specific errors
380
+
381
+ ```ts
382
+ import { matchError, ApiError, AuthenticationError } from "@prisma/compute-sdk";
383
+
384
+ const result = await compute.deploy({ /* ... */ });
385
+
386
+ if (result.isErr()) {
387
+ matchError(result.error, {
388
+ AuthenticationError: (e) => {
389
+ console.error("Not authenticated. Check your token.");
390
+ },
391
+ ApiError: (e) => {
392
+ console.error(`API error (${e.statusCode}): ${e.message}`);
393
+ if (e.hint) console.error(`Hint: ${e.hint}`);
394
+ },
395
+ BuildError: (e) => {
396
+ console.error(`Build failed: ${e.message}`);
397
+ },
398
+ TimeoutError: (e) => {
399
+ console.error(`Timed out after ${Math.round(e.elapsedMs / 1000)}s`);
400
+ },
401
+ _: (e) => {
402
+ console.error(`Unexpected error: ${e.message}`);
403
+ },
404
+ });
405
+ }
406
+ ```
407
+
408
+ ### Error type unions
409
+
410
+ The SDK exports narrowed error unions for each operation:
411
+
412
+ - **`DeployError`** — errors from `deploy()`: `AuthenticationError | ApiError | MissingArgumentError | BuildError | ArtifactError | TimeoutError | VersionFailedError | CancelledError`
413
+ - **`UpdateEnvError`** — errors from `updateEnv()`: `AuthenticationError | ApiError | MissingArgumentError | NoExistingVersionError | TimeoutError | VersionFailedError | CancelledError`
414
+ - **`DestroyError`** — errors from `destroyVersion()` / `destroyService()`: `AuthenticationError | ApiError | MissingArgumentError | CancelledError | DestroyAggregateError`
415
+ - **`ComputeError`** — union of all error types
416
+
417
+ ## Progress and interaction callbacks
418
+
419
+ Long-running operations accept `progress` and `interaction` callbacks for UI integration.
420
+
421
+ ### Deploy progress
422
+
423
+ ```ts
424
+ await compute.deploy({
425
+ strategy,
426
+ projectId: "proj_abc",
427
+ serviceName: "my-app",
428
+ region: "us-east-1",
429
+ progress: {
430
+ onBuildStart() {
431
+ console.log("Building...");
432
+ },
433
+ onBuildComplete(artifact) {
434
+ console.log(`Built to ${artifact.directory}`);
435
+ },
436
+ onArchiveCreating() {
437
+ console.log("Creating archive...");
438
+ },
439
+ onArchiveReady(sizeBytes) {
440
+ console.log(`Archive: ${(sizeBytes / 1024).toFixed(1)} KB`);
441
+ },
442
+ onVersionCreated(versionId) {
443
+ console.log(`Version: ${versionId}`);
444
+ },
445
+ onUploadStart() {
446
+ console.log("Uploading...");
447
+ },
448
+ onUploadComplete() {
449
+ console.log("Upload complete.");
450
+ },
451
+ onStartRequested() {
452
+ console.log("Starting...");
453
+ },
454
+ onStatusChange(status) {
455
+ console.log(`Status: ${status}`);
456
+ },
457
+ onRunning(deploymentUrl) {
458
+ console.log(`Live at ${deploymentUrl}`);
459
+ },
460
+ },
461
+ });
462
+ ```
463
+
464
+ ### Deploy interaction
465
+
466
+ When `projectId`, `serviceId`, or `region` are not provided, the SDK calls interaction callbacks to let the consumer resolve them (e.g., by prompting the user):
467
+
468
+ ```ts
469
+ await compute.deploy({
470
+ strategy,
471
+ interaction: {
472
+ async selectProject(projects) {
473
+ // projects: ProjectInfo[] — return the chosen project ID
474
+ return projects[0].id;
475
+ },
476
+ async selectService(services) {
477
+ // services: ServiceInfo[] — return a service ID, or null to create a new one
478
+ return null;
479
+ },
480
+ async provideServiceName() {
481
+ // return a name for the new service
482
+ return "my-new-service";
483
+ },
484
+ async selectRegion(regions) {
485
+ // regions: RegionInfo[] — return the chosen region ID
486
+ return "us-east-1";
487
+ },
488
+ },
489
+ });
490
+ ```
491
+
492
+ ### Destroy progress
493
+
494
+ ```ts
495
+ await compute.destroyService({
496
+ serviceId: "svc_xyz",
497
+ progress: {
498
+ onStoppingVersions(versionIds) { /* ... */ },
499
+ onVersionStopped(versionId) { /* ... */ },
500
+ onAllVersionsStopped() { /* ... */ },
501
+ onDeletingVersions(versionIds) { /* ... */ },
502
+ onVersionDeleted(versionId) { /* ... */ },
503
+ onAllVersionsDeleted() { /* ... */ },
504
+ onServiceDeleted(serviceId) { /* ... */ },
505
+ },
506
+ });
507
+ ```
508
+
509
+ ## Cancellation
510
+
511
+ All operations support cancellation via the standard `AbortSignal`:
512
+
513
+ ```ts
514
+ const controller = new AbortController();
515
+
516
+ // Cancel after 30 seconds
517
+ setTimeout(() => controller.abort(), 30_000);
518
+
519
+ const result = await compute.deploy({
520
+ strategy,
521
+ serviceId: "svc_xyz",
522
+ signal: controller.signal,
523
+ });
524
+
525
+ if (result.isErr() && result.error._tag === "CancelledError") {
526
+ console.log("Deployment was cancelled.");
527
+ }
528
+ ```
529
+
530
+ ## Domain types
531
+
532
+ ```ts
533
+ import type {
534
+ ProjectInfo, // { id, name, defaultRegion? }
535
+ ServiceInfo, // { id, name, region, projectId, createdAt? }
536
+ ServiceDetail, // ServiceInfo & { latestVersionId? }
537
+ VersionInfo, // { id, status, createdAt, previewDomain? }
538
+ VersionDetail, // VersionInfo & { envVars? }
539
+ RegionInfo, // { id, displayName }
540
+ ResolvedConfig, // { projectId, serviceId, serviceName, region, portMapping? }
541
+ PortMapping, // { http?: number | null }
542
+ } from "@prisma/compute-sdk";
543
+ ```
544
+
545
+ ### Available regions
546
+
547
+ ```ts
548
+ import { REGIONS, KNOWN_REGION_IDS } from "@prisma/compute-sdk";
549
+
550
+ // KNOWN_REGION_IDS: readonly ["us-east-1", "us-west-1", "eu-west-3", "eu-central-1", "ap-northeast-1", "ap-southeast-1"]
551
+ // REGIONS: RegionInfo[] — same IDs with displayName
552
+ ```
553
+
554
+ ## Full example
555
+
556
+ ```ts
557
+ import { ComputeClient, PreBuilt, matchError } from "@prisma/compute-sdk";
558
+ import { createManagementApiClient } from "@prisma/management-api-sdk";
559
+
560
+ async function main() {
561
+ const apiClient = createManagementApiClient({
562
+ token: process.env.PRISMA_API_TOKEN!,
563
+ });
564
+
565
+ const compute = new ComputeClient(apiClient);
566
+
567
+ // Deploy
568
+ const deployResult = await compute.deploy({
569
+ strategy: new PreBuilt({
570
+ appPath: "./dist",
571
+ entrypoint: "server.js",
572
+ }),
573
+ projectId: process.env.PROJECT_ID!,
574
+ serviceName: "my-api",
575
+ region: "us-east-1",
576
+ envVars: {
577
+ DATABASE_URL: process.env.DATABASE_URL!,
578
+ NODE_ENV: "production",
579
+ },
580
+ portMapping: { http: 3000 },
581
+ progress: {
582
+ onBuildStart: () => console.log("Preparing artifact..."),
583
+ onUploadStart: () => console.log("Uploading..."),
584
+ onStartRequested: () => console.log("Starting..."),
585
+ onStatusChange: (s) => console.log(` Status: ${s}`),
586
+ onRunning: (url) => console.log(`Deployed: ${url}`),
587
+ },
588
+ });
589
+
590
+ if (deployResult.isErr()) {
591
+ matchError(deployResult.error, {
592
+ AuthenticationError: () => {
593
+ console.error("Invalid token. Set PRISMA_API_TOKEN.");
594
+ process.exit(1);
595
+ },
596
+ BuildError: (e) => {
597
+ console.error(`Build failed: ${e.message}`);
598
+ process.exit(1);
599
+ },
600
+ TimeoutError: (e) => {
601
+ console.error(`Deploy timed out (${Math.round(e.elapsedMs / 1000)}s).`);
602
+ console.error(`Version ${e.versionId} may still be starting.`);
603
+ process.exit(1);
604
+ },
605
+ _: (e) => {
606
+ console.error(`Error: ${e.message}`);
607
+ process.exit(1);
608
+ },
609
+ });
610
+ return;
611
+ }
612
+
613
+ const { serviceId, versionId, deploymentUrl } = deployResult.value;
614
+ console.log(`\nService: ${serviceId}`);
615
+ console.log(`Version: ${versionId}`);
616
+ console.log(`URL: ${deploymentUrl}`);
617
+
618
+ // List versions
619
+ const versionsResult = await compute.listVersions({ serviceId });
620
+ if (versionsResult.isOk()) {
621
+ console.log(`\nVersions (${versionsResult.value.length}):`);
622
+ for (const v of versionsResult.value) {
623
+ console.log(` ${v.id} — ${v.status} (${v.createdAt})`);
624
+ }
625
+ }
626
+ }
627
+
628
+ main();
629
+ ```
630
+
631
+ ## Requirements
632
+
633
+ - Node.js >= 18.0.0
634
+ - `@prisma/management-api-sdk` >= 1.20.1
635
+
636
+ ## License
637
+
638
+ Apache-2.0