@renderinc/sdk 0.2.1 → 0.4.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 (92) hide show
  1. package/README.md +159 -42
  2. package/dist/experimental/experimental.d.ts +2 -2
  3. package/dist/experimental/experimental.d.ts.map +1 -1
  4. package/dist/experimental/experimental.js +4 -4
  5. package/dist/experimental/object/api.d.ts +2 -1
  6. package/dist/experimental/object/api.d.ts.map +1 -1
  7. package/dist/experimental/object/api.js +20 -3
  8. package/dist/experimental/object/client.d.ts +11 -5
  9. package/dist/experimental/object/client.d.ts.map +1 -1
  10. package/dist/experimental/object/client.js +78 -13
  11. package/dist/experimental/object/e2e-helpers.d.ts +3 -0
  12. package/dist/experimental/object/e2e-helpers.d.ts.map +1 -0
  13. package/dist/experimental/object/e2e-helpers.js +70 -0
  14. package/dist/experimental/object/index.d.ts +1 -1
  15. package/dist/experimental/object/index.d.ts.map +1 -1
  16. package/dist/experimental/object/types.d.ts +18 -0
  17. package/dist/experimental/object/types.d.ts.map +1 -1
  18. package/dist/generated/schema.d.ts +167 -28
  19. package/dist/generated/schema.d.ts.map +1 -1
  20. package/dist/render.d.ts.map +1 -1
  21. package/dist/render.js +3 -1
  22. package/dist/workflows/client/client.d.ts +7 -3
  23. package/dist/workflows/client/client.d.ts.map +1 -1
  24. package/dist/workflows/client/client.js +96 -15
  25. package/dist/workflows/client/index.d.ts +2 -0
  26. package/dist/workflows/client/index.d.ts.map +1 -1
  27. package/dist/workflows/client/index.js +5 -1
  28. package/dist/workflows/client/sse.d.ts +0 -7
  29. package/dist/workflows/client/sse.d.ts.map +1 -1
  30. package/dist/workflows/client/sse.js +1 -71
  31. package/dist/workflows/client/task-run-promise.d.ts +12 -0
  32. package/dist/workflows/client/task-run-promise.d.ts.map +1 -0
  33. package/dist/workflows/client/task-run-promise.js +22 -0
  34. package/dist/workflows/client/task-run-result.d.ts +10 -0
  35. package/dist/workflows/client/task-run-result.d.ts.map +1 -0
  36. package/dist/workflows/client/task-run-result.js +18 -0
  37. package/dist/workflows/client/types.d.ts +2 -0
  38. package/dist/workflows/client/types.d.ts.map +1 -1
  39. package/dist/workflows/uds.d.ts +1 -0
  40. package/dist/workflows/uds.d.ts.map +1 -1
  41. package/dist/workflows/uds.js +30 -2
  42. package/dist/workflows/workflows.d.ts +19 -0
  43. package/dist/workflows/workflows.d.ts.map +1 -0
  44. package/dist/workflows/workflows.js +51 -0
  45. package/package.json +5 -1
  46. package/CHANGELOG.md +0 -33
  47. package/biome.json +0 -84
  48. package/examples/client/main.ts +0 -42
  49. package/examples/client/package-lock.json +0 -601
  50. package/examples/client/package.json +0 -16
  51. package/examples/client/tsconfig.json +0 -17
  52. package/examples/task/main.ts +0 -90
  53. package/examples/task/package-lock.json +0 -584
  54. package/examples/task/package.json +0 -16
  55. package/examples/task/tsconfig.json +0 -17
  56. package/src/errors.test.ts +0 -75
  57. package/src/errors.ts +0 -73
  58. package/src/experimental/experimental.ts +0 -56
  59. package/src/experimental/index.ts +0 -24
  60. package/src/experimental/object/api.ts +0 -91
  61. package/src/experimental/object/client.test.ts +0 -138
  62. package/src/experimental/object/client.ts +0 -317
  63. package/src/experimental/object/index.ts +0 -22
  64. package/src/experimental/object/types.test.ts +0 -87
  65. package/src/experimental/object/types.ts +0 -131
  66. package/src/generated/schema.ts +0 -12937
  67. package/src/index.ts +0 -7
  68. package/src/render.ts +0 -35
  69. package/src/utils/create-api-client.ts +0 -13
  70. package/src/utils/get-base-url.test.ts +0 -58
  71. package/src/utils/get-base-url.ts +0 -16
  72. package/src/version.ts +0 -37
  73. package/src/workflows/client/client.test.ts +0 -68
  74. package/src/workflows/client/client.ts +0 -142
  75. package/src/workflows/client/create-client.ts +0 -17
  76. package/src/workflows/client/index.ts +0 -3
  77. package/src/workflows/client/sse.ts +0 -95
  78. package/src/workflows/client/types.ts +0 -56
  79. package/src/workflows/executor.ts +0 -124
  80. package/src/workflows/index.ts +0 -7
  81. package/src/workflows/registry.test.ts +0 -76
  82. package/src/workflows/registry.ts +0 -88
  83. package/src/workflows/runner.ts +0 -38
  84. package/src/workflows/schema.ts +0 -348
  85. package/src/workflows/task.ts +0 -117
  86. package/src/workflows/types.test.ts +0 -52
  87. package/src/workflows/types.ts +0 -89
  88. package/src/workflows/uds.ts +0 -139
  89. package/test-types.ts +0 -14
  90. package/tsconfig.build.json +0 -4
  91. package/tsconfig.json +0 -23
  92. package/vitest.config.ts +0 -8
