@renderinc/sdk 0.1.0 → 0.2.1

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 (126) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +17 -7
  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/experimental.d.ts +12 -0
  16. package/dist/experimental/experimental.d.ts.map +1 -0
  17. package/dist/experimental/experimental.js +16 -0
  18. package/dist/experimental/index.d.ts +3 -0
  19. package/dist/experimental/index.d.ts.map +1 -0
  20. package/dist/experimental/index.js +10 -0
  21. package/dist/experimental/object/api.d.ts +11 -0
  22. package/dist/experimental/object/api.d.ts.map +1 -0
  23. package/dist/experimental/object/api.js +44 -0
  24. package/dist/experimental/object/client.d.ts +21 -0
  25. package/dist/experimental/object/client.d.ts.map +1 -0
  26. package/dist/experimental/object/client.js +127 -0
  27. package/dist/experimental/object/index.d.ts +5 -0
  28. package/dist/experimental/object/index.d.ts.map +1 -0
  29. package/dist/experimental/object/index.js +8 -0
  30. package/dist/experimental/object/types.d.ts +49 -0
  31. package/dist/experimental/object/types.d.ts.map +1 -0
  32. package/dist/experimental/object/types.js +2 -0
  33. package/dist/generated/schema.d.ts +9910 -0
  34. package/dist/generated/schema.d.ts.map +1 -0
  35. package/dist/generated/schema.js +2 -0
  36. package/dist/index.d.ts +3 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +5 -2
  39. package/dist/render.d.ts +2 -0
  40. package/dist/render.d.ts.map +1 -1
  41. package/dist/render.js +4 -2
  42. package/dist/utils/create-api-client.d.ts +1 -1
  43. package/dist/utils/create-api-client.d.ts.map +1 -1
  44. package/dist/utils/create-api-client.js +2 -0
  45. package/dist/version.d.ts +3 -0
  46. package/dist/version.d.ts.map +1 -0
  47. package/dist/version.js +23 -0
  48. package/dist/workflows/client/client.d.ts +1 -1
  49. package/dist/workflows/client/client.d.ts.map +1 -1
  50. package/dist/workflows/client/sse.d.ts +2 -2
  51. package/dist/workflows/client/sse.d.ts.map +1 -1
  52. package/dist/workflows/client/sse.js +2 -0
  53. package/dist/workflows/client/types.d.ts +1 -1
  54. package/dist/workflows/client/types.d.ts.map +1 -1
  55. package/dist/workflows/executor.d.ts +2 -2
  56. package/dist/workflows/executor.d.ts.map +1 -1
  57. package/dist/workflows/registry.d.ts +1 -1
  58. package/dist/workflows/registry.d.ts.map +1 -1
  59. package/dist/workflows/registry.js +13 -6
  60. package/dist/workflows/runner.d.ts.map +1 -1
  61. package/dist/workflows/runner.js +2 -0
  62. package/dist/workflows/schema.d.ts +9 -0
  63. package/dist/workflows/schema.d.ts.map +1 -1
  64. package/dist/workflows/task.d.ts +1 -0
  65. package/dist/workflows/task.d.ts.map +1 -1
  66. package/dist/workflows/task.js +34 -0
  67. package/dist/workflows/types.d.ts +3 -1
  68. package/dist/workflows/types.d.ts.map +1 -1
  69. package/dist/workflows/uds.d.ts +1 -1
  70. package/dist/workflows/uds.d.ts.map +1 -1
  71. package/dist/workflows/uds.js +27 -82
  72. package/examples/client/main.ts +42 -0
  73. package/examples/client/package-lock.json +601 -0
  74. package/examples/client/package.json +16 -0
  75. package/examples/client/tsconfig.json +17 -0
  76. package/examples/task/main.ts +90 -0
  77. package/examples/task/package-lock.json +584 -0
  78. package/examples/task/package.json +16 -0
  79. package/examples/task/tsconfig.json +17 -0
  80. package/package.json +19 -27
  81. package/src/errors.test.ts +75 -0
  82. package/src/errors.ts +73 -0
  83. package/src/experimental/experimental.ts +56 -0
  84. package/src/experimental/index.ts +24 -0
  85. package/src/experimental/object/api.ts +91 -0
  86. package/src/experimental/object/client.test.ts +138 -0
  87. package/src/experimental/object/client.ts +317 -0
  88. package/src/experimental/object/index.ts +22 -0
  89. package/src/experimental/object/types.test.ts +87 -0
  90. package/src/experimental/object/types.ts +131 -0
  91. package/src/generated/schema.ts +12937 -0
  92. package/src/index.ts +7 -0
  93. package/src/render.ts +35 -0
  94. package/src/utils/create-api-client.ts +13 -0
  95. package/src/utils/get-base-url.test.ts +58 -0
  96. package/src/utils/get-base-url.ts +16 -0
  97. package/src/version.ts +37 -0
  98. package/src/workflows/client/client.test.ts +68 -0
  99. package/src/workflows/client/client.ts +142 -0
  100. package/src/workflows/client/create-client.ts +17 -0
  101. package/src/workflows/client/index.ts +3 -0
  102. package/src/workflows/client/sse.ts +95 -0
  103. package/src/workflows/client/types.ts +56 -0
  104. package/src/workflows/executor.ts +124 -0
  105. package/src/workflows/index.ts +7 -0
  106. package/src/workflows/registry.test.ts +76 -0
  107. package/src/workflows/registry.ts +88 -0
  108. package/src/workflows/runner.ts +38 -0
  109. package/src/workflows/schema.ts +348 -0
  110. package/src/workflows/task.ts +117 -0
  111. package/src/workflows/types.test.ts +52 -0
  112. package/src/workflows/types.ts +89 -0
  113. package/src/workflows/uds.ts +139 -0
  114. package/test-types.ts +14 -0
  115. package/tsconfig.build.json +4 -0
  116. package/tsconfig.json +23 -0
  117. package/vitest.config.ts +8 -0
  118. package/dist/workflows/client/errors.d.ts +0 -25
  119. package/dist/workflows/client/errors.d.ts.map +0 -1
  120. package/dist/workflows/client/errors.js +0 -56
  121. package/dist/workflows/client/schema.d.ts +0 -9322
  122. package/dist/workflows/client/schema.d.ts.map +0 -1
  123. package/dist/workflows/client/workflows.d.ts +0 -15
  124. package/dist/workflows/client/workflows.d.ts.map +0 -1
  125. package/dist/workflows/client/workflows.js +0 -63
  126. /package/dist/{workflows/client/schema.js → experimental/blob/types.js} +0 -0
