@renderinc/sdk 0.1.0 → 0.2.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 (100) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +15 -15
  3. package/biome.json +84 -0
  4. package/dist/experimental/blob/api.d.ts +11 -0
  5. package/dist/experimental/blob/api.d.ts.map +1 -0
  6. package/dist/experimental/blob/api.js +44 -0
  7. package/dist/experimental/blob/client.d.ts +21 -0
  8. package/dist/experimental/blob/client.d.ts.map +1 -0
  9. package/dist/experimental/blob/client.js +127 -0
  10. package/dist/experimental/blob/index.d.ts +5 -0
  11. package/dist/experimental/blob/index.d.ts.map +1 -0
  12. package/dist/experimental/blob/index.js +8 -0
  13. package/dist/experimental/blob/types.d.ts +49 -0
  14. package/dist/experimental/blob/types.d.ts.map +1 -0
  15. package/dist/experimental/blob/types.js +2 -0
  16. package/dist/experimental/experimental.d.ts +8 -0
  17. package/dist/experimental/experimental.d.ts.map +1 -0
  18. package/dist/experimental/experimental.js +10 -0
  19. package/dist/experimental/index.d.ts +3 -0
  20. package/dist/experimental/index.d.ts.map +1 -0
  21. package/dist/experimental/index.js +9 -0
  22. package/dist/generated/schema.d.ts +9782 -0
  23. package/dist/generated/schema.d.ts.map +1 -0
  24. package/dist/generated/schema.js +2 -0
  25. package/dist/index.d.ts +3 -1
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +5 -2
  28. package/dist/render.d.ts +2 -0
  29. package/dist/render.d.ts.map +1 -1
  30. package/dist/render.js +4 -2
  31. package/dist/utils/create-api-client.d.ts +1 -1
  32. package/dist/utils/create-api-client.d.ts.map +1 -1
  33. package/dist/utils/create-api-client.js +2 -0
  34. package/dist/version.d.ts +3 -0
  35. package/dist/version.d.ts.map +1 -0
  36. package/dist/version.js +23 -0
  37. package/dist/workflows/client/client.d.ts +1 -1
  38. package/dist/workflows/client/client.d.ts.map +1 -1
  39. package/dist/workflows/client/sse.d.ts +2 -2
  40. package/dist/workflows/client/sse.d.ts.map +1 -1
  41. package/dist/workflows/client/sse.js +2 -0
  42. package/dist/workflows/client/types.d.ts +1 -1
  43. package/dist/workflows/client/types.d.ts.map +1 -1
  44. package/dist/workflows/executor.d.ts +2 -2
  45. package/dist/workflows/executor.d.ts.map +1 -1
  46. package/dist/workflows/registry.d.ts +1 -1
  47. package/dist/workflows/registry.d.ts.map +1 -1
  48. package/dist/workflows/registry.js +13 -6
  49. package/dist/workflows/runner.d.ts.map +1 -1
  50. package/dist/workflows/runner.js +2 -0
  51. package/dist/workflows/schema.d.ts +9 -0
  52. package/dist/workflows/schema.d.ts.map +1 -1
  53. package/dist/workflows/task.d.ts +1 -0
  54. package/dist/workflows/task.d.ts.map +1 -1
  55. package/dist/workflows/task.js +34 -0
  56. package/dist/workflows/types.d.ts +3 -1
  57. package/dist/workflows/types.d.ts.map +1 -1
  58. package/dist/workflows/uds.d.ts +1 -1
  59. package/dist/workflows/uds.d.ts.map +1 -1
  60. package/dist/workflows/uds.js +9 -39
  61. package/examples/client/main.ts +42 -0
  62. package/examples/client/package-lock.json +601 -0
  63. package/examples/client/package.json +16 -0
  64. package/examples/client/tsconfig.json +17 -0
  65. package/examples/task/main.ts +90 -0
  66. package/examples/task/package-lock.json +585 -0
  67. package/examples/task/package.json +16 -0
  68. package/examples/task/tsconfig.json +17 -0
  69. package/package.json +13 -22
  70. package/src/errors.ts +73 -0
  71. package/src/experimental/blob/api.ts +91 -0
  72. package/src/experimental/blob/client.ts +317 -0
  73. package/src/experimental/blob/index.ts +22 -0
  74. package/src/experimental/blob/types.ts +131 -0
  75. package/src/experimental/experimental.ts +33 -0
  76. package/src/experimental/index.ts +24 -0
  77. package/src/generated/schema.ts +12729 -0
  78. package/src/index.ts +7 -0
  79. package/src/render.ts +35 -0
  80. package/src/utils/create-api-client.ts +13 -0
  81. package/src/utils/get-base-url.ts +16 -0
  82. package/src/version.ts +37 -0
  83. package/src/workflows/client/client.ts +142 -0
  84. package/src/workflows/client/create-client.ts +17 -0
  85. package/src/workflows/client/index.ts +3 -0
  86. package/src/workflows/client/sse.ts +95 -0
  87. package/src/workflows/client/types.ts +56 -0
  88. package/src/workflows/executor.ts +124 -0
  89. package/src/workflows/index.ts +7 -0
  90. package/src/workflows/registry.test.ts +76 -0
  91. package/src/workflows/registry.ts +88 -0
  92. package/src/workflows/runner.ts +38 -0
  93. package/src/workflows/schema.ts +348 -0
  94. package/src/workflows/task.ts +117 -0
  95. package/src/workflows/types.ts +89 -0
  96. package/src/workflows/uds.ts +179 -0
  97. package/test-types.ts +14 -0
  98. package/tsconfig.build.json +4 -0
  99. package/tsconfig.json +23 -0
  100. package/vite.config.ts +7 -0
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "NodeNext",
5
+ "lib": ["ES2020"],
6
+ "outDir": "./dist",
7
+ "rootDir": ".",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "moduleResolution": "NodeNext",
13
+ "resolveJsonModule": true
14
+ },
15
+ "include": ["**/*.ts"],
16
+ "exclude": ["node_modules", "dist"]
17
+ }
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@renderinc/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Render SDK for TypeScript",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
- "build": "tsc",
8
+ "build": "tsc -p tsconfig.build.json",
9
9
  "test": "vitest",
