@prisma/compute-sdk 0.1.0 → 0.3.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.
@@ -1 +1 @@
1
- {"version":3,"file":"polling.js","sourceRoot":"","sources":["../src/polling.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"polling.js","sourceRoot":"","sources":["../src/polling.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -9,6 +9,15 @@ export interface ProjectInfo {
9
9
  name: string;
10
10
  defaultRegion?: string;
11
11
  }
12
+ export interface DatabaseInfo {
13
+ id: string;
14
+ name: string;
15
+ region: string;
16
+ connectionString?: string;
17
+ }
18
+ export interface CreateProjectResult extends ProjectInfo {
19
+ database?: DatabaseInfo;
20
+ }
12
21
  export interface ServiceInfo {
13
22
  id: string;
14
23
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IACxC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,sGAOnB,CAAC;AAEX,eAAO,MAAM,OAAO,EAAE,UAAU,EAG7B,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAoB,SAAQ,WAAW;IACtD,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IACxC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,sGAOnB,CAAC;AAEX,eAAO,MAAM,OAAO,EAAE,UAAU,EAG7B,CAAC"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAiDH,iFAAiF;AACjF,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,WAAW;IACX,WAAW;IACX,WAAW;IACX,cAAc;IACd,gBAAgB;IAChB,gBAAgB;CACR,CAAC;AAEX,MAAM,CAAC,MAAM,OAAO,GAAiB,gBAAgB,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IACjE,EAAE;IACF,WAAW,EAAE,EAAE;CAChB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AA4DH,iFAAiF;AACjF,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,WAAW;IACX,WAAW;IACX,WAAW;IACX,cAAc;IACd,gBAAgB;IAChB,gBAAgB;CACR,CAAC;AAEX,MAAM,CAAC,MAAM,OAAO,GAAiB,gBAAgB,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IACjE,EAAE;IACF,WAAW,EAAE,EAAE;CAChB,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "@prisma/compute-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "TypeScript SDK for deploying and managing applications on Prisma Compute",
5
5
  "license": "Apache-2.0",
6
+ "repository": "prisma/project-compute",
6
7
  "type": "module",
7
8
  "exports": {
8
9
  ".": {
10
+ "bun": "./src/index.ts",
9
11
  "import": {
10
12
  "types": "./dist/index.d.ts",
11
13
  "default": "./dist/index.js"
@@ -23,21 +25,21 @@
23
25
  },
24
26
  "scripts": {
25
27
  "build": "tsc --project tsconfig.build.json",
26
- "prepublishOnly": "npm run build",
28
+ "prepack": "npm run build",
27
29
  "test": "bun test",
28
30
  "check:types": "tsc --noEmit"
29
31
  },
30
32
  "dependencies": {
31
33
  "better-result": "^2.7.0",
32
- "tar-stream": "^3.1.7"
34
+ "tiny-invariant": "1.3.3",
35
+ "tar-stream": "^3.1.8"
33
36
  },
34
37
  "peerDependencies": {
35
- "@prisma/management-api-sdk": ">=1.20.1"
38
+ "@prisma/management-api-sdk": ">=1.23.0"
36
39
  },
37
40
  "devDependencies": {
38
41
  "@types/bun": "latest",
39
- "@types/node": "^22.0.0",
40
- "@types/tar-stream": "^3.1.3",
41
- "typescript": "^5.9.3"
42
+ "@types/tar-stream": "^3.1.4",
43
+ "typescript": "^6.0.2"
42
44
  }
43
45
  }
package/src/api-client.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import type { ManagementApiClient, paths } from "@prisma/management-api-sdk";
2
2
  import { Result } from "better-result";
3
- import { ApiError, AuthenticationError } from "./errors.ts";
3
+ import { ApiError, AuthenticationError, CancelledError } from "./errors.ts";
4
4
 
5
- export type ApiClientError = AuthenticationError | ApiError;
5
+ export type ApiClientError = CancelledError | AuthenticationError | ApiError;
6
6
 
7
7
  /**
8
8
  * Lean internal wrapper around ManagementApiClient.
@@ -26,9 +26,21 @@ export class InternalApiClient {
26
26
  signal,
27
27
  }),
28
28
  "/v1/projects",
29
+ signal,
29
30
  );
30
31
  }
31
32
 
33
+ async createProject(body: CreateProjectBody, signal?: AbortSignal) {
34
+ return this.#request(
35
+ this.#client.POST("/v1/projects", {
36
+ body: body as SdkCreateProjectBody,
37
+ signal,
38
+ }),
39
+ "/v1/projects",
40
+ signal,
41
+ ).then((r) => r.map((body) => body.data));
42
+ }
43
+
32
44
  async getProject(id: string, signal?: AbortSignal) {
33
45
  return this.#request(
34
46
  this.#client.GET("/v1/projects/{id}", {
@@ -36,6 +48,7 @@ export class InternalApiClient {
36
48
  signal,
37
49
  }),
38
50
  "/v1/projects/{id}",
51
+ signal,
39
52
  ).then((r) => r.map((body) => body.data));
40
53
  }
41
54
 
@@ -47,6 +60,7 @@ export class InternalApiClient {
47
60
  signal,
48
61
  }),
49
62
  "/v1/projects/{projectId}/compute-services",
63
+ signal,
50
64
  );
51
65
  }
52
66
 
@@ -62,6 +76,7 @@ export class InternalApiClient {
62
76
  signal,
63
77
  }),
64
78
  "/v1/projects/{projectId}/compute-services",
79
+ signal,
65
80
  ).then((r) => r.map((body) => body.data));
66
81
  }
67
82
 
@@ -72,6 +87,7 @@ export class InternalApiClient {
72
87
  signal,
73
88
  }),
74
89
  "/v1/compute-services/{computeServiceId}",
90
+ signal,
75
91
  ).then((r) => r.map((body) => body.data));
76
92
  }
77
93
 
@@ -84,6 +100,7 @@ export class InternalApiClient {
84
100
  params: { path: { computeServiceId } },
85
101
  signal,
86
102
  }),
103
+ signal,
87
104
  );
88
105
  }
89
106
 
@@ -98,6 +115,7 @@ export class InternalApiClient {
98
115
  signal,
99
116
  }),
100
117
  "/v1/compute-services/{computeServiceId}/versions",
118
+ signal,
101
119
  );
102
120
  }
103
121
 
@@ -113,6 +131,7 @@ export class InternalApiClient {
113
131
  signal,
114
132
  }),
115
133
  "/v1/compute-services/{computeServiceId}/versions",
134
+ signal,
116
135
  ).then((r) => r.map((body) => body.data));
117
136
  }
118
137
 
@@ -123,6 +142,7 @@ export class InternalApiClient {
123
142
  signal,
124
143
  }),
125
144
  "/v1/compute-services/versions/{versionId}",
145
+ signal,
126
146
  ).then((r) => r.map((body) => body.data));
127
147
  }
128
148
 
@@ -133,6 +153,7 @@ export class InternalApiClient {
133
153
  signal,
134
154
  }),
135
155
  "/v1/compute-services/versions/{versionId}/start",
156
+ signal,
136
157
  ).then((r) => r.map((body) => body.data));
137
158
  }
138
159
 
@@ -145,6 +166,7 @@ export class InternalApiClient {
145
166
  params: { path: { versionId } },
146
167
  signal,
147
168
  }),
169
+ signal,
148
170
  );
149
171
  }
150
172
 
@@ -157,23 +179,33 @@ export class InternalApiClient {
157
179
  params: { path: { versionId } },
158
180
  signal,
159
181
  }),
182
+ signal,
160
183
  );
161
184
  }
162
185
 
163
186
  async #request<TData>(
164
187
  request: Promise<RequestResult<TData>>,
165
188
  endpoint: string,
189
+ signal?: AbortSignal,
166
190
  ): Promise<Result<NonNullable<TData>, ApiClientError>> {
167
- const result = await request;
191
+ let result: RequestResult<TData>;
192
+ try {
193
+ result = await request;
194
+ } catch (error) {
195
+ return Result.err(parseRequestError(error, undefined, signal));
196
+ }
168
197
  if (result.error) {
169
- return Result.err(parseApiError(result.error, result.response));
198
+ return Result.err(
199
+ parseRequestError(result.error, result.response, signal),
200
+ );
170
201
  }
171
202
  const body = result.data;
172
203
  if (body == null) {
173
204
  return Result.err(
174
- parseApiError(
205
+ parseRequestError(
175
206
  undefined,
176
207
  undefined,
208
+ signal,
177
209
  `Management API returned an empty response body for ${endpoint}`,
178
210
  ),
179
211
  );
@@ -189,12 +221,13 @@ export class InternalApiClient {
189
221
  }>
190
222
  >,
191
223
  endpoint: string,
224
+ signal?: AbortSignal,
192
225
  ): Promise<Result<TItem[], ApiClientError>> {
193
226
  const allItems: TItem[] = [];
194
227
  let cursor: string | undefined;
195
228
 
196
229
  for (;;) {
197
- const result = await this.#request(makeRequest(cursor), endpoint);
230
+ const result = await this.#request(makeRequest(cursor), endpoint, signal);
198
231
  if (result.isErr()) return result;
199
232
 
200
233
  allItems.push(...result.value.data);
@@ -209,10 +242,18 @@ export class InternalApiClient {
209
242
 
210
243
  async #unwrapVoid(
211
244
  request: Promise<RequestResult<unknown>>,
245
+ signal?: AbortSignal,
212
246
  ): Promise<Result<void, ApiClientError>> {
213
- const result = await request;
247
+ let result: RequestResult<unknown>;
248
+ try {
249
+ result = await request;
250
+ } catch (error) {
251
+ return Result.err(parseRequestError(error, undefined, signal));
252
+ }
214
253
  if (result.error) {
215
- return Result.err(parseApiError(result.error, result.response));
254
+ return Result.err(
255
+ parseRequestError(result.error, result.response, signal),
256
+ );
216
257
  }
217
258
  return Result.ok(undefined);
218
259
  }
@@ -222,6 +263,14 @@ type RequestResult<TData> =
222
263
  | { data: TData; error?: never; response: Response }
223
264
  | { data?: never; error: unknown; response: Response };
224
265
 
266
+ type SdkCreateProjectBody = NonNullable<
267
+ paths["/v1/projects"]["post"]["requestBody"]
268
+ >["content"]["application/json"];
269
+
270
+ type CreateProjectBody = Omit<SdkCreateProjectBody, "region"> & {
271
+ region?: string;
272
+ };
273
+
225
274
  type SdkCreateProjectServiceBody = NonNullable<
226
275
  paths["/v1/projects/{projectId}/compute-services"]["post"]["requestBody"]
227
276
  >["content"]["application/json"];
@@ -303,20 +352,25 @@ function parseApiError(
303
352
  });
304
353
  }
305
354
 
355
+ function parseRequestError(
356
+ error: unknown,
357
+ response?: Response,
358
+ signal?: AbortSignal,
359
+ fallbackMessage?: string,
360
+ ): ApiClientError {
361
+ if (signal?.aborted) {
362
+ return new CancelledError();
363
+ }
364
+ return parseApiError(error, response, fallbackMessage);
365
+ }
366
+
306
367
  /** Extract the upload URL from a version creation response. */
307
368
  export function readUploadUrl(data: {
308
369
  uploadUrl?: string | null;
309
- artifactUploadUrl?: string | null;
310
370
  }): string | null {
311
371
  if (typeof data.uploadUrl === "string" && data.uploadUrl.length > 0) {
312
372
  return data.uploadUrl;
313
373
  }
314
- if (
315
- typeof data.artifactUploadUrl === "string" &&
316
- data.artifactUploadUrl.length > 0
317
- ) {
318
- return data.artifactUploadUrl;
319
- }
320
374
  return null;
321
375
  }
322
376
 
package/src/archive.ts CHANGED
@@ -19,9 +19,10 @@ export async function createArchive(
19
19
  entrypoint: string,
20
20
  ): Promise<Uint8Array> {
21
21
  const packer = pack();
22
+ const rootPath = path.resolve(directory);
22
23
 
23
24
  // Walk the directory and add all files
24
- await addDirectory(packer, directory, "bundle");
25
+ await addDirectory(packer, rootPath, rootPath, "bundle");
25
26
 
26
27
  // Inject the manifest as a synthetic entry
27
28
  const manifest = JSON.stringify(
@@ -43,42 +44,49 @@ export async function createArchive(
43
44
 
44
45
  async function addDirectory(
45
46
  packer: Pack,
47
+ rootPath: string,
46
48
  fsPath: string,
47
49
  tarPrefix: string,
48
50
  ): Promise<void> {
49
- const entries = await readdir(fsPath, { withFileTypes: true });
51
+ const directoryPath = resolvePathWithinRoot(rootPath, fsPath);
52
+ const entries = await readdir(directoryPath, { withFileTypes: true });
50
53
 
51
54
  for (const entry of entries) {
52
- const fullPath = path.join(fsPath, entry.name);
55
+ const fullPath = path.join(directoryPath, entry.name);
53
56
  const tarPath = `${tarPrefix}/${entry.name}`;
54
57
 
55
58
  if (entry.isSymbolicLink()) {
56
59
  // Dereference symlinks: stat the target and add accordingly
57
60
  const target = await readlink(fullPath);
58
- const resolvedPath = path.resolve(fsPath, target);
61
+ const resolvedPath = resolvePathWithinRoot(
62
+ rootPath,
63
+ path.resolve(directoryPath, target),
64
+ );
59
65
  const resolvedStat = await stat(resolvedPath);
60
66
 
61
67
  if (resolvedStat.isDirectory()) {
62
- await addDirectory(packer, resolvedPath, tarPath);
68
+ await addDirectory(packer, rootPath, resolvedPath, tarPath);
63
69
  } else if (resolvedStat.isFile()) {
64
- await addFile(packer, resolvedPath, tarPath, resolvedStat);
70
+ await addFile(packer, rootPath, resolvedPath, tarPath, resolvedStat);
65
71
  }
66
72
  } else if (entry.isDirectory()) {
67
- await addDirectory(packer, fullPath, tarPath);
73
+ await addDirectory(packer, rootPath, fullPath, tarPath);
68
74
  } else if (entry.isFile()) {
69
75
  const fileStat = await lstat(fullPath);
70
- await addFile(packer, fullPath, tarPath, fileStat);
76
+ await addFile(packer, rootPath, fullPath, tarPath, fileStat);
71
77
  }
72
78
  }
73
79
  }
74
80
 
75
81
  async function addFile(
76
82
  packer: Pack,
83
+ rootPath: string,
77
84
  fsPath: string,
78
85
  tarPath: string,
79
86
  fileStat: { size: number; mode: number; mtime: Date },
80
87
  ): Promise<void> {
81
- const content = await readFile(fsPath);
88
+ const filePath = resolvePathWithinRoot(rootPath, fsPath);
89
+ const content = await readFile(filePath);
82
90
  packer.entry(
83
91
  {
84
92
  name: tarPath,
@@ -90,6 +98,22 @@ async function addFile(
90
98
  );
91
99
  }
92
100
 
101
+ function resolvePathWithinRoot(rootPath: string, fsPath: string): string {
102
+ const normalizedRootPath = path.resolve(rootPath);
103
+ const resolvedPath = path.resolve(fsPath);
104
+ const relativePath = path.relative(normalizedRootPath, resolvedPath);
105
+
106
+ if (
107
+ relativePath === ".." ||
108
+ relativePath.startsWith(`..${path.sep}`) ||
109
+ path.isAbsolute(relativePath)
110
+ ) {
111
+ throw new Error(`Symlink target escapes archive root: ${resolvedPath}`);
112
+ }
113
+
114
+ return resolvedPath;
115
+ }
116
+
93
117
  function collectGzipped(packer: Pack): Promise<Uint8Array> {
94
118
  return new Promise<Uint8Array>((resolve, reject) => {
95
119
  const chunks: Buffer[] = [];
package/src/callbacks.ts CHANGED
@@ -5,6 +5,7 @@ import type {
5
5
  ServiceInfo,
6
6
  VersionInfo,
7
7
  } from "./types.ts";
8
+
8
9
  export interface DeployProgress {
9
10
  onBuildStart?(): void;
10
11
  onBuildComplete?(artifact: BuildArtifact): void;