package/README.md CHANGED
@@ -30,6 +30,12 @@ Or with pnpm:
30
30
  pnpm add @renderinc/sdk
31
31
  ```
32
32
 
33
+ Or with Bun:
34
+
35
+ ```bash
36
+ bun add @renderinc/sdk
37
+ ```
38
+
33
39
  ## Quick Start
34
40
 
35
41
  ### REST API Client
@@ -47,11 +53,13 @@ const result = await render.workflows.runTask('my-workflow/my-task', [42, 'hello
47
53
  console.log('Status:', result.status);
48
54
  console.log('Results:', result.results);
49
55
 
56
+ // Or start a task and decide when to await the result
57
+ const run = await render.workflows.startTask('my-workflow/my-task', [42, 'hello']);
58
+ console.log('Task run ID:', run.taskRunId);
59
+ const details = await run.get();
60
+
50
61
  // List recent task runs
51
62
  const taskRuns = await render.workflows.listTaskRuns({ limit: 10 });
52
-
53
- // Get specific task run
54
- const details = await render.workflows.getTaskRun(result.id);
55
63
  ```
56
64
 
57
65
  Alternatively, you can create a workflows client directly:
@@ -125,9 +133,12 @@ Creates a new Render SDK instance with access to all Render products.
125
133
  - `baseUrl?: string` - Base URL (defaults to `https://api.render.com`)
126
134
  - `useLocalDev?: boolean` - Use local development mode
127
135
  - `localDevUrl?: string` - Local development URL
136
+ - `ownerId?: string` - Default owner ID for object storage (falls back to `RENDER_WORKSPACE_ID` env var)
137
+ - `region?: string` - Default region for object storage (falls back to `RENDER_REGION` env var)
128
138
 
129
139
  **Properties:**
130
140
  - `workflows` - WorkflowsClient instance for managing workflow tasks
141
+ - `experimental` - ExperimentalClient instance for object storage and other experimental APIs
131
142
 
132
143
  **Example:**
133
144
  ```typescript
@@ -175,6 +186,56 @@ const result = await render.workflows.runTask('my-workflow/square', [5]);
175
186
  console.log('Results:', result.results);
176
187
  ```
177
188
 
189
+ #### `render.workflows.startTask(taskIdentifier, inputData, signal?)`
190
+
191
+ Starts a task run and returns a `TaskRunResult`. Results are not streamed until you call `.get()` on the returned result. Use this when you need the task run ID, want to defer awaiting, or want fire-and-forget.
192
+
193
+ **Parameters:**
194
+ - `taskIdentifier: string` - Task identifier in format "workflow-slug/task-name"
195
+ - `inputData: any[]` - Input data as array of parameters
196
+ - `signal?: AbortSignal` - Optional abort signal for cancellation
197
+
198
+ **Returns:** `Promise<TaskRunResult>`
199
+
200
+ **Example:**
201
+ ```typescript
202
+ const render = new Render();
203
+
204
+ // Start a task and grab its ID
205
+ const run = await render.workflows.startTask('my-workflow/square', [5]);
206
+ console.log('Task run ID:', run.taskRunId);
207
+
208
+ // Await the result when you're ready
209
+ const result = await run.get();
210
+ console.log('Results:', result.results);
211
+ ```
212
+
213
+ #### `render.workflows.taskRunEvents(taskRunIds, signal?)`
214
+
215
+ Streams task run events as an async iterable. Yields a `TaskRunDetails` for each terminal event (completed or failed) received on the stream.
216
+
217
+ **Parameters:**
218
+ - `taskRunIds: string[]` - One or more task run IDs to subscribe to
219
+ - `signal?: AbortSignal` - Optional abort signal for cancellation
220
+
221
+ **Returns:** `AsyncGenerator<TaskRunDetails>`
222
+
223
+ **Example:**
224
+ ```typescript
225
+ const render = new Render();
226
+
227
+ const run1 = await render.workflows.startTask('my-workflow/square', [3]);
228
+ const run2 = await render.workflows.startTask('my-workflow/square', [6]);
229
+
230
+ // The stream stays open until you break or abort.
231
+ const pending = new Set([run1.taskRunId, run2.taskRunId]);
232
+ for await (const event of render.workflows.taskRunEvents([...pending])) {
233
+ console.log('Event:', event.status, event.id, event.results);
234
+ pending.delete(event.id);
235
+ if (pending.size === 0) break;
236
+ }
237
+ ```
238
+
178
239
  #### `render.workflows.getTaskRun(taskRunId)`