10
- "lint": "biome lint src",
11
- "lint:fix": "biome lint --write src",
12
- "format": "biome format --write src",
13
- "format:check": "biome format src",
14
- "check": "biome check --write src",
10
+ "lint": "biome lint",
11
+ "lint:fix": "biome lint --write",
12
+ "format": "biome format --write",
13
+ "format:check": "biome format",
14
+ "check": "biome check --write",
15
15
  "prepublishOnly": "npm run build"
16
16
  },
17
17
  "keywords": [
@@ -22,15 +22,6 @@
22
22
  ],
23
23
  "author": "Render",
24
24
  "license": "MIT",
25
- "repository": {
26
- "type": "git",
27
- "url": "https://github.com/render-oss/sdk.git",
28
- "directory": "typescript"
29
- },
30
- "homepage": "https://github.com/render-oss/sdk/tree/main/typescript#readme",
31
- "bugs": {
32
- "url": "https://github.com/render-oss/sdk/issues"
33
- },
34
25
  "devDependencies": {
35
26
  "@biomejs/biome": "2.3.8",
36
27
  "@types/node": "^20.0.0",
@@ -55,11 +46,11 @@
55
46
  "import": "./dist/workflows/index.js",
56
47
  "require": "./dist/workflows/index.js",
57
48
  "types": "./dist/workflows/index.d.ts"
49
+ },
50
+ "./experimental": {
51
+ "import": "./dist/experimental/index.js",
52
+ "require": "./dist/experimental/index.js",
53
+ "types": "./dist/experimental/index.d.ts"
58
54
  }
59
- },
60
- "files": [
61
- "dist",
62
- "README.md",
63
- "LICENSE"
64
- ]
55
+ }
65
56
  }