@@ -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
+ DeleteObjectInput,
6
+ GetObjectInput,
7
+ ObjectData,
8
+ ObjectScope,
9
+ PutObjectInput,
10
+ PutObjectResult,
11
+ Region,
12
+ ScopedDeleteObjectInput,
13
+ ScopedGetObjectInput,
14
+ ScopedPutObjectInput,
15
+ } from "./types.js";
16
+
17
+ /**
18
+ * Layer 3: High-Level Object 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 ObjectClient {
25
+ constructor(private readonly apiClient: Client<paths>) {}
26
+
27
+ /**
28
+ * Upload an object to storage
29
+ *
30
+ * @param input - Upload parameters including object 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 objectClient.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 objectClient.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: PutObjectInput): Promise<PutObjectResult> {
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 an object from storage
104
+ *
105
+ * @param input - Download parameters including object identifier
106
+ * @returns Object data with content
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const obj = await objectClient.get({
111
+ * ownerId: "tea-xxxxx",
112
+ * region: "oregon",
113
+ * key: "path/to/file.png"
114
+ * });
115
+ *
116
+ * console.log(obj.size); // Size in bytes
117
+ * console.log(obj.contentType); // MIME type if available
118
+ * // obj.data is a Buffer
119
+ * ```
120
+ */
121
+ async get(input: GetObjectInput): Promise<ObjectData> {
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 an object from storage
156
+ *
157
+ * @param input - Delete parameters including object identifier
158
+ *
159
+ * @example
160
+ * ```typescript
161
+ * await objectClient.delete({
162
+ * ownerId: "tea-xxxxx",
163
+ * region: "oregon",
164
+ * key: "path/to/file.png"
165
+ * });
166
+ * ```
167
+ */
168
+ async delete(input: DeleteObjectInput): 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 object: ${error.message || "Unknown error"}`);
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Create a scoped object client for a specific owner and region
187
+ *
188
+ * @param scope - Owner ID and region to scope operations to
189
+ * @returns Scoped object client that doesn't require ownerId/region on each call
190
+ *
191
+ * @example
192
+ * ```typescript
193
+ * const scoped = objectClient.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: ObjectScope): ScopedObjectClient {
205
+ return new ScopedObjectClient(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: PutObjectInput): 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 Object 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 ScopedObjectClient {
249
+ private readonly objectClient: ObjectClient;
250
+
251
+ constructor(
252
+ apiClient: Client<paths>,
253
+ private readonly scope: ObjectScope,
254
+ ) {
255
+ this.objectClient = new ObjectClient(apiClient);
256
+ }
257
+
258
+ /**
259
+ * Upload an object 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 = objectClient.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: ScopedPutObjectInput): Promise<PutObjectResult> {
275
+ return this.objectClient.put({
276
+ ...this.scope,
277
+ ...input,
278
+ } as PutObjectInput);
279
+ }
280
+
281
+ /**
282
+ * Download an object from storage using scoped owner and region
283
+ *
284
+ * @param input - Download parameters (key only)
285
+ * @returns Object data with content
286
+ *
287
+ * @example
288
+ * ```typescript
289
+ * const scoped = objectClient.scoped({ ownerId: "tea-xxxxx", region: "oregon" });
290
+ * const obj = await scoped.get({ key: "file.png" });
291
+ * ```
292
+ */
293
+ async get(input: ScopedGetObjectInput): Promise<ObjectData> {
294
+ return this.objectClient.get({
295
+ ...this.scope,
296
+ ...input,
297
+ } as GetObjectInput);
298
+ }
299
+
300
+ /**
301
+ * Delete an object from storage using scoped owner and region
302
+ *
303
+ * @param input - Delete parameters (key only)
304
+ *
305
+ * @example
306
+ * ```typescript
307
+ * const scoped = objectClient.scoped({ ownerId: "tea-xxxxx", region: "oregon" });
308
+ * await scoped.delete({ key: "file.png" });
309
+ * ```
310
+ */
311
+ async delete(input: ScopedDeleteObjectInput): Promise<void> {
312
+ return this.objectClient.delete({
313
+ ...this.scope,
314
+ ...input,
315
+ } as DeleteObjectInput);
316
+ }
317
+ }
@@ -0,0 +1,22 @@
1
+ // Object storage client exports
2
+ export { ObjectApi } from "./api.js";
3
+ export { ObjectClient, ScopedObjectClient } from "./client.js";
4
+ // Type exports
5
+ export type {
6
+ DeleteObjectInput,
7
+ GetObjectInput,
8
+ ObjectData,
9
+ ObjectIdentifier,
10
+ ObjectScope,
11
+ PresignedDownloadUrl,
12
+ PresignedUploadUrl,
13
+ PutObjectInput,
14
+ PutObjectInputBuffer,
15
+ PutObjectInputStream,
16
+ PutObjectResult,
17
+ ScopedDeleteObjectInput,
18
+ ScopedGetObjectInput,
19
+ ScopedPutObjectInput,
20
+ } from "./types.js";
21
+ // Re-export the Region enum (both type and value)
22
+ export { Region } from "./types.js";
@@ -0,0 +1,87 @@
1
+ import type { Readable } from "node:stream";
2
+ import type {
3
+ DeleteObjectInput,
4
+ GetObjectInput,
5
+ ObjectIdentifier,
6
+ ObjectScope,
7
+ PutObjectInput,
8
+ PutObjectInputBuffer,
9
+ PutObjectInputStream,
10
+ ScopedDeleteObjectInput,
11
+ ScopedGetObjectInput,
12
+ ScopedPutObjectInput,
13
+ } from "./types.js";
14
+
15
+ describe("PutObjectInput discriminated union", () => {
16
+ it("accepts buffer variant", () => {
17
+ const input: PutObjectInput = {
18
+ ownerId: "tea-123",
19
+ region: "frankfurt",
20
+ key: "test.txt",
21
+ data: Buffer.from("hello"),
22
+ };
23
+ expectTypeOf(input).toMatchTypeOf<PutObjectInput>();
24
+ });
25
+
26
+ it("accepts stream variant with required size", () => {
27
+ const input: PutObjectInput = {
28
+ ownerId: "tea-123",
29
+ region: "frankfurt",
30
+ key: "test.txt",
31
+ data: {} as Readable,
32
+ size: 100,
33
+ };
34
+ expectTypeOf(input).toMatchTypeOf<PutObjectInput>();
35
+ });
36
+
37
+ it("union includes both variants", () => {
38
+ expectTypeOf<PutObjectInput>().toMatchTypeOf<PutObjectInputBuffer | PutObjectInputStream>();
39
+ });
40
+ });
41
+
42
+ describe("ObjectIdentifier type", () => {
43
+ it("requires tea- prefix on ownerId", () => {
44
+ expectTypeOf<ObjectIdentifier["ownerId"]>().toEqualTypeOf<`tea-${string}`>();
45
+ });
46
+
47
+ it("has required key property", () => {
48
+ expectTypeOf<ObjectIdentifier>().toHaveProperty("key");
49
+ expectTypeOf<ObjectIdentifier["key"]>().toEqualTypeOf<string>();
50
+ });
51
+ });
52
+
53
+ describe("Scoped types omit ObjectScope fields", () => {
54
+ it("ScopedPutObjectInput omits ownerId and region", () => {
55
+ expectTypeOf<ScopedPutObjectInput>().not.toHaveProperty("ownerId");
56
+ expectTypeOf<ScopedPutObjectInput>().not.toHaveProperty("region");
57
+ expectTypeOf<ScopedPutObjectInput>().toHaveProperty("key");
58
+ expectTypeOf<ScopedPutObjectInput>().toHaveProperty("data");
59
+ });
60
+
61
+ it("ScopedGetObjectInput omits ownerId and region", () => {
62
+ expectTypeOf<ScopedGetObjectInput>().not.toHaveProperty("ownerId");
63
+ expectTypeOf<ScopedGetObjectInput>().not.toHaveProperty("region");
64
+ expectTypeOf<ScopedGetObjectInput>().toHaveProperty("key");
65
+ });
66
+
67
+ it("ScopedDeleteObjectInput omits ownerId and region", () => {
68
+ expectTypeOf<ScopedDeleteObjectInput>().not.toHaveProperty("ownerId");
69
+ expectTypeOf<ScopedDeleteObjectInput>().not.toHaveProperty("region");
70
+ expectTypeOf<ScopedDeleteObjectInput>().toHaveProperty("key");
71
+ });
72
+
73
+ it("ObjectScope has ownerId and region", () => {
74
+ expectTypeOf<ObjectScope>().toHaveProperty("ownerId");
75
+ expectTypeOf<ObjectScope>().toHaveProperty("region");
76
+ });
77
+ });
78
+
79
+ describe("GetObjectInput and DeleteObjectInput extend ObjectIdentifier", () => {
80
+ it("GetObjectInput matches ObjectIdentifier", () => {
81
+ expectTypeOf<GetObjectInput>().toMatchTypeOf<ObjectIdentifier>();
82
+ });
83
+
84
+ it("DeleteObjectInput matches ObjectIdentifier", () => {
85
+ expectTypeOf<DeleteObjectInput>().toMatchTypeOf<ObjectIdentifier>();
86
+ });
87
+ });
@@ -0,0 +1,131 @@
1
+ import type { Readable } from "node:stream";
2
+
3
+ /**
4
+ * Supported regions for object storage
5
+ */
6
+ export type Region = "frankfurt" | "oregon" | "ohio" | "singapore" | "virginia";
7
+
8
+ /**
9
+ * Base identifier for a storage object
10
+ */
11
+ export interface ObjectIdentifier {
12
+ /** Owner ID (workspace team ID) in format tea-xxxxx */
13
+ ownerId: `tea-${string}`;
14
+ /** Region where the object is stored */
15
+ region: Region | string;
16
+ /** Object key (path) for the object */
17
+ key: string;
18
+ }
19
+
20
+ /**
21
+ * Base options for putting an object
22
+ */
23
+ interface PutObjectInputBase extends ObjectIdentifier {
24
+ /** MIME type of the content (optional, will be auto-detected if not provided) */
25
+ contentType?: string;
26
+ }
27
+
28
+ /**
29
+ * Put object input for Buffer, Uint8Array, or string data
30
+ * Size is optional and will be auto-calculated
31
+ */
32
+ export interface PutObjectInputBuffer extends PutObjectInputBase {
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 object input for readable streams
41
+ * Size is required for streams
42
+ */
43
+ export interface PutObjectInputStream extends PutObjectInputBase {
44
+ /** Readable stream */
45
+ data: Readable;
46
+ /** Size in bytes (required for streams) */
47
+ size: number;
48
+ }
49
+
50
+ /**
51
+ * Input for uploading an object
52
+ * Discriminated union: size is optional for Buffer/Uint8Array, required for streams
53
+ */
54
+ export type PutObjectInput = PutObjectInputBuffer | PutObjectInputStream;
55
+
56
+ /**
57
+ * Input for downloading an object
58
+ */
59
+ export interface GetObjectInput extends ObjectIdentifier {}
60
+
61
+ /**
62
+ * Input for deleting an object
63
+ */
64
+ export interface DeleteObjectInput extends ObjectIdentifier {}
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 object data
90
+ */
91
+ export interface ObjectData {
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 an object
102
+ */
103
+ export interface PutObjectResult {
104
+ /** ETag from storage provider */
105
+ etag?: string;
106
+ }
107
+
108
+ /**
109
+ * Scope configuration for scoped object client
110
+ */
111
+ export interface ObjectScope {
112
+ /** Owner ID (workspace team ID) in format tea-xxxxx */
113
+ ownerId: `tea-${string}`;
114
+ /** Region where the object is stored */
115
+ region: Region | string;
116
+ }
117
+
118
+ /**
119
+ * Scoped input for uploading an object (without ownerId/region)
120
+ */
121
+ export type ScopedPutObjectInput = Omit<PutObjectInput, keyof ObjectScope>;
122
+
123
+ /**
124
+ * Scoped input for downloading an object (without ownerId/region)
125
+ */
126
+ export type ScopedGetObjectInput = Omit<GetObjectInput, keyof ObjectScope>;
127
+
128
+ /**
129
+ * Scoped input for deleting an object (without ownerId/region)
130
+ */
131
+ export type ScopedDeleteObjectInput = Omit<DeleteObjectInput, keyof ObjectScope>;