179
240
 
180
241
  Gets task run details by ID.
@@ -190,6 +251,22 @@ const render = new Render();
190
251
  const details = await render.workflows.getTaskRun('task-run-id');
191
252
  ```
192
253
 
254
+ #### `render.workflows.cancelTaskRun(taskRunId)`
255
+
256
+ Cancels a running task.
257
+
258
+ **Parameters:**
259
+ - `taskRunId: string` - Task run ID to cancel
260
+
261
+ **Returns:** `Promise<void>`
262
+
263
+ **Example:**
264
+ ```typescript
265
+ const render = new Render();
266
+ const run = await render.workflows.startTask('my-workflow/square', [5]);
267
+ await render.workflows.cancelTaskRun(run.taskRunId);
268
+ ```
269
+
193
270
  #### `render.workflows.listTaskRuns(params)`
194
271
 
195
272
  Lists task runs with optional filters.
@@ -300,20 +377,36 @@ enum TaskRunStatus {
300
377
  ```typescript
301
378
  interface TaskRun {
302
379
  id: string;
380
+ taskId: string;
303
381
  status: TaskRunStatus;
304
- created_at: string;
305
- updated_at: string;
306
- task_identifier: string;
382
+ startedAt?: string;
383
+ completedAt?: string;
384
+ parentTaskRunId: string;
385
+ rootTaskRunId: string;
386
+ retries: number;
307
387
  }
308
388
  ```
309
389
 
310
390
  #### `TaskRunDetails`
311
391
 
312
392
  ```typescript
