From 82a54e2f9f4f00134fd37d956ea3a42f6e46204a Mon Sep 17 00:00:00 2001 From: Tony Deng Date: Mon, 24 Nov 2025 14:26:51 -0800 Subject: [PATCH 1/8] apparently need re-export to correctly enable `import { RunloopSDK } from '@runloop/api-client'` --- src/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/index.ts b/src/index.ts index 5c1aed960..5ea97a180 100644 --- a/src/index.ts +++ b/src/index.ts @@ -616,6 +616,9 @@ export { UnprocessableEntityError, } from './error'; +// Re-export RunloopSDK from sdk.ts +export { RunloopSDK } from './sdk'; + /** * @example * ```typescript From 4bff669d7d8e7d9d154d308a7f5aa04f5ffe3159 Mon Sep 17 00:00:00 2001 From: Tony Deng Date: Mon, 24 Nov 2025 14:44:26 -0800 Subject: [PATCH 2/8] attempt 2 --- src/index.ts | 3 --- src/sdk.ts | 12 ------------ 2 files changed, 15 deletions(-) diff --git a/src/index.ts b/src/index.ts index 5ea97a180..5c1aed960 100644 --- a/src/index.ts +++ b/src/index.ts @@ -616,9 +616,6 @@ export { UnprocessableEntityError, } from './error'; -// Re-export RunloopSDK from sdk.ts -export { RunloopSDK } from './sdk'; - /** * @example * ```typescript diff --git a/src/sdk.ts b/src/sdk.ts index e7c8943f8..388f1259d 100644 --- a/src/sdk.ts +++ b/src/sdk.ts @@ -595,18 +595,6 @@ export class StorageObjectOps { } } -// @deprecated Use {@link RunloopSDK} instead. -/** - * @deprecated Use {@link RunloopSDK} instead. - * @example - * ```typescript - * import { RunloopSDK } from '@runloop/api-client'; - * const sdk = new RunloopSDK(); - * const devbox = await sdk.devbox.create({ name: 'my-devbox' }); - * ``` - */ -export default Runloop; - export declare namespace RunloopSDK { export { RunloopSDK as Client, From 5bfd6e8f294e3f6846c05e9a1573e9bdb2b269d4 Mon Sep 17 00:00:00 2001 From: Tony Deng Date: Mon, 24 Nov 2025 15:49:04 -0800 Subject: [PATCH 3/8] temp --- src/core.ts | 2 +- src/resources/blueprints.ts | 8 ++++++-- src/sdk.ts | 13 ------------- src/sdk/storage-object.ts | 13 ++++++++----- src/uploads.ts | 5 ++--- tests/api-resources/agents.test.ts | 2 +- tests/index.test.ts | 4 ++-- 7 files changed, 20 insertions(+), 27 deletions(-) diff --git a/src/core.ts b/src/core.ts index df432015a..3e2bb7786 100644 --- a/src/core.ts +++ b/src/core.ts @@ -896,7 +896,7 @@ const getPlatformProperties = (): PlatformProperties => { 'X-Stainless-Arch': normalizeArch(Deno.build.arch), 'X-Stainless-Runtime': 'deno', 'X-Stainless-Runtime-Version': - typeof Deno.version === 'string' ? Deno.version : (Deno.version?.deno ?? 'unknown'), + typeof Deno.version === 'string' ? Deno.version : Deno.version?.deno ?? 'unknown', }; } if (typeof EdgeRuntime !== 'undefined') { diff --git a/src/resources/blueprints.ts b/src/resources/blueprints.ts index 993c3f0d0..cf0971bd4 100644 --- a/src/resources/blueprints.ts +++ b/src/resources/blueprints.ts @@ -56,7 +56,9 @@ function validateFileMounts(fileMounts?: { [key: string]: string } | null): Arra const over = sizeBytes - FILE_MOUNT_MAX_SIZE_BYTES; errors.push( `file_mount '${mountPath}' is ${formatBytes(over)} over the limit (` + - `${formatBytes(sizeBytes)} / ${formatBytes(FILE_MOUNT_MAX_SIZE_BYTES)}). Use object_mounts instead.`, + `${formatBytes(sizeBytes)} / ${formatBytes( + FILE_MOUNT_MAX_SIZE_BYTES, + )}). Use object_mounts instead.`, ); } totalSizeBytes += sizeBytes; @@ -66,7 +68,9 @@ function validateFileMounts(fileMounts?: { [key: string]: string } | null): Arra const totalOver = totalSizeBytes - FILE_MOUNT_TOTAL_MAX_SIZE_BYTES; errors.push( `total file_mounts size is ${formatBytes(totalOver)} over the limit (` + - `${formatBytes(totalSizeBytes)} / ${formatBytes(FILE_MOUNT_TOTAL_MAX_SIZE_BYTES)}). Use object_mounts instead.`, + `${formatBytes(totalSizeBytes)} / ${formatBytes( + FILE_MOUNT_TOTAL_MAX_SIZE_BYTES, + )}). Use object_mounts instead.`, ); } diff --git a/src/sdk.ts b/src/sdk.ts index 388f1259d..424cc2337 100644 --- a/src/sdk.ts +++ b/src/sdk.ts @@ -595,19 +595,6 @@ export class StorageObjectOps { } } -export declare namespace RunloopSDK { - export { - RunloopSDK as Client, - DevboxOps as DevboxOps, - BlueprintOps as BlueprintOps, - SnapshotOps as SnapshotOps, - StorageObjectOps as StorageObjectOps, - Devbox as Devbox, - Blueprint as Blueprint, - Snapshot as Snapshot, - StorageObject as StorageObject, - }; -} // Export SDK classes from sdk/sdk.ts - these are separate from RunloopSDK to avoid circular dependencies export { Devbox, diff --git a/src/sdk/storage-object.ts b/src/sdk/storage-object.ts index 2f968e299..bfa995770 100644 --- a/src/sdk/storage-object.ts +++ b/src/sdk/storage-object.ts @@ -9,6 +9,7 @@ import type { import * as fs from 'node:fs/promises'; import * as path from 'node:path'; import * as tar from 'tar'; +import fetch from 'node-fetch'; // Extract the content type from the API types type ContentType = ObjectCreateParams['content_type']; @@ -312,7 +313,7 @@ export class StorageObject { try { const response = await fetch(uploadUrl, { method: 'PUT', - body: new Blob([text]), + body: text, }); if (!response.ok) { @@ -387,7 +388,7 @@ export class StorageObject { try { const response = await fetch(uploadUrl, { method: 'PUT', - body: new Blob([buffer]), + body: buffer, }); if (!response.ok) { @@ -430,14 +431,16 @@ export class StorageObject { let buffer; try { const tarStream = tar.create({ gzip: true, cwd: dirPath }, ['.']); - const chunks = []; + const chunks: Buffer[] = []; for await (const chunk of tarStream) { chunks.push(chunk); } - buffer = Buffer.concat(chunks); + buffer = Buffer.concat(chunks as readonly Uint8Array[]); } catch (error) { throw new Error( - `Failed to create tarball from directory ${dirPath}: ${error instanceof Error ? error.message : 'Unknown error'}`, + `Failed to create tarball from directory ${dirPath}: ${ + error instanceof Error ? error.message : 'Unknown error' + }`, ); } diff --git a/src/uploads.ts b/src/uploads.ts index 2264b5704..8fd2154d4 100644 --- a/src/uploads.ts +++ b/src/uploads.ts @@ -156,9 +156,8 @@ async function getBytes(value: ToFileInput): Promise> { } } else { throw new Error( - `Unexpected data type: ${typeof value}; constructor: ${ - value?.constructor?.name - }; props: ${propsForError(value)}`, + `Unexpected data type: ${typeof value}; constructor: ${value?.constructor + ?.name}; props: ${propsForError(value)}`, ); } diff --git a/tests/api-resources/agents.test.ts b/tests/api-resources/agents.test.ts index 6513f221d..2d263095e 100644 --- a/tests/api-resources/agents.test.ts +++ b/tests/api-resources/agents.test.ts @@ -1,6 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import Runloop from '@runloop/api-client'; +import { Runloop } from '@runloop/api-client'; import { Response } from 'node-fetch'; const client = new Runloop({ diff --git a/tests/index.test.ts b/tests/index.test.ts index 6db7c9732..8ce09d171 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -285,8 +285,8 @@ describe('retries', () => { let count = 0; const testFetch = async (url: RequestInfo, { signal }: RequestInit = {}): Promise => { if (count++ === 0) { - return new Promise((resolve, reject) => - signal?.addEventListener('abort', () => reject(new Error('timed out'))), + return new Promise( + (resolve, reject) => signal?.addEventListener('abort', () => reject(new Error('timed out'))), ); } return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } }); From de59752239546d440e4298a84b36e7300e2db233 Mon Sep 17 00:00:00 2001 From: Tony Deng Date: Mon, 24 Nov 2025 15:52:32 -0800 Subject: [PATCH 4/8] ok --- src/sdk.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/sdk.ts b/src/sdk.ts index 424cc2337..af2240048 100644 --- a/src/sdk.ts +++ b/src/sdk.ts @@ -595,6 +595,19 @@ export class StorageObjectOps { } } +export declare namespace RunloopSDK { + export { + DevboxOps as DevboxOps, + BlueprintOps as BlueprintOps, + SnapshotOps as SnapshotOps, + StorageObjectOps as StorageObjectOps, + Devbox as Devbox, + Blueprint as Blueprint, + Snapshot as Snapshot, + StorageObject as StorageObject, + }; +} + // Export SDK classes from sdk/sdk.ts - these are separate from RunloopSDK to avoid circular dependencies export { Devbox, From 2c5c73b490b8ba5d48d818775b53be702de396e2 Mon Sep 17 00:00:00 2001 From: Tony Deng Date: Mon, 24 Nov 2025 16:01:08 -0800 Subject: [PATCH 5/8] fix lint --- tests/lib/streaming-reconnection.test.ts | 5 +++-- tests/smoketests/object-oriented/storage-object.test.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/lib/streaming-reconnection.test.ts b/tests/lib/streaming-reconnection.test.ts index d6f72abe7..97e169f0f 100644 --- a/tests/lib/streaming-reconnection.test.ts +++ b/tests/lib/streaming-reconnection.test.ts @@ -1,3 +1,4 @@ +import { ReadableStream, ReadableStreamDefaultController } from 'stream/web'; import { APIError } from '../../src/error'; import { withStreamAutoReconnect } from '../../src/lib/streaming-reconnection'; import { Stream } from '../../src/streaming'; @@ -37,7 +38,7 @@ function createStreamFactory( let lastDeliveredOffset = offset ?? 0; const readable = new ReadableStream({ - async pull(controller) { + async pull(controller: ReadableStreamDefaultController) { const trigger = takeTrigger(lastDeliveredOffset); if (trigger) { controller.error(trigger.error ?? new Error('Synthetic failure')); @@ -220,7 +221,7 @@ describe('withStreamAutoReconnect', () => { creatorCalls += 1; const abortController = new AbortController(); const readable = new ReadableStream({ - pull(controller) { + pull(controller: ReadableStreamDefaultController) { controller.error(error); }, cancel() { diff --git a/tests/smoketests/object-oriented/storage-object.test.ts b/tests/smoketests/object-oriented/storage-object.test.ts index b28708ed9..fde73ab30 100644 --- a/tests/smoketests/object-oriented/storage-object.test.ts +++ b/tests/smoketests/object-oriented/storage-object.test.ts @@ -257,7 +257,7 @@ describe('smoketest: object-oriented storage object', () => { }, }); await tarStream.write(data); - const content = Buffer.concat(contentChunks); + const content = Buffer.concat(contentChunks as readonly Uint8Array[]); expect(content.toString('utf-8')).toBe('Hello from uploadFromDir!'); } finally { await fs.unlink(contentPath).catch(() => {}); From 24002f4f1cb9419320acfa5bdb8fb697e22a7c18 Mon Sep 17 00:00:00 2001 From: Tony Deng Date: Mon, 24 Nov 2025 16:12:29 -0800 Subject: [PATCH 6/8] fix again --- src/sdk.ts | 1 + src/sdk/storage-object.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sdk.ts b/src/sdk.ts index af2240048..c46b7fb28 100644 --- a/src/sdk.ts +++ b/src/sdk.ts @@ -597,6 +597,7 @@ export class StorageObjectOps { export declare namespace RunloopSDK { export { + RunloopSDK as Client, DevboxOps as DevboxOps, BlueprintOps as BlueprintOps, SnapshotOps as SnapshotOps, diff --git a/src/sdk/storage-object.ts b/src/sdk/storage-object.ts index bfa995770..7ecca31be 100644 --- a/src/sdk/storage-object.ts +++ b/src/sdk/storage-object.ts @@ -6,10 +6,10 @@ import type { ObjectDownloadURLView, ObjectListParams, } from '../resources/objects'; +import { fetch } from '../_shims/index'; import * as fs from 'node:fs/promises'; import * as path from 'node:path'; import * as tar from 'tar'; -import fetch from 'node-fetch'; // Extract the content type from the API types type ContentType = ObjectCreateParams['content_type']; From 831165be84767c9d43811e4147b3997e3c7bdf2d Mon Sep 17 00:00:00 2001 From: Tony Deng Date: Mon, 24 Nov 2025 16:14:16 -0800 Subject: [PATCH 7/8] run fix --- src/core.ts | 2 +- src/uploads.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core.ts b/src/core.ts index 3e2bb7786..df432015a 100644 --- a/src/core.ts +++ b/src/core.ts @@ -896,7 +896,7 @@ const getPlatformProperties = (): PlatformProperties => { 'X-Stainless-Arch': normalizeArch(Deno.build.arch), 'X-Stainless-Runtime': 'deno', 'X-Stainless-Runtime-Version': - typeof Deno.version === 'string' ? Deno.version : Deno.version?.deno ?? 'unknown', + typeof Deno.version === 'string' ? Deno.version : (Deno.version?.deno ?? 'unknown'), }; } if (typeof EdgeRuntime !== 'undefined') { diff --git a/src/uploads.ts b/src/uploads.ts index 8fd2154d4..5fde000d0 100644 --- a/src/uploads.ts +++ b/src/uploads.ts @@ -156,8 +156,7 @@ async function getBytes(value: ToFileInput): Promise> { } } else { throw new Error( - `Unexpected data type: ${typeof value}; constructor: ${value?.constructor - ?.name}; props: ${propsForError(value)}`, + `Unexpected data type: ${typeof value}; constructor: ${value?.constructor?.name}; props: ${propsForError(value)}`, ); } From f7c7a7da0a328f6c79e747192249a5cbc303da67 Mon Sep 17 00:00:00 2001 From: Tony Deng Date: Mon, 24 Nov 2025 16:19:02 -0800 Subject: [PATCH 8/8] temp --- tests/objects/storage-object.test.ts | 85 ++++++++++++++++------------ 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/tests/objects/storage-object.test.ts b/tests/objects/storage-object.test.ts index d3d2e5572..9c71e62df 100644 --- a/tests/objects/storage-object.test.ts +++ b/tests/objects/storage-object.test.ts @@ -4,8 +4,17 @@ import type { ObjectView, ObjectDownloadURLView } from '../../src/resources/obje // Mock the Runloop client jest.mock('../../src/index'); -// Mock fetch globally -(global as any).fetch = jest.fn(); +// Mock node-fetch (used by shims in Node.js environment) +// We'll get a reference to the mock in beforeEach +jest.mock('node-fetch', () => { + const mockFn = jest.fn(); + // Store on global so we can access it + (global as any).__nodeFetchMock = mockFn; + return { + default: mockFn, + __esModule: true, + }; +}); // Mock fs and path modules jest.mock('node:fs/promises', () => ({ @@ -32,11 +41,17 @@ describe('StorageObject (New API)', () => { let mockObjectData: ObjectView; let mockFs: any; let mockPath: any; + let mockFetch: jest.Mock; beforeEach(() => { // Get mocked modules mockFs = require('node:fs/promises'); mockPath = require('node:path'); + + // Get the mocked fetch function from node-fetch + const nodeFetch = require('node-fetch'); + mockFetch = nodeFetch.default; + (global as any).fetch = mockFetch; // Create mock client instance with proper structure mockClient = { @@ -93,7 +108,7 @@ describe('StorageObject (New API)', () => { }; // Reset fetch mock - ((global as any).fetch as jest.Mock).mockReset(); + mockFetch.mockReset(); }); describe('create', () => { @@ -234,11 +249,11 @@ describe('StorageObject (New API)', () => { statusText: 'OK', }; - ((global as any).fetch as jest.Mock).mockResolvedValue(mockFetchResponse); + mockFetch.mockResolvedValue(mockFetchResponse); await storageObject.uploadContent('Hello, World!'); - expect((global as any).fetch).toHaveBeenCalledWith(mockObjectData.upload_url, { + expect(mockFetch).toHaveBeenCalledWith(mockObjectData.upload_url, { method: 'PUT', body: Buffer.from('Hello, World!', 'utf-8'), }); @@ -254,12 +269,12 @@ describe('StorageObject (New API)', () => { statusText: 'OK', }; - ((global as any).fetch as jest.Mock).mockResolvedValue(mockFetchResponse); + mockFetch.mockResolvedValue(mockFetchResponse); const buffer = Buffer.from([0x89, 0x50, 0x4e, 0x47]); await storageObject.uploadContent(buffer); - expect((global as any).fetch).toHaveBeenCalledWith(mockObjectData.upload_url, { + expect(mockFetch).toHaveBeenCalledWith(mockObjectData.upload_url, { method: 'PUT', body: buffer, }); @@ -284,7 +299,7 @@ describe('StorageObject (New API)', () => { text: jest.fn().mockResolvedValue('Forbidden'), }; - ((global as any).fetch as jest.Mock).mockResolvedValue(mockFetchResponse); + mockFetch.mockResolvedValue(mockFetchResponse); await expect(storageObject.uploadContent('test')).rejects.toThrow('Upload failed: 403'); }); @@ -355,11 +370,11 @@ describe('StorageObject (New API)', () => { text: jest.fn().mockResolvedValue('File contents'), }; - ((global as any).fetch as jest.Mock).mockResolvedValue(mockFetchResponse); + mockFetch.mockResolvedValue(mockFetchResponse); const content = await storageObject.downloadAsText(); - expect((global as any).fetch).toHaveBeenCalledWith(mockDownloadUrl.download_url); + expect(mockFetch).toHaveBeenCalledWith(mockDownloadUrl.download_url); expect(content).toBe('File contents'); }); @@ -376,7 +391,7 @@ describe('StorageObject (New API)', () => { statusText: 'Not Found', }; - ((global as any).fetch as jest.Mock).mockResolvedValue(mockFetchResponse); + mockFetch.mockResolvedValue(mockFetchResponse); await expect(storageObject.downloadAsText()).rejects.toThrow('Download failed: 404 Not Found'); }); @@ -396,11 +411,11 @@ describe('StorageObject (New API)', () => { arrayBuffer: jest.fn().mockResolvedValue(mockArrayBuffer), }; - ((global as any).fetch as jest.Mock).mockResolvedValue(mockFetchResponse); + mockFetch.mockResolvedValue(mockFetchResponse); const buffer = await storageObject.downloadAsBuffer(); - expect((global as any).fetch).toHaveBeenCalledWith(mockDownloadUrl.download_url); + expect(mockFetch).toHaveBeenCalledWith(mockDownloadUrl.download_url); expect(Buffer.isBuffer(buffer)).toBe(true); expect(buffer.length).toBe(4); }); @@ -435,7 +450,7 @@ describe('StorageObject (New API)', () => { // Upload - mock getInfo for uploadContent mockClient.objects.retrieve.mockResolvedValue(mockObjectData); - ((global as any).fetch as jest.Mock).mockResolvedValue({ ok: true }); + mockFetch.mockResolvedValue({ ok: true }); await obj.uploadContent('Test content'); // Complete @@ -448,7 +463,7 @@ describe('StorageObject (New API)', () => { download_url: 'https://s3.example.com/download/workflow-test.txt', }; mockClient.objects.download.mockResolvedValue(mockDownloadUrl); - ((global as any).fetch as jest.Mock).mockResolvedValue({ + mockFetch.mockResolvedValue({ ok: true, text: jest.fn().mockResolvedValue('Test content'), }); @@ -463,7 +478,7 @@ describe('StorageObject (New API)', () => { // Clear all mocks jest.clearAllMocks(); // Reset global fetch mock - ((global as any).fetch as jest.Mock).mockClear(); + mockFetch.mockClear(); }); it('should upload a text file with auto-detected content-type', async () => { @@ -479,7 +494,7 @@ describe('StorageObject (New API)', () => { mockClient.objects.retrieve.mockResolvedValue(mockObjectInfo); mockClient.objects.complete.mockResolvedValue(mockCompletedData); - ((global as any).fetch as jest.Mock).mockResolvedValue({ + mockFetch.mockResolvedValue({ ok: true, status: 200, statusText: 'OK', @@ -509,7 +524,7 @@ describe('StorageObject (New API)', () => { mockClient.objects.retrieve.mockResolvedValue(mockObjectInfo); mockClient.objects.complete.mockResolvedValue(mockCompletedData); - ((global as any).fetch as jest.Mock).mockResolvedValue({ + mockFetch.mockResolvedValue({ ok: true, status: 200, statusText: 'OK', @@ -559,7 +574,7 @@ describe('StorageObject (New API)', () => { mockClient.objects.create.mockResolvedValue(mockObjectData); mockClient.objects.retrieve.mockResolvedValue(mockObjectInfo); - ((global as any).fetch as jest.Mock).mockResolvedValue({ + mockFetch.mockResolvedValue({ ok: false, status: 500, statusText: 'Internal Server Error', @@ -583,7 +598,7 @@ describe('StorageObject (New API)', () => { mockClient.objects.retrieve.mockResolvedValue(mockObjectInfo); mockClient.objects.complete.mockResolvedValue(mockCompletedData); - ((global as any).fetch as jest.Mock).mockResolvedValue({ + mockFetch.mockResolvedValue({ ok: true, status: 200, statusText: 'OK', @@ -610,7 +625,7 @@ describe('StorageObject (New API)', () => { // Clear all mocks jest.clearAllMocks(); // Reset global fetch mock - ((global as any).fetch as jest.Mock).mockClear(); + mockFetch.mockClear(); }); it('should upload text content with text content-type', async () => { @@ -623,7 +638,7 @@ describe('StorageObject (New API)', () => { mockClient.objects.retrieve.mockResolvedValue(mockObjectInfo); mockClient.objects.complete.mockResolvedValue(mockCompletedData); - ((global as any).fetch as jest.Mock).mockResolvedValue({ + mockFetch.mockResolvedValue({ ok: true, status: 200, statusText: 'OK', @@ -636,7 +651,7 @@ describe('StorageObject (New API)', () => { undefined, ); // uploadFromText uses Blob for fetch body - const fetchCalls = ((global as any).fetch as jest.Mock).mock.calls; + const fetchCalls = mockFetch.mock.calls; expect(fetchCalls[0][0]).toBe('https://upload.example.com/text'); expect(fetchCalls[0][1].method).toBe('PUT'); expect(fetchCalls[0][1].body).toBeInstanceOf(Blob); @@ -654,7 +669,7 @@ describe('StorageObject (New API)', () => { mockClient.objects.retrieve.mockResolvedValue(mockObjectInfo); mockClient.objects.complete.mockResolvedValue(mockCompletedData); - ((global as any).fetch as jest.Mock).mockResolvedValue({ + mockFetch.mockResolvedValue({ ok: true, status: 200, statusText: 'OK', @@ -679,7 +694,7 @@ describe('StorageObject (New API)', () => { mockClient.objects.create.mockResolvedValue(mockObjectData); mockClient.objects.retrieve.mockResolvedValue(mockObjectInfo); - ((global as any).fetch as jest.Mock).mockResolvedValue({ + mockFetch.mockResolvedValue({ ok: false, status: 403, statusText: 'Forbidden', @@ -700,7 +715,7 @@ describe('StorageObject (New API)', () => { mockClient.objects.retrieve.mockResolvedValue(mockObjectInfo); mockClient.objects.complete.mockResolvedValue(mockCompletedData); - ((global as any).fetch as jest.Mock).mockResolvedValue({ + mockFetch.mockResolvedValue({ ok: true, status: 200, statusText: 'OK', @@ -721,7 +736,7 @@ describe('StorageObject (New API)', () => { // Clear all mocks jest.clearAllMocks(); // Reset global fetch mock - ((global as any).fetch as jest.Mock).mockClear(); + mockFetch.mockClear(); }); it('should upload buffer with specified content-type and name', async () => { @@ -734,7 +749,7 @@ describe('StorageObject (New API)', () => { mockClient.objects.retrieve.mockResolvedValue(mockObjectInfo); mockClient.objects.complete.mockResolvedValue(mockCompletedData); - ((global as any).fetch as jest.Mock).mockResolvedValue({ + mockFetch.mockResolvedValue({ ok: true, status: 200, statusText: 'OK', @@ -749,7 +764,7 @@ describe('StorageObject (New API)', () => { { metadata: { source: 'buffer' } }, ); // uploadFromBuffer uses Blob for fetch body - const fetchCalls = ((global as any).fetch as jest.Mock).mock.calls; + const fetchCalls = mockFetch.mock.calls; expect(fetchCalls[0][0]).toBe('https://upload.example.com/buffer'); expect(fetchCalls[0][1].method).toBe('PUT'); expect(fetchCalls[0][1].body).toBeInstanceOf(Blob); @@ -779,7 +794,7 @@ describe('StorageObject (New API)', () => { mockClient.objects.create.mockResolvedValue(mockObjectData); mockClient.objects.retrieve.mockResolvedValue(mockObjectInfo); - ((global as any).fetch as jest.Mock).mockResolvedValue({ + mockFetch.mockResolvedValue({ ok: false, status: 403, statusText: 'Forbidden', @@ -800,7 +815,7 @@ describe('StorageObject (New API)', () => { mockClient.objects.retrieve.mockResolvedValue(mockObjectInfo); mockClient.objects.complete.mockResolvedValue(mockCompletedData); - ((global as any).fetch as jest.Mock).mockResolvedValue({ + mockFetch.mockResolvedValue({ ok: true, status: 200, statusText: 'OK', @@ -823,7 +838,7 @@ describe('StorageObject (New API)', () => { // Clear all mocks jest.clearAllMocks(); // Reset global fetch mock - ((global as any).fetch as jest.Mock).mockClear(); + mockFetch.mockClear(); // Get tar mock mockTar = require('tar'); }); @@ -848,7 +863,7 @@ describe('StorageObject (New API)', () => { mockClient.objects.retrieve.mockResolvedValue(mockObjectInfo); mockClient.objects.complete.mockResolvedValue(mockCompletedData); - ((global as any).fetch as jest.Mock).mockResolvedValue({ + mockFetch.mockResolvedValue({ ok: true, status: 200, statusText: 'OK', @@ -885,7 +900,7 @@ describe('StorageObject (New API)', () => { mockClient.objects.create.mockResolvedValue(mockObjectData); mockClient.objects.complete.mockResolvedValue(mockCompletedData); - ((global as any).fetch as jest.Mock).mockResolvedValue({ + mockFetch.mockResolvedValue({ ok: true, status: 200, statusText: 'OK', @@ -944,7 +959,7 @@ describe('StorageObject (New API)', () => { const mockObjectData = { id: 'dir-999', upload_url: 'https://upload.example.com/dir' }; mockClient.objects.create.mockResolvedValue(mockObjectData); - ((global as any).fetch as jest.Mock).mockResolvedValue({ + mockFetch.mockResolvedValue({ ok: false, status: 500, statusText: 'Internal Server Error',