package/src/errors.ts ADDED
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Base error class for all Render SDK errors
3
+ */
4
+ export class RenderError extends Error {
5
+ constructor(message: string) {
6
+ super(message);
7
+ this.name = "RenderError";
8
+ Object.setPrototypeOf(this, RenderError.prototype);
9
+ }
10
+ }
11
+
12
+ /**
13
+ * Error for task execution failures
14
+ */
15
+ export class TaskRunError extends RenderError {
16
+ constructor(
17
+ message: string,
18
+ public taskRunId?: string,
19
+ public taskError?: string,
20
+ ) {
21
+ super(message);
22
+ this.name = "TaskRunError";
23
+ Object.setPrototypeOf(this, TaskRunError.prototype);
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Error for HTTP client errors (4xx)
29
+ */
30
+ export class ClientError extends RenderError {
31
+ constructor(
32
+ message: string,
33
+ public statusCode: number,
34
+ public response?: any,
35
+ ) {
36
+ super(message);
37
+ this.name = "ClientError";
38
+ Object.setPrototypeOf(this, ClientError.prototype);
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Error for HTTP server errors (5xx)
44
+ */
45
+ export class ServerError extends RenderError {
46
+ constructor(
47
+ message: string,
48
+ public statusCode: number,
49
+ public response?: any,
50
+ ) {
51
+ super(message);
52
+ this.name = "ServerError";
53
+ Object.setPrototypeOf(this, ServerError.prototype);
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Error for request timeouts
59
+ */
60
+ export class TimeoutError extends RenderError {
61
+ constructor(message: string) {
62
+ super(message);
63
+ this.name = "TimeoutError";
64
+ Object.setPrototypeOf(this, TimeoutError.prototype);
65
+ }
66
+ }
67
+
68
+ export class AbortError extends Error {
69
+ constructor() {
70
+ super("The operation was aborted.");
71
+ this.name = "AbortError";
72
+ }
73
+ }
@@ -0,0 +1,91 @@
1
+ import type { Client } from "openapi-fetch";
2
+ import { RenderError } from "../../errors.js";
3
+ import type { paths } from "../../generated/schema.js";
4
+ import type { PresignedDownloadUrl, PresignedUploadUrl, Region } from "./types.js";
5
+
6
+ /**
7
+ * Layer 2: Typed Blob API Client
8
+ *
9
+ * Provides idiomatic TypeScript wrapper around the raw OpenAPI client.
10
+ * Handles presigned URL flow but still exposes the two-step nature
11
+ * (get URL, then upload/download). Useful for advanced use cases
12
+ * requiring fine-grained control.
13
+ */
14
+ export class BlobApi {
15
+ constructor(private readonly apiClient: Client<paths>) {}
16
+
17
+ /**
18
+ * Get a presigned URL for uploading a blob
19
+ *
20
+ * @param ownerId - Owner ID (workspace team ID)
21
+ * @param region - Storage region
22
+ * @param key - Object key (path)
23
+ * @param sizeBytes - Size of the blob in bytes
24
+ * @returns Presigned upload URL with expiration and size limit
25
+ */
26
+ async getUploadUrl(
27
+ ownerId: string,
28
+ region: Region | string,
29
+ key: string,
30
+ sizeBytes: number,
31
+ ): Promise<PresignedUploadUrl> {
32
+ const { data, error } = await this.apiClient.PUT("/blobs/{ownerId}/{region}/{key}", {
33
+ params: { path: { ownerId, region: region as Region, key } },
34
+ body: { sizeBytes },
35
+ });
36
+
37
+ if (error) {
38
+ throw new RenderError(`Failed to get upload URL: ${error.message || "Unknown error"}`);
39
+ }
40
+
41
+ return {
42
+ url: data.url,
43
+ expiresAt: new Date(data.expiresAt),
44
+ maxSizeBytes: data.maxSizeBytes,
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Get a presigned URL for downloading a blob
50
+ *
51
+ * @param ownerId - Owner ID (workspace team ID)
52
+ * @param region - Storage region
53
+ * @param key - Object key (path)
54
+ * @returns Presigned download URL with expiration
55
+ */
56
+ async getDownloadUrl(
57
+ ownerId: string,
58
+ region: Region | string,
59
+ key: string,
60
+ ): Promise<PresignedDownloadUrl> {
61
+ const { data, error } = await this.apiClient.GET("/blobs/{ownerId}/{region}/{key}", {
62
+ params: { path: { ownerId, region: region as Region, key } },
63
+ });
64
+
65
+ if (error) {
66
+ throw new RenderError(`Failed to get download URL: ${error.message || "Unknown error"}`);
67
+ }
68
+
69
+ return {
70
+ url: data.url,
71
+ expiresAt: new Date(data.expiresAt),
72
+ };
73
+ }
74
+
75
+ /**
76
+ * Delete a blob
77
+ *
78
+ * @param ownerId - Owner ID (workspace team ID)
79
+ * @param region - Storage region
80
+ * @param key - Object key (path)
81
+ */
82
+ async delete(ownerId: string, region: Region | string, key: string): Promise<void> {
83
+ const { error } = await this.apiClient.DELETE("/blobs/{ownerId}/{region}/{key}", {
84
+ params: { path: { ownerId, region: region as Region, key } },
85
+ });
86
+
87
+ if (error) {
88
+ throw new RenderError(`Failed to delete blob: ${error.message || "Unknown error"}`);
89
+ }
90
+ }
91
+ }
@@ -0,0 +1,317 @@
1
+ import type { Client } from "openapi-fetch";
2
+ import { RenderError } from "../../errors.js";
3
+ import type { paths } from "../../generated/schema.js";
4
+ import type {
5
+ BlobData,
6
+ BlobScope,
7
+ DeleteBlobInput,
8
+ GetBlobInput,
9
+ PutBlobInput,
10
+ PutBlobResult,
11
+ Region,
12
+ ScopedDeleteBlobInput,
13
+ ScopedGetBlobInput,
14
+ ScopedPutBlobInput,
15
+ } from "./types.js";
16
+
17
+ /**
18
+ * Layer 3: High-Level Blob Client
19
+ *
20
+ * User-facing API that abstracts presigned URLs completely.
21
+ * Provides simple put/get/delete operations that handle the
22
+ * two-step presigned URL flow internally.
23
+ */
24
+ export class BlobClient {
25
+ constructor(private readonly apiClient: Client<paths>) {}
26
+
27
+ /**
28
+ * Upload a blob to storage
29
+ *
30
+ * @param input - Upload parameters including blob identifier and data
31
+ * @returns Result with optional ETag
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * // Upload a Buffer
36
+ * const data = Buffer.from("binary content");
37
+ * await blobClient.put({
38
+ * ownerId: "tea-xxxxx",
39
+ * region: "oregon",
40
+ * key: "path/to/file.png",
41
+ * data,
42
+ * contentType: "image/png"
43
+ * });
44
+ *
45
+ * // Upload from stream
46
+ * const stream = createReadStream("/path/to/file.zip");
47
+ * const stats = statSync("/path/to/file.zip");
48
+ * await blobClient.put({
49
+ * ownerId: "tea-xxxxx",
50
+ * region: "oregon",
51
+ * key: "file.zip",
52
+ * data: stream,
53
+ * size: stats.size
54
+ * });
55
+ * ```
56
+ */
57
+ async put(input: PutBlobInput): Promise<PutBlobResult> {
58
+ // Resolve and validate size
59
+ const size = this.resolveSize(input);
60
+
61
+ // Step 1: Get presigned upload URL from Render API
62
+ const { data, error } = await this.apiClient.PUT("/blobs/{ownerId}/{region}/{key}", {
63
+ params: {
64
+ path: {
65
+ ownerId: input.ownerId,
66
+ region: input.region as Region,
67
+ key: input.key,
68
+ },
69
+ },
70
+ body: { sizeBytes: size },
71
+ });
72
+
73
+ if (error) {
74
+ throw new RenderError(`Failed to get upload URL: ${error.message || "Unknown error"}`);
75
+ }
76
+
77
+ // Step 2: Upload to storage via presigned URL
78
+ const headers: Record<string, string> = {
79
+ "Content-Length": size.toString(),
80
+ };
81
+
82
+ if (input.contentType) {
83
+ headers["Content-Type"] = input.contentType;
84
+ }
85
+
86
+ const response = await fetch(data.url, {
87
+ method: "PUT",
88
+ headers,
89
+ body: input.data,
90
+ duplex: "half",
91
+ });
92
+
93
+ if (!response.ok) {
94
+ throw new RenderError(`Upload failed: ${response.status} ${response.statusText}`);
95
+ }
96
+
97
+ return {
98
+ etag: response.headers.get("ETag") ?? undefined,
99
+ };
100
+ }
101
+
102
+ /**
103
+ * Download a blob from storage
104
+ *
105
+ * @param input - Download parameters including blob identifier
106
+ * @returns Blob data with content
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const blob = await blobClient.get({
111
+ * ownerId: "tea-xxxxx",
112
+ * region: "oregon",
113
+ * key: "path/to/file.png"
114
+ * });
115
+ *
116
+ * console.log(blob.size); // Size in bytes
117
+ * console.log(blob.contentType); // MIME type if available
118
+ * // blob.data is a Buffer
119
+ * ```
120
+ */
121
+ async get(input: GetBlobInput): Promise<BlobData> {
122
+ // Step 1: Get presigned download URL from Render API
123
+ const { data, error } = await this.apiClient.GET("/blobs/{ownerId}/{region}/{key}", {
124
+ params: {
125
+ path: {
126
+ ownerId: input.ownerId,
127
+ region: input.region as Region,
128
+ key: input.key,
129
+ },
130
+ },
131
+ });
132
+
133
+ if (error) {
134
+ throw new RenderError(`Failed to get download URL: ${error.message || "Unknown error"}`);
135
+ }
136
+
137
+ // Step 2: Download from storage via presigned URL
138
+ const response = await fetch(data.url);
139
+
140
+ if (!response.ok) {
141
+ throw new RenderError(`Download failed: ${response.status} ${response.statusText}`);
142
+ }
143
+
144
+ const arrayBuffer = await response.arrayBuffer();
145
+ const buffer = Buffer.from(arrayBuffer);
146
+
147
+ return {
148
+ data: buffer,
149
+ size: buffer.byteLength,
150
+ contentType: response.headers.get("Content-Type") ?? undefined,
151
+ };
152
+ }
153
+
154
+ /**
155
+ * Delete a blob from storage
156
+ *
157
+ * @param input - Delete parameters including blob identifier
158
+ *
159
+ * @example
160
+ * ```typescript
161
+ * await blobClient.delete({
162
+ * ownerId: "tea-xxxxx",
163
+ * region: "oregon",
164
+ * key: "path/to/file.png"
165
+ * });
166
+ * ```
167
+ */
168
+ async delete(input: DeleteBlobInput): Promise<void> {
169
+ // DELETE goes directly to Render API (no presigned URL)
170
+ const { error } = await this.apiClient.DELETE("/blobs/{ownerId}/{region}/{key}", {
171
+ params: {
172
+ path: {
173
+ ownerId: input.ownerId,
174
+ region: input.region as Region,
175
+ key: input.key,
176
+ },
177
+ },
178
+ });
179
+
180
+ if (error) {
181
+ throw new RenderError(`Failed to delete blob: ${error.message || "Unknown error"}`);
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Create a scoped blob client for a specific owner and region
187
+ *
188
+ * @param scope - Owner ID and region to scope operations to
189
+ * @returns Scoped blob client that doesn't require ownerId/region on each call
190
+ *
191
+ * @example
192
+ * ```typescript
193
+ * const scoped = blobClient.scoped({
194
+ * ownerId: "tea-xxxxx",
195
+ * region: "oregon"
196
+ * });
197
+ *
198
+ * // Subsequent calls only need the key
199
+ * await scoped.put({ key: "file.png", data: buffer });
200
+ * await scoped.get({ key: "file.png" });
201
+ * await scoped.delete({ key: "file.png" });
202
+ * ```
203
+ */
204
+ scoped(scope: BlobScope): ScopedBlobClient {
205
+ return new ScopedBlobClient(this.apiClient, scope);
206
+ }
207
+
208
+ /**
209
+ * Resolve and validate the size for a put operation
210
+ *
211
+ * - For Buffer/Uint8Array: auto-calculate size, validate if provided
212
+ * - For streams/strings: require explicit size
213
+ */
214
+ private resolveSize(input: PutBlobInput): number {
215
+ if (Buffer.isBuffer(input.data) || input.data instanceof Uint8Array) {
216
+ const actualSize = input.data.byteLength;
217
+
218
+ if (input.size !== undefined && input.size !== actualSize) {
219
+ throw new RenderError(
220
+ `Size mismatch: provided size ${input.size} does not match actual size ${actualSize}`,
221
+ );
222
+ }
223
+
224
+ return actualSize;
225
+ }
226
+
227
+ // For Readable streams or strings, size must be provided
228
+ if (input.size === undefined) {
229
+ throw new RenderError(
230
+ "Size is required for stream and string inputs. Provide the size parameter.",
231
+ );
232
+ }
233
+
234
+ if (input.size <= 0) {
235
+ throw new RenderError("Size must be a positive integer");
236
+ }
237
+
238
+ return input.size;
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Scoped Blob Client
244
+ *
245
+ * Pre-configured client for a specific owner and region.
246
+ * Eliminates the need to specify ownerId and region on every operation.
247
+ */
248
+ export class ScopedBlobClient {
249
+ private readonly blobClient: BlobClient;
250
+
251
+ constructor(
252
+ apiClient: Client<paths>,
253
+ private readonly scope: BlobScope,
254
+ ) {
255
+ this.blobClient = new BlobClient(apiClient);
256
+ }
257
+
258
+ /**
259
+ * Upload a blob to storage using scoped owner and region
260
+ *
261
+ * @param input - Upload parameters (key and data only)
262
+ * @returns Result with optional ETag
263
+ *
264
+ * @example
265
+ * ```typescript
266
+ * const scoped = blobClient.scoped({ ownerId: "tea-xxxxx", region: "oregon" });
267
+ * await scoped.put({
268
+ * key: "file.png",
269
+ * data: Buffer.from("content"),
270
+ * contentType: "image/png"
271
+ * });
272
+ * ```
273
+ */
274
+ async put(input: ScopedPutBlobInput): Promise<PutBlobResult> {
275
+ return this.blobClient.put({
276
+ ...this.scope,
277
+ ...input,
278
+ } as PutBlobInput);
279
+ }
280
+
281
+ /**
282
+ * Download a blob from storage using scoped owner and region
283
+ *
284
+ * @param input - Download parameters (key only)
285
+ * @returns Blob data with content
286
+ *
287
+ * @example
288
+ * ```typescript
289
+ * const scoped = blobClient.scoped({ ownerId: "tea-xxxxx", region: "oregon" });
290
+ * const blob = await scoped.get({ key: "file.png" });
291
+ * ```
292
+ */
293
+ async get(input: ScopedGetBlobInput): Promise<BlobData> {
294
+ return this.blobClient.get({
295
+ ...this.scope,
296
+ ...input,
297
+ } as GetBlobInput);
298
+ }
299
+
300
+ /**
301
+ * Delete a blob from storage using scoped owner and region
302
+ *
303
+ * @param input - Delete parameters (key only)
304
+ *
305
+ * @example
306
+ * ```typescript
307
+ * const scoped = blobClient.scoped({ ownerId: "tea-xxxxx", region: "oregon" });
308
+ * await scoped.delete({ key: "file.png" });
309
+ * ```
310
+ */
311
+ async delete(input: ScopedDeleteBlobInput): Promise<void> {
312
+ return this.blobClient.delete({
313
+ ...this.scope,
314
+ ...input,
315
+ } as DeleteBlobInput);
316
+ }
317
+ }
@@ -0,0 +1,22 @@
1
+ // Blob storage client exports
2
+ export { BlobApi } from "./api.js";
3
+ export { BlobClient, ScopedBlobClient } from "./client.js";
4
+ // Type exports
5
+ export type {
6
+ BlobData,
7
+ BlobIdentifier,
8
+ BlobScope,
9
+ DeleteBlobInput,
10
+ GetBlobInput,
11
+ PresignedDownloadUrl,
12
+ PresignedUploadUrl,
13
+ PutBlobInput,
14
+ PutBlobInputBuffer,
15
+ PutBlobInputStream,
16
+ PutBlobResult,
17
+ ScopedDeleteBlobInput,
18
+ ScopedGetBlobInput,
19
+ ScopedPutBlobInput,
20
+ } from "./types.js";
21
+ // Re-export the Region enum (both type and value)
22
+ export { Region } from "./types.js";
@@ -0,0 +1,131 @@
1
+ import type { Readable } from "node:stream";
2
+
3
+ /**
4
+ * Supported regions for blob storage
5
+ */
6
+ export type Region = "frankfurt" | "oregon" | "ohio" | "singapore" | "virginia";
7
+
8
+ /**
9
+ * Base identifier for a blob object
10
+ */
11
+ export interface BlobIdentifier {
12
+ /** Owner ID (workspace team ID) in format tea-xxxxx */
13
+ ownerId: `tea-${string}`;
14
+ /** Region where the blob is stored */
15
+ region: Region | string;
16
+ /** Object key (path) for the blob */
17
+ key: string;
18
+ }
19
+
20
+ /**
21
+ * Base options for putting a blob
22
+ */
23
+ interface PutBlobInputBase extends BlobIdentifier {
24
+ /** MIME type of the content (optional, will be auto-detected if not provided) */
25
+ contentType?: string;
26
+ }
27
+
28
+ /**
29
+ * Put blob input for Buffer, Uint8Array, or string data
30
+ * Size is optional and will be auto-calculated
31
+ */
32
+ export interface PutBlobInputBuffer extends PutBlobInputBase {
33
+ /** Binary data as Buffer, Uint8Array, or string */
34
+ data: Buffer | Uint8Array | string;
35
+ /** Size in bytes (optional, auto-calculated for Buffer/Uint8Array) */
36
+ size?: number;
37
+ }
38
+
39
+ /**
40
+ * Put blob input for readable streams
41
+ * Size is required for streams
42
+ */
43
+ export interface PutBlobInputStream extends PutBlobInputBase {
44
+ /** Readable stream */
45
+ data: Readable;
46
+ /** Size in bytes (required for streams) */
47
+ size: number;
48
+ }
49
+
50
+ /**
51
+ * Input for uploading a blob
52
+ * Discriminated union: size is optional for Buffer/Uint8Array, required for streams
53
+ */
54
+ export type PutBlobInput = PutBlobInputBuffer | PutBlobInputStream;
55
+
56
+ /**
57
+ * Input for downloading a blob
58
+ */
59
+ export interface GetBlobInput extends BlobIdentifier {}
60
+
61
+ /**
62
+ * Input for deleting a blob
63
+ */
64
+ export interface DeleteBlobInput extends BlobIdentifier {}
65
+
66
+ /**
67
+ * Presigned URL for uploading
68
+ */
69
+ export interface PresignedUploadUrl {
70
+ /** Presigned upload URL */
71
+ url: string;
72
+ /** Expiration timestamp */
73
+ expiresAt: Date;
74
+ /** Maximum size allowed for upload */
75
+ maxSizeBytes: number;
76
+ }
77
+
78
+ /**
79
+ * Presigned URL for downloading
80
+ */
81
+ export interface PresignedDownloadUrl {
82
+ /** Presigned download URL */
83
+ url: string;
84
+ /** Expiration timestamp */
85
+ expiresAt: Date;
86
+ }
87
+
88
+ /**
89
+ * Downloaded blob data
90
+ */
91
+ export interface BlobData {
92
+ /** Binary content */
93
+ data: Buffer;
94
+ /** MIME type if available */
95
+ contentType?: string;
96
+ /** Size in bytes */
97
+ size: number;
98
+ }
99
+
100
+ /**
101
+ * Result from uploading a blob
102
+ */
103
+ export interface PutBlobResult {
104
+ /** ETag from storage provider */
105
+ etag?: string;
106
+ }
107
+
108
+ /**
109
+ * Scope configuration for scoped blob client
110
+ */
111
+ export interface BlobScope {
112
+ /** Owner ID (workspace team ID) in format tea-xxxxx */
113
+ ownerId: `tea-${string}`;
114
+ /** Region where the blob is stored */
115
+ region: Region | string;
116
+ }
117
+
118
+ /**
119
+ * Scoped input for uploading a blob (without ownerId/region)
120
+ */
121
+ export type ScopedPutBlobInput = Omit<PutBlobInput, keyof BlobScope>;
122
+
123
+ /**
124
+ * Scoped input for downloading a blob (without ownerId/region)
125
+ */
126
+ export type ScopedGetBlobInput = Omit<GetBlobInput, keyof BlobScope>;
127
+
128
+ /**
129
+ * Scoped input for deleting a blob (without ownerId/region)
130
+ */
131
+ export type ScopedDeleteBlobInput = Omit<DeleteBlobInput, keyof BlobScope>;