313
- interface TaskRunDetails extends TaskRun {
314
- completed_at?: string;
315
- results?: any[];
393
+ interface TaskRunDetails {
394
+ id: string;
395
+ taskId: string;
396
+ status: TaskRunStatus;
397
+ results?: any;
316
398
  error?: string;
399
+ startedAt?: string;
400
+ completedAt?: string;
401
+ }
402
+ ```
403
+
404
+ #### `TaskRunResult`
405
+
406
+ ```typescript
407
+ class TaskRunResult {
408
+ readonly taskRunId: string;
409
+ get(): Promise<TaskRunDetails>;
317
410
  }
318
411
  ```
319
412
 
@@ -340,6 +433,7 @@ The SDK provides several error classes:
340
433
  import { Render } from '@renderinc/sdk';
341
434
  import {
342
435
  RenderError,
436
+ TaskRunError,
343
437
  ClientError,
344
438
  ServerError,
345
439
  AbortError,
@@ -350,7 +444,9 @@ const render = new Render();
350
444
  try {
351
445
  const result = await render.workflows.runTask('my-workflow/task', [42]);
352
446
  } catch (error) {
353
- if (error instanceof ClientError) {
447
+ if (error instanceof TaskRunError) {
448
+ console.error('Task failed:', error.taskRunId, error.message);
449
+ } else if (error instanceof ClientError) {
354
450
  console.error('Client error:', error.statusCode, error.cause);
355
451
  } else if (error instanceof ServerError) {
356
452
  console.error('Server error:', error.statusCode, error.cause);
@@ -365,11 +461,36 @@ try {
365
461
  ## Environment Variables
366
462
 
367
463
  - `RENDER_API_KEY` - Your Render API key (required)
464
+ - `RENDER_WORKSPACE_ID` - Default owner ID for object storage (workspace team ID, e.g. `tea-xxxxx`)
465
+ - `RENDER_REGION` - Default region for object storage (e.g. `oregon`, `frankfurt`)
368
466
  - `RENDER_USE_LOCAL_DEV` - Enable local development mode (`true`/`false`)
369
467
  - `RENDER_LOCAL_DEV_URL` - Local development URL (default: `http://localhost:8120`)
370
468
  - `RENDER_SDK_MODE` - Task execution mode (`run` or `register`)
371
469
  - `RENDER_SDK_SOCKET_PATH` - Unix socket path for task communication
372
470
 
471
+ ### Object Storage
472
+
473
+ When running on Render, `RENDER_WORKSPACE_ID` and `RENDER_REGION` are set automatically. You can also pass them as constructor options:
474
+
475
+ ```typescript
476
+ import { Render } from '@renderinc/sdk';
477
+
478
+ const render = new Render(); // Uses env vars for auth + object storage defaults
479
+
480
+ // Upload (no need to pass ownerId/region when env vars are set)
481
+ await render.experimental.storage.objects.put({
482
+ key: 'path/to/file.png',
483
+ data: Buffer.from('binary content'),
484
+ contentType: 'image/png',
485
+ });
486
+
487
+ // Download
488
+ const obj = await render.experimental.storage.objects.get({ key: 'path/to/file.png' });
489
+
490
+ // List
491
+ const response = await render.experimental.storage.objects.list();
492
+ ```
493
+
373
494
  ## Examples
374
495
 
375
496
  ### Example 1: Running a Task
@@ -379,18 +500,14 @@ import { Render } from '@renderinc/sdk';
379
500
 
380
501
  const render = new Render();
381
502
 
382
- async function runSquareTask() {
383
- const result = await render.workflows.runTask('my-workflow/square', [5]);
384
- console.log('Square of 5 is:', result.results[0]); // 25
385
- }
386
-
387
- runSquareTask();
503
+ const result = await render.workflows.runTask('my-workflow/square', [5]);
504
+ console.log('Square of 5 is:', result.results[0]); // 25
388
505
  ```
389
506
 
390
507
  ### Example 2: Defining Tasks with Subtasks
391
508
 
392
509
  ```typescript
393
- import { task, startTaskServer } from '@renderinc/sdk/workflows';
510
+ import { task } from '@renderinc/sdk/workflows';
394
511
 
395
512
  const square = task(
396
513
  { name: 'square' },
@@ -407,8 +524,6 @@ task(
407
524
  return Math.sqrt(aSquared + bSquared);
408
525
  }
409
526
  );
410
-
411
- await startTaskServer();
412
527
  ```
413
528
 
414
529
  ### Example 3: Error Handling in Tasks
@@ -502,8 +617,7 @@ async function workflowExample() {
502
617
  console.log(`\nRecent task runs: ${recentRuns.length}`);
503
618
 
504
619
  for (const run of recentRuns) {
505
- const details = await render.workflows.getTaskRun(run.id);
506
- console.log(`- ${run.taskId}: ${details.status}`);
620
+ console.log(`- ${run.id}: ${run.status} (${run.taskId})`);
507
621
  }
508
622
  } catch (error) {
509
623
  console.error('Error:', error);
@@ -544,30 +658,33 @@ npm run format
544
658
  ```
545
659
  typescript/
546
660
  ├── src/
547
- │ ├── render.ts # Main Render SDK class
548
- │ ├── index.ts # Main exports
549
- └── workflows/ # Workflows functionality
550
- ├── client/ # REST API client
551
- ├── client.ts # WorkflowsClient class
552
- │ ├── workflows.ts # WorkflowsService
553
- │ ├── sse.ts # SSE client
554
- │ ├── types.ts # Type definitions
555
- │ ├── errors.ts # Error classes
556
- └── index.ts # Exports
557
- ├── task/ # Task execution SDK
558
- │ ├── task.ts # task() function
559
- │ ├── runner.ts # start() and run()
560
- │ ├── executor.ts # TaskExecutor
561
- │ ├── registry.ts # TaskRegistry
562
- │ ├── uds.ts # Unix socket client
563
- │ ├── types.ts # Type definitions
564
- │ └── index.ts # Exports
565
- └── index.ts # Workflows exports
661
+ │ ├── render.ts # Main Render SDK class
662
+ │ ├── errors.ts # Error classes
663
+ ├── index.ts # Main exports
664
+ ├── version.ts # SDK version and user-agent
665
+ │ ├── workflows/ # Workflows functionality
666
+ │ ├── task.ts # task() function
667
+ │ ├── runner.ts # startTaskServer() and run()
668
+ │ ├── executor.ts # TaskExecutor
669
+ │ ├── registry.ts # TaskRegistry
670
+ ├── uds.ts # Unix socket client
671
+ ├── types.ts # Type definitions
672
+ │ ├── client/ # REST API client
673
+ ├── client.ts # WorkflowsClient class
674
+ ├── create-client.ts # createWorkflowsClient() factory
675
+ ├── task-run-result.ts # TaskRunResult class
676
+ ├── sse.ts # SSE event types
677
+ ├── types.ts # Client type definitions
678
+ └── index.ts # Exports
679
+ └── index.ts # Workflows exports
680
+ │ ├── experimental/ # Experimental features
681
+ │ │ └── object/ # Object storage API
682
+ │ └── utils/ # Shared utilities
566
683
  ├── examples/
567
- │ ├── client/ # Client example
684
+ │ ├── client/ # Client example
568
685
  │ │ ├── main.ts
569
686
  │ │ └── package.json
570
- │ └── task/ # Task example
687
+ │ └── task/ # Task example
571
688
  │ ├── main.ts
572
689
  │ └── package.json
573
690
  ├── package.json
@@ -3,10 +3,10 @@ import type { paths } from "../generated/schema.js";
3
3
  import { ObjectClient } from "./object/client.js";
4
4
  export declare class StorageClient {
5
5
  readonly objects: ObjectClient;
6
- constructor(apiClient: Client<paths>);
6
+ constructor(apiClient: Client<paths>, defaultOwnerId?: string, defaultRegion?: string);
7
7
  }
8
8
  export declare class ExperimentalClient {
9
9
  readonly storage: StorageClient;
10
- constructor(apiClient: Client<paths>);
10
+ constructor(apiClient: Client<paths>, defaultOwnerId?: string, defaultRegion?: string);
11
11
  }
12
12
  //# sourceMappingURL=experimental.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"experimental.d.ts","sourceRoot":"","sources":["../../src/experimental/experimental.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAgBlD,qBAAa,aAAa;IAExB,SAAgB,OAAO,EAAE,YAAY,CAAC;gBAE1B,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC;CAGrC;AAuBD,qBAAa,kBAAkB;IAE7B,SAAgB,OAAO,EAAE,aAAa,CAAC;gBAE3B,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC;CAGrC"}
1
+ {"version":3,"file":"experimental.d.ts","sourceRoot":"","sources":["../../src/experimental/experimental.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAgBlD,qBAAa,aAAa;IAExB,SAAgB,OAAO,EAAE,YAAY,CAAC;gBAE1B,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM;CAGtF;AAuBD,qBAAa,kBAAkB;IAE7B,SAAgB,OAAO,EAAE,aAAa,CAAC;gBAE3B,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM;CAGtF"}
@@ -3,14 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ExperimentalClient = exports.StorageClient = void 0;
4
4
  const client_js_1 = require("./object/client.js");
5
5
  class StorageClient {
6
- constructor(apiClient) {
7
- this.objects = new client_js_1.ObjectClient(apiClient);
6
+ constructor(apiClient, defaultOwnerId, defaultRegion) {
7
+ this.objects = new client_js_1.ObjectClient(apiClient, defaultOwnerId, defaultRegion);
8
8
  }
9
9
  }
10
10
  exports.StorageClient = StorageClient;
11
11
  class ExperimentalClient {
12
- constructor(apiClient) {
13
- this.storage = new StorageClient(apiClient);
12
+ constructor(apiClient, defaultOwnerId, defaultRegion) {
13
+ this.storage = new StorageClient(apiClient, defaultOwnerId, defaultRegion);
14
14
  }
15
15
  }
16
16
  exports.ExperimentalClient = ExperimentalClient;
@@ -1,11 +1,12 @@
1
1
  import type { Client } from "openapi-fetch";
2
2
  import type { paths } from "../../generated/schema.js";
3
- import type { PresignedDownloadUrl, PresignedUploadUrl, Region } from "./types.js";
3
+ import type { ListObjectsResponse, PresignedDownloadUrl, PresignedUploadUrl, Region } from "./types.js";
4
4
  export declare class ObjectApi {
5
5
  private readonly apiClient;
6
6
  constructor(apiClient: Client<paths>);
7
7
  getUploadUrl(ownerId: string, region: Region | string, key: string, sizeBytes: number): Promise<PresignedUploadUrl>;
8
8
  getDownloadUrl(ownerId: string, region: Region | string, key: string): Promise<PresignedDownloadUrl>;
9
9
  delete(ownerId: string, region: Region | string, key: string): Promise<void>;
10
+ listObjects(ownerId: string, region: Region | string, cursor?: string, limit?: number): Promise<ListObjectsResponse>;
10
11
  }
11
12
  //# sourceMappingURL=api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/experimental/object/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAUnF,qBAAa,SAAS;IACR,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC;IAW/C,YAAY,CAChB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,kBAAkB,CAAC;IAyBxB,cAAc,CAClB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,oBAAoB,CAAC;IAsB1B,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CASnF"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/experimental/object/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,KAAK,EACV,mBAAmB,EAEnB,oBAAoB,EACpB,kBAAkB,EAClB,MAAM,EACP,MAAM,YAAY,CAAC;AAUpB,qBAAa,SAAS;IACR,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC;IAW/C,YAAY,CAChB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,kBAAkB,CAAC;IAyBxB,cAAc,CAClB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,oBAAoB,CAAC;IAsB1B,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB5E,WAAW,CACf,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,mBAAmB,CAAC;CAoBhC"}
@@ -7,7 +7,7 @@ class ObjectApi {
7
7
  this.apiClient = apiClient;
8
8
  }
9
9
  async getUploadUrl(ownerId, region, key, sizeBytes) {
10
- const { data, error } = await this.apiClient.PUT("/blobs/{ownerId}/{region}/{key}", {
10
+ const { data, error } = await this.apiClient.PUT("/objects/{ownerId}/{region}/{key}", {
11
11
  params: { path: { ownerId, region: region, key } },
12
12
  body: { sizeBytes },
13
13
  });
@@ -21,7 +21,7 @@ class ObjectApi {
21
21
  };
22
22
  }
23
23
  async getDownloadUrl(ownerId, region, key) {
24
- const { data, error } = await this.apiClient.GET("/blobs/{ownerId}/{region}/{key}", {
24
+ const { data, error } = await this.apiClient.GET("/objects/{ownerId}/{region}/{key}", {
25
25
  params: { path: { ownerId, region: region, key } },
26
26
  });
27
27
  if (error) {
@@ -33,12 +33,29 @@ class ObjectApi {
33
33
  };
34
34
  }
35
35
  async delete(ownerId, region, key) {
36
- const { error } = await this.apiClient.DELETE("/blobs/{ownerId}/{region}/{key}", {
36
+ const { error } = await this.apiClient.DELETE("/objects/{ownerId}/{region}/{key}", {
37
37
  params: { path: { ownerId, region: region, key } },
38
38
  });
39
39
  if (error) {
40
40
  throw new errors_js_1.RenderError(`Failed to delete object: ${error.message || "Unknown error"}`);
41
41
  }
42
42
  }
43
+ async listObjects(ownerId, region, cursor, limit) {
44
+ const { data, error } = await this.apiClient.GET("/objects/{ownerId}/{region}", {
45
+ params: {
46
+ path: { ownerId, region: region },
47
+ query: { cursor, limit },
48
+ },
49
+ });
50
+ if (error) {
51
+ throw new errors_js_1.RenderError(`Failed to list objects: ${error.message || "Unknown error"}`);
52
+ }
53
+ const objects = data.items.map((item) => ({
54
+ key: item.object.key,
55
+ size: item.object.sizeBytes,
56
+ lastModified: new Date(item.object.lastModified),
57
+ }));
58
+ return { objects, hasNext: data.hasNext, nextCursor: data.nextCursor };
59
+ }
43
60
  }
44
61
  exports.ObjectApi = ObjectApi;
@@ -1,12 +1,17 @@
1
1
  import type { Client } from "openapi-fetch";
2
2
  import type { paths } from "../../generated/schema.js";
3
- import type { DeleteObjectInput, GetObjectInput, ObjectData, ObjectScope, PutObjectInput, PutObjectResult, ScopedDeleteObjectInput, ScopedGetObjectInput, ScopedPutObjectInput } from "./types.js";
3
+ import type { DeleteObjectInput, GetObjectInput, ListObjectsInput, ListObjectsResponse, ObjectData, ObjectScope, OptionalScope, PutObjectInput, PutObjectResult, ScopedDeleteObjectInput, ScopedGetObjectInput, ScopedListObjectsInput, ScopedPutObjectInput } from "./types.js";
4
4
  export declare class ObjectClient {
5
5
  private readonly apiClient;
6
- constructor(apiClient: Client<paths>);
7
- put(input: PutObjectInput): Promise<PutObjectResult>;
8
- get(input: GetObjectInput): Promise<ObjectData>;
9
- delete(input: DeleteObjectInput): Promise<void>;
6
+ private readonly defaultOwnerId?;
7
+ private readonly defaultRegion?;
8
+ constructor(apiClient: Client<paths>, defaultOwnerId?: string, defaultRegion?: string);
9
+ private resolveOwnerId;
10
+ private resolveRegion;
11
+ put(input: OptionalScope<PutObjectInput>): Promise<PutObjectResult>;
12
+ get(input: OptionalScope<GetObjectInput>): Promise<ObjectData>;
13
+ delete(input: OptionalScope<DeleteObjectInput>): Promise<void>;
14
+ list(input: OptionalScope<ListObjectsInput>): Promise<ListObjectsResponse>;
10
15
  scoped(scope: ObjectScope): ScopedObjectClient;
11
16
  private resolveSize;
12
17
  }
@@ -17,5 +22,6 @@ export declare class ScopedObjectClient {
17
22
  put(input: ScopedPutObjectInput): Promise<PutObjectResult>;
18
23
  get(input: ScopedGetObjectInput): Promise<ObjectData>;
19
24
  delete(input: ScopedDeleteObjectInput): Promise<void>;
25
+ list(input?: ScopedListObjectsInput): Promise<ListObjectsResponse>;
20
26
  }
21
27
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/experimental/object/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,UAAU,EACV,WAAW,EACX,cAAc,EACd,eAAe,EAEf,uBAAuB,EACvB,oBAAoB,EACpB,oBAAoB,EACrB,MAAM,YAAY,CAAC;AASpB,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC;IAgC/C,GAAG,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;IAgEpD,GAAG,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC;IA+C/C,MAAM,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAoCrD,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,kBAAkB;IAU9C,OAAO,CAAC,WAAW;CA0BpB;AAQD,qBAAa,kBAAkB;IAK3B,OAAO,CAAC,QAAQ,CAAC,KAAK;IAJxB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;gBAG1C,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,EACP,KAAK,EAAE,WAAW;IAqB/B,GAAG,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC;IAmB1D,GAAG,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,UAAU,CAAC;IAkBrD,MAAM,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;CAM5D"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/experimental/object/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,UAAU,EAEV,WAAW,EACX,aAAa,EACb,cAAc,EACd,eAAe,EAEf,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACrB,MAAM,YAAY,CAAC;AASpB,qBAAa,YAAY;IAKrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAJ5B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAkB;gBAG9B,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,EACzC,cAAc,CAAC,EAAE,MAAM,EACvB,aAAa,CAAC,EAAE,MAAM;IAMxB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,aAAa;IA4Cf,GAAG,CAAC,KAAK,EAAE,aAAa,CAAC,cAAc,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC;IAwFnE,GAAG,CAAC,KAAK,EAAE,aAAa,CAAC,cAAc,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;IAkD9D,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC,iBAAiB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAgD9D,IAAI,CAAC,KAAK,EAAE,aAAa,CAAC,gBAAgB,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAiDhF,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,kBAAkB;IAU9C,OAAO,CAAC,WAAW;CA0BpB;AAQD,qBAAa,kBAAkB;IAK3B,OAAO,CAAC,QAAQ,CAAC,KAAK;IAJxB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;gBAG1C,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,EACP,KAAK,EAAE,WAAW;IAqB/B,GAAG,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC;IAmB1D,GAAG,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,UAAU,CAAC;IAkBrD,MAAM,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBrD,IAAI,CAAC,KAAK,GAAE,sBAA2B,GAAG,OAAO,CAAC,mBAAmB,CAAC;CAM7E"}
@@ -1,18 +1,37 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ScopedObjectClient = exports.ObjectClient = void 0;
4
+ const node_stream_1 = require("node:stream");
4
5
  const errors_js_1 = require("../../errors.js");
5
6
  class ObjectClient {
6
- constructor(apiClient) {
7
+ constructor(apiClient, defaultOwnerId, defaultRegion) {
7
8
  this.apiClient = apiClient;
9
+ this.defaultOwnerId = defaultOwnerId;
10
+ this.defaultRegion = defaultRegion;
11
+ }
12
+ resolveOwnerId(ownerId) {
13
+ const resolved = ownerId || this.defaultOwnerId;
14
+ if (!resolved) {
15
+ throw new errors_js_1.RenderError("ownerId is required. Provide it as a parameter or set the RENDER_WORKSPACE_ID environment variable.");
16
+ }
17
+ return resolved;
18
+ }
19
+ resolveRegion(region) {
20
+ const resolved = region || this.defaultRegion;
21
+ if (!resolved) {
22
+ throw new errors_js_1.RenderError("region is required. Provide it as a parameter or set the RENDER_REGION environment variable.");
23
+ }
24
+ return resolved;
8
25
  }
9
26
  async put(input) {
27
+ const ownerId = this.resolveOwnerId(input.ownerId);
28
+ const region = this.resolveRegion(input.region);
10
29
  const size = this.resolveSize(input);
11
- const { data, error } = await this.apiClient.PUT("/blobs/{ownerId}/{region}/{key}", {
30
+ const { data, error } = await this.apiClient.PUT("/objects/{ownerId}/{region}/{key}", {
12
31
  params: {
13
32
  path: {
14
- ownerId: input.ownerId,
15
- region: input.region,
33
+ ownerId,
34
+ region: region,
16
35
  key: input.key,
17
36
  },
18
37
  },
@@ -21,16 +40,27 @@ class ObjectClient {
21
40
  if (error) {
22
41
  throw new errors_js_1.RenderError(`Failed to get upload URL: ${error.message || "Unknown error"}`);
23
42
  }
43
+ if (size !== data.maxSizeBytes) {
44
+ throw new errors_js_1.ClientError(`File size ${size} bytes does not match expected size of ${data.maxSizeBytes} bytes`, 400);
45
+ }
24
46
  const headers = {
25
47
  "Content-Length": size.toString(),
26
48
  };
27
49
  if (input.contentType) {
28
50
  headers["Content-Type"] = input.contentType;
29
51
  }
52
+ let body = input.data;
53
+ if (process.versions.bun && input.data instanceof node_stream_1.Readable) {
54
+ const chunks = [];
55
+ for await (const chunk of input.data) {
56
+ chunks.push(Buffer.from(chunk));
57
+ }
58
+ body = Buffer.concat(chunks);
59
+ }
30
60
  const response = await fetch(data.url, {
31
61
  method: "PUT",
32
62
  headers,
33
- body: input.data,
63
+ body,
34
64
  duplex: "half",
35
65
  });
36
66
  if (!response.ok) {
@@ -41,11 +71,13 @@ class ObjectClient {
41
71
  };
42
72
  }
43
73
  async get(input) {
44
- const { data, error } = await this.apiClient.GET("/blobs/{ownerId}/{region}/{key}", {
74
+ const ownerId = this.resolveOwnerId(input.ownerId);
75
+ const region = this.resolveRegion(input.region);
76
+ const { data, error } = await this.apiClient.GET("/objects/{ownerId}/{region}/{key}", {
45
77
  params: {
46
78
  path: {
47
- ownerId: input.ownerId,
48
- region: input.region,
79
+ ownerId,
80
+ region: region,
49
81
  key: input.key,
50
82
  },
51
83
  },
@@ -66,11 +98,13 @@ class ObjectClient {
66
98
  };
67
99
  }
68
100
  async delete(input) {
69
- const { error } = await this.apiClient.DELETE("/blobs/{ownerId}/{region}/{key}", {
101
+ const ownerId = this.resolveOwnerId(input.ownerId);
102
+ const region = this.resolveRegion(input.region);
103
+ const { error } = await this.apiClient.DELETE("/objects/{ownerId}/{region}/{key}", {
70
104
  params: {
71
105
  path: {
72
- ownerId: input.ownerId,
73
- region: input.region,
106
+ ownerId,
107
+ region: region,
74
108
  key: input.key,
75
109
  },
76
110
  },
@@ -79,6 +113,31 @@ class ObjectClient {
79
113
  throw new errors_js_1.RenderError(`Failed to delete object: ${error.message || "Unknown error"}`);
80
114
  }
81
115
  }
116
+ async list(input) {
117
+ const ownerId = this.resolveOwnerId(input.ownerId);
118
+ const region = this.resolveRegion(input.region);
119
+ const { data, error } = await this.apiClient.GET("/objects/{ownerId}/{region}", {
120
+ params: {
121
+ path: {
122
+ ownerId,
123
+ region: region,
124
+ },
125
+ query: {
126
+ cursor: input.cursor,
127
+ limit: input.limit,
128
+ },
129
+ },
130
+ });
131
+ if (error) {
132
+ throw new errors_js_1.RenderError(`Failed to list objects: ${error.message || "Unknown error"}`);
133
+ }
134
+ const objects = data.items.map((item) => ({
135
+ key: item.object.key,
136
+ size: item.object.sizeBytes,
137
+ lastModified: new Date(item.object.lastModified),
138
+ }));
139
+ return { objects, hasNext: data.hasNext, nextCursor: data.nextCursor };
140
+ }
82
141
  scoped(scope) {
83
142
  return new ScopedObjectClient(this.apiClient, scope);
84
143
  }
@@ -93,8 +152,8 @@ class ObjectClient {
93
152
  if (input.size === undefined) {
94
153
  throw new errors_js_1.RenderError("Size is required for stream and string inputs. Provide the size parameter.");
95
154
  }
96
- if (input.size <= 0) {
97
- throw new errors_js_1.RenderError("Size must be a positive integer");
155
+ if (input.size < 0) {
156
+ throw new errors_js_1.RenderError("Size must be a non-negative integer");
98
157
  }
99
158
  return input.size;
100
159
  }
@@ -123,5 +182,11 @@ class ScopedObjectClient {
123
182
  ...input,
124
183
  });
125
184
  }
185
+ async list(input = {}) {
186
+ return this.objectClient.list({
187
+ ...this.scope,
188
+ ...input,
189
+ });
190
+ }
126
191
  }
127
192
  exports.ScopedObjectClient = ScopedObjectClient;
@@ -0,0 +1,3 @@
1
+ import type { ObjectClient } from "./client.js";
2
+ export declare function objectStorageCrudCheck(client: ObjectClient, ownerId: `tea-${string}`, region: string): Promise<string>;
3
+ //# sourceMappingURL=e2e-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"e2e-helpers.d.ts","sourceRoot":"","sources":["../../../src/experimental/object/e2e-helpers.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAchD,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,OAAO,MAAM,EAAE,EACxB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,CAsEjB"}