@yetter/client 0.0.12 → 0.0.14
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.
- package/README.md +59 -3
- package/dist/api.d.ts +3 -0
- package/dist/api.js +110 -60
- package/dist/client.d.ts +27 -52
- package/dist/client.js +327 -256
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/types.d.ts +18 -0
- package/package.json +6 -1
- package/bowow2.jpeg +0 -0
- package/examples/stream.ts +0 -54
- package/examples/submit.ts +0 -80
- package/examples/subscribe.ts +0 -41
- package/examples/upload-and-generate.ts +0 -39
- package/src/api.ts +0 -159
- package/src/client.ts +0 -697
- package/src/index.ts +0 -12
- package/src/types.ts +0 -192
- package/tsconfig.json +0 -13
package/README.md
CHANGED
|
@@ -8,6 +8,16 @@ The Yetter JS Client provides a convenient way to interact with the Yetter API f
|
|
|
8
8
|
npm install @yetter/client
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
## Testing
|
|
12
|
+
|
|
13
|
+
Run the local test suite:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm test
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Tests are automatically omitted from npm releases because `package.json` uses a `files` whitelist (`dist`, `README.md`).
|
|
20
|
+
|
|
11
21
|
## Authentication
|
|
12
22
|
|
|
13
23
|
The client requires a Yetter API key for authentication. Ensure you have the `YTR_API_KEY` or `REACT_APP_YTR_API_KEY` environment variable set:
|
|
@@ -30,6 +40,44 @@ yetter.configure({
|
|
|
30
40
|
|
|
31
41
|
If `yetter.configure()` is used, it will override any API key found in environment variables for subsequent API calls. If neither environment variables are set nor `yetter.configure()` is called with an API key, an error will be thrown when attempting to make an API call.
|
|
32
42
|
|
|
43
|
+
## Instance Client (Recommended for Concurrent Apps)
|
|
44
|
+
|
|
45
|
+
For server or multi-tenant workloads, prefer creating isolated client instances instead of sharing global static state.
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { YetterClient } from "@yetter/client";
|
|
49
|
+
|
|
50
|
+
const teamAClient = new YetterClient({ apiKey: process.env.TEAM_A_YTR_API_KEY! });
|
|
51
|
+
const teamBClient = new YetterClient({ apiKey: process.env.TEAM_B_YTR_API_KEY! });
|
|
52
|
+
|
|
53
|
+
const result = await teamAClient.subscribe("ytr-ai/qwen/image-edit/i2i", {
|
|
54
|
+
input: {
|
|
55
|
+
prompt: "Transform this image to watercolor painting style",
|
|
56
|
+
image_url: ["https://example.com/input.jpg"],
|
|
57
|
+
num_inference_steps: 28,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Running i2i Example Scripts
|
|
63
|
+
|
|
64
|
+
For image-to-image models (such as `ytr-ai/qwen/image-edit/i2i`), provide both a prompt and an input image.
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
export YTR_API_KEY="your_api_key_here"
|
|
68
|
+
export YTR_MODEL="ytr-ai/qwen/image-edit/i2i"
|
|
69
|
+
export YTR_IMAGE_PATH="./bowow2.jpeg"
|
|
70
|
+
export YTR_PROMPT="Transform this image to watercolor painting style"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Then run one of:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npm run test:submit
|
|
77
|
+
npm run test:subscribe
|
|
78
|
+
npm run test:stream
|
|
79
|
+
```
|
|
80
|
+
|
|
33
81
|
## Core Functionalities
|
|
34
82
|
|
|
35
83
|
The client is available via the `yetter` object imported from `yetter-js` (or the relevant path to `client.js`/`client.ts` if used directly).
|
|
@@ -48,6 +96,7 @@ This function submits an image generation request and long-polls for the result.
|
|
|
48
96
|
- Timeout after 30 minutes, with an attempt to cancel the job.
|
|
49
97
|
- Optional `onQueueUpdate` callback for real-time feedback on queue position and status.
|
|
50
98
|
- Optional `logs` flag to include logs in status updates.
|
|
99
|
+
- Optional `pollIntervalMs` to control status polling interval (default: 2000ms).
|
|
51
100
|
|
|
52
101
|
**Example (from `examples/subscribe.ts`):**
|
|
53
102
|
|
|
@@ -97,11 +146,18 @@ This function submits an image generation request and returns an async iterable
|
|
|
97
146
|
**Features:**
|
|
98
147
|
|
|
99
148
|
* Initiates a request and establishes an SSE connection.
|
|
149
|
+
* Automatically falls back to status polling if SSE transport repeatedly fails.
|
|
100
150
|
* Provides an `AsyncIterator` (`Symbol.asyncIterator`) to loop through status events (`StreamEvent`).
|
|
101
151
|
* A `done()` method: Returns a Promise that resolves with the final `GetResponseResponse` **after the server emits `event: done`** (successful completion), or rejects if the server emits `event: error` or the final response cannot be fetched.
|
|
102
152
|
* A `cancel()` method: Requests cancellation on the backend; the stream naturally ends when the server emits `data` (with `status: "CANCELLED"`) followed by `event: done`.
|
|
103
153
|
* A `getRequestId()` method: Returns the request ID for the stream.
|
|
104
154
|
|
|
155
|
+
**Transport options (`StreamOptions`):**
|
|
156
|
+
|
|
157
|
+
* `disableSse?: boolean` - skip SSE and use polling only.
|
|
158
|
+
* `pollIntervalMs?: number` - polling interval for fallback/forced polling (default: `2000`).
|
|
159
|
+
* `sseMaxConsecutiveErrors?: number` - number of transport errors tolerated before fallback (default: `3`).
|
|
160
|
+
|
|
105
161
|
**Events (`StreamEvent`):**
|
|
106
162
|
Each event pushed by the stream is an object typically including:
|
|
107
163
|
|
|
@@ -199,11 +255,11 @@ async function main() {
|
|
|
199
255
|
console.log("Request ID:", request_id);
|
|
200
256
|
|
|
201
257
|
if (request_id) {
|
|
202
|
-
console.log(`\n--- Polling for status of Request ID: ${request_id} ---`);
|
|
258
|
+
console.log(`\n--- Polling for status of Request ID: ${request_id} (10-minute timeout) ---`);
|
|
203
259
|
// Polling logic:
|
|
204
260
|
let success = false;
|
|
205
261
|
const startTime = Date.now();
|
|
206
|
-
const timeoutMilliseconds =
|
|
262
|
+
const timeoutMilliseconds = 10 * 60 * 1000; // 10 minutes
|
|
207
263
|
const pollIntervalMilliseconds = 10 * 1000; // Poll every 10 seconds
|
|
208
264
|
|
|
209
265
|
while (Date.now() - startTime < timeoutMilliseconds) {
|
|
@@ -259,7 +315,7 @@ const uploadResult = await yetter.uploadFile("./input-image.jpg", {
|
|
|
259
315
|
const result = await yetter.subscribe("ytr-ai/model/i2i", {
|
|
260
316
|
input: {
|
|
261
317
|
prompt: "Transform to anime style",
|
|
262
|
-
|
|
318
|
+
image_url: [uploadResult.url],
|
|
263
319
|
},
|
|
264
320
|
});
|
|
265
321
|
```
|
package/dist/api.d.ts
CHANGED
|
@@ -4,6 +4,9 @@ export declare class YetterImageClient {
|
|
|
4
4
|
private endpoint;
|
|
5
5
|
constructor(options: ClientOptions);
|
|
6
6
|
getApiEndpoint(): string;
|
|
7
|
+
private getAuthHeaders;
|
|
8
|
+
private readErrorBody;
|
|
9
|
+
private requestJson;
|
|
7
10
|
generate(body: GenerateRequest): Promise<GenerateResponse>;
|
|
8
11
|
getStatus(body: GetStatusRequest): Promise<GetStatusResponse>;
|
|
9
12
|
cancel(body: CancelRequest): Promise<CancelResponse>;
|
package/dist/api.js
CHANGED
|
@@ -1,4 +1,29 @@
|
|
|
1
1
|
import fetch from "cross-fetch";
|
|
2
|
+
const DEFAULT_TIMEOUT_MS = 30000;
|
|
3
|
+
const MAX_RETRY_DELAY_MS = 5000;
|
|
4
|
+
const BASE_RETRY_DELAY_MS = 300;
|
|
5
|
+
const RETRYABLE_STATUS_CODES = new Set([408, 425, 429, 500, 502, 503, 504]);
|
|
6
|
+
function sleep(ms) {
|
|
7
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
8
|
+
}
|
|
9
|
+
function isAbortError(error) {
|
|
10
|
+
return (error === null || error === void 0 ? void 0 : error.name) === "AbortError";
|
|
11
|
+
}
|
|
12
|
+
function isLikelyNetworkError(error) {
|
|
13
|
+
if (!error) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
if (error instanceof TypeError) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
const message = String(error.message || "");
|
|
20
|
+
return /(ECONNRESET|ECONNREFUSED|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|NetworkError|network request failed)/i.test(message);
|
|
21
|
+
}
|
|
22
|
+
function getRetryDelayMs(attempt) {
|
|
23
|
+
const backoff = Math.min(MAX_RETRY_DELAY_MS, BASE_RETRY_DELAY_MS * (2 ** attempt));
|
|
24
|
+
const jitter = Math.floor(Math.random() * 150);
|
|
25
|
+
return backoff + jitter;
|
|
26
|
+
}
|
|
2
27
|
export class YetterImageClient {
|
|
3
28
|
constructor(options) {
|
|
4
29
|
if (!options.apiKey) {
|
|
@@ -10,67 +35,102 @@ export class YetterImageClient {
|
|
|
10
35
|
getApiEndpoint() {
|
|
11
36
|
return this.endpoint;
|
|
12
37
|
}
|
|
38
|
+
getAuthHeaders() {
|
|
39
|
+
return {
|
|
40
|
+
"Content-Type": "application/json",
|
|
41
|
+
Authorization: this.apiKey,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async readErrorBody(res) {
|
|
45
|
+
try {
|
|
46
|
+
const text = await res.text();
|
|
47
|
+
return text || res.statusText || "Unknown API error";
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return res.statusText || "Unknown API error";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async requestJson(url, init, options = {}) {
|
|
54
|
+
var _a, _b, _c;
|
|
55
|
+
const maxRetries = (_a = options.maxRetries) !== null && _a !== void 0 ? _a : 0;
|
|
56
|
+
const timeoutMs = (_b = options.timeoutMs) !== null && _b !== void 0 ? _b : DEFAULT_TIMEOUT_MS;
|
|
57
|
+
const errorPrefix = (_c = options.errorPrefix) !== null && _c !== void 0 ? _c : "API error";
|
|
58
|
+
for (let attempt = 0;; attempt += 1) {
|
|
59
|
+
const controller = new AbortController();
|
|
60
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
61
|
+
try {
|
|
62
|
+
const res = await fetch(url, {
|
|
63
|
+
...init,
|
|
64
|
+
signal: controller.signal,
|
|
65
|
+
});
|
|
66
|
+
if (!res.ok) {
|
|
67
|
+
const errorText = await this.readErrorBody(res);
|
|
68
|
+
const shouldRetry = attempt < maxRetries && RETRYABLE_STATUS_CODES.has(res.status);
|
|
69
|
+
if (shouldRetry) {
|
|
70
|
+
await sleep(getRetryDelayMs(attempt));
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
throw new Error(`${errorPrefix} (${res.status}): ${errorText}`);
|
|
74
|
+
}
|
|
75
|
+
return (await res.json());
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
const retryableNetworkError = isAbortError(error) || isLikelyNetworkError(error);
|
|
79
|
+
const shouldRetry = attempt < maxRetries && retryableNetworkError;
|
|
80
|
+
if (shouldRetry) {
|
|
81
|
+
await sleep(getRetryDelayMs(attempt));
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (isAbortError(error)) {
|
|
85
|
+
throw new Error(`${errorPrefix}: request timed out after ${timeoutMs}ms`);
|
|
86
|
+
}
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
clearTimeout(timeout);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
13
94
|
async generate(body) {
|
|
95
|
+
if (!body.model) {
|
|
96
|
+
throw new Error("`model` is required in generate request body");
|
|
97
|
+
}
|
|
14
98
|
const url = `${this.endpoint}/${body.model}`;
|
|
15
|
-
|
|
99
|
+
return this.requestJson(url, {
|
|
16
100
|
method: "POST",
|
|
17
|
-
headers:
|
|
18
|
-
"Content-Type": "application/json",
|
|
19
|
-
Authorization: this.apiKey,
|
|
20
|
-
},
|
|
101
|
+
headers: this.getAuthHeaders(),
|
|
21
102
|
body: JSON.stringify(body),
|
|
103
|
+
}, {
|
|
104
|
+
maxRetries: 0,
|
|
22
105
|
});
|
|
23
|
-
if (!res.ok) {
|
|
24
|
-
const errorText = await res.text();
|
|
25
|
-
throw new Error(`API error (${res.status}): ${errorText}`);
|
|
26
|
-
}
|
|
27
|
-
return (await res.json());
|
|
28
106
|
}
|
|
29
107
|
async getStatus(body) {
|
|
30
108
|
const url = new URL(body.url);
|
|
31
109
|
if (body.logs) {
|
|
32
110
|
url.searchParams.append('logs', '1');
|
|
33
111
|
}
|
|
34
|
-
|
|
112
|
+
return this.requestJson(url.toString(), {
|
|
35
113
|
method: "GET",
|
|
36
|
-
headers:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
},
|
|
114
|
+
headers: this.getAuthHeaders(),
|
|
115
|
+
}, {
|
|
116
|
+
maxRetries: 4,
|
|
40
117
|
});
|
|
41
|
-
if (!res.ok) {
|
|
42
|
-
const errorText = await res.text();
|
|
43
|
-
throw new Error(`API error (${res.status}): ${errorText}`);
|
|
44
|
-
}
|
|
45
|
-
return (await res.json());
|
|
46
118
|
}
|
|
47
119
|
async cancel(body) {
|
|
48
|
-
|
|
120
|
+
return this.requestJson(body.url, {
|
|
49
121
|
method: "PUT",
|
|
50
|
-
headers:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
},
|
|
122
|
+
headers: this.getAuthHeaders(),
|
|
123
|
+
}, {
|
|
124
|
+
maxRetries: 3,
|
|
54
125
|
});
|
|
55
|
-
if (!res.ok) {
|
|
56
|
-
const errorText = await res.text();
|
|
57
|
-
throw new Error(`API error (${res.status}): ${errorText}`);
|
|
58
|
-
}
|
|
59
|
-
return (await res.json());
|
|
60
126
|
}
|
|
61
127
|
async getResponse(body) {
|
|
62
|
-
|
|
128
|
+
return this.requestJson(body.url, {
|
|
63
129
|
method: "GET",
|
|
64
|
-
headers:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
},
|
|
130
|
+
headers: this.getAuthHeaders(),
|
|
131
|
+
}, {
|
|
132
|
+
maxRetries: 4,
|
|
68
133
|
});
|
|
69
|
-
if (!res.ok) {
|
|
70
|
-
const errorText = await res.text();
|
|
71
|
-
throw new Error(`API error (${res.status}): ${errorText}`);
|
|
72
|
-
}
|
|
73
|
-
return (await res.json());
|
|
74
134
|
}
|
|
75
135
|
/**
|
|
76
136
|
* Request presigned URL(s) for file upload
|
|
@@ -78,19 +138,14 @@ export class YetterImageClient {
|
|
|
78
138
|
* @returns Presigned URL response with mode (single/multipart)
|
|
79
139
|
*/
|
|
80
140
|
async getUploadUrl(body) {
|
|
81
|
-
|
|
141
|
+
return this.requestJson(`${this.endpoint}/uploads`, {
|
|
82
142
|
method: "POST",
|
|
83
|
-
headers:
|
|
84
|
-
"Content-Type": "application/json",
|
|
85
|
-
Authorization: this.apiKey,
|
|
86
|
-
},
|
|
143
|
+
headers: this.getAuthHeaders(),
|
|
87
144
|
body: JSON.stringify(body),
|
|
145
|
+
}, {
|
|
146
|
+
maxRetries: 0,
|
|
147
|
+
errorPrefix: "Upload URL request failed",
|
|
88
148
|
});
|
|
89
|
-
if (!res.ok) {
|
|
90
|
-
const errorText = await res.text();
|
|
91
|
-
throw new Error(`Upload URL request failed (${res.status}): ${errorText}`);
|
|
92
|
-
}
|
|
93
|
-
return (await res.json());
|
|
94
149
|
}
|
|
95
150
|
/**
|
|
96
151
|
* Notify server that upload is complete
|
|
@@ -98,18 +153,13 @@ export class YetterImageClient {
|
|
|
98
153
|
* @returns Uploaded file metadata with public URL
|
|
99
154
|
*/
|
|
100
155
|
async uploadComplete(body) {
|
|
101
|
-
|
|
156
|
+
return this.requestJson(`${this.endpoint}/uploads/complete`, {
|
|
102
157
|
method: "POST",
|
|
103
|
-
headers:
|
|
104
|
-
"Content-Type": "application/json",
|
|
105
|
-
Authorization: this.apiKey,
|
|
106
|
-
},
|
|
158
|
+
headers: this.getAuthHeaders(),
|
|
107
159
|
body: JSON.stringify(body),
|
|
160
|
+
}, {
|
|
161
|
+
maxRetries: 0,
|
|
162
|
+
errorPrefix: "Upload completion failed",
|
|
108
163
|
});
|
|
109
|
-
if (!res.ok) {
|
|
110
|
-
const errorText = await res.text();
|
|
111
|
-
throw new Error(`Upload completion failed (${res.status}): ${errorText}`);
|
|
112
|
-
}
|
|
113
|
-
return (await res.json());
|
|
114
164
|
}
|
|
115
165
|
}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,7 +1,32 @@
|
|
|
1
1
|
import { ClientOptions, GetResponseResponse, SubscribeOptions, GenerateResponse, SubmitQueueOptions, GetResultOptions, GetResultResponse, StatusOptions, StatusResponse, StreamOptions, YetterStream, UploadOptions, UploadCompleteResponse } from "./types.js";
|
|
2
|
+
export declare class YetterClient {
|
|
3
|
+
private apiKey;
|
|
4
|
+
private endpoint;
|
|
5
|
+
constructor(options?: Partial<ClientOptions>);
|
|
6
|
+
private setApiKey;
|
|
7
|
+
private assertApiKeyConfigured;
|
|
8
|
+
private createApiClient;
|
|
9
|
+
private getUploadTimeoutMs;
|
|
10
|
+
private putWithTimeout;
|
|
11
|
+
configure(options: ClientOptions): void;
|
|
12
|
+
subscribe(model: string, options: SubscribeOptions): Promise<GetResponseResponse>;
|
|
13
|
+
readonly queue: {
|
|
14
|
+
submit: (model: string, options: SubmitQueueOptions) => Promise<GenerateResponse>;
|
|
15
|
+
status: (model: string, options: StatusOptions) => Promise<StatusResponse>;
|
|
16
|
+
result: (model: string, options: GetResultOptions) => Promise<GetResultResponse>;
|
|
17
|
+
};
|
|
18
|
+
stream(model: string, options: StreamOptions): Promise<YetterStream>;
|
|
19
|
+
uploadFile(fileOrPath: string | File | Blob, options?: UploadOptions): Promise<UploadCompleteResponse>;
|
|
20
|
+
uploadBlob(file: File | Blob, options?: UploadOptions): Promise<UploadCompleteResponse>;
|
|
21
|
+
private _uploadFromPath;
|
|
22
|
+
private _uploadFromBlob;
|
|
23
|
+
private _uploadFileSingle;
|
|
24
|
+
private _uploadFileMultipart;
|
|
25
|
+
private _uploadBlobSingle;
|
|
26
|
+
private _uploadBlobMultipart;
|
|
27
|
+
}
|
|
2
28
|
export declare class yetter {
|
|
3
|
-
private static
|
|
4
|
-
private static endpoint;
|
|
29
|
+
private static client;
|
|
5
30
|
static configure(options: ClientOptions): void;
|
|
6
31
|
static subscribe(model: string, options: SubscribeOptions): Promise<GetResponseResponse>;
|
|
7
32
|
static queue: {
|
|
@@ -10,56 +35,6 @@ export declare class yetter {
|
|
|
10
35
|
result: (model: string, options: GetResultOptions) => Promise<GetResultResponse>;
|
|
11
36
|
};
|
|
12
37
|
static stream(model: string, options: StreamOptions): Promise<YetterStream>;
|
|
13
|
-
/**
|
|
14
|
-
* Upload a file from the filesystem (Node.js) or File/Blob object (browser)
|
|
15
|
-
*
|
|
16
|
-
* @param fileOrPath File path (Node.js) or File/Blob object (browser)
|
|
17
|
-
* @param options Upload configuration options
|
|
18
|
-
* @returns Promise resolving to upload result with public URL
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* ```typescript
|
|
22
|
-
* // Node.js
|
|
23
|
-
* const result = await yetter.uploadFile("/path/to/image.jpg", {
|
|
24
|
-
* onProgress: (pct) => console.log(`Upload: ${pct}%`)
|
|
25
|
-
* });
|
|
26
|
-
*
|
|
27
|
-
* // Browser
|
|
28
|
-
* const fileInput = document.querySelector('input[type="file"]');
|
|
29
|
-
* const file = fileInput.files[0];
|
|
30
|
-
* const result = await yetter.uploadFile(file, {
|
|
31
|
-
* onProgress: (pct) => updateProgressBar(pct)
|
|
32
|
-
* });
|
|
33
|
-
* ```
|
|
34
|
-
*/
|
|
35
38
|
static uploadFile(fileOrPath: string | File | Blob, options?: UploadOptions): Promise<UploadCompleteResponse>;
|
|
36
|
-
/**
|
|
37
|
-
* Upload a file from browser (File or Blob object)
|
|
38
|
-
* This is an alias for uploadFile for better clarity in browser contexts
|
|
39
|
-
*/
|
|
40
39
|
static uploadBlob(file: File | Blob, options?: UploadOptions): Promise<UploadCompleteResponse>;
|
|
41
|
-
/**
|
|
42
|
-
* Upload file from filesystem path (Node.js only)
|
|
43
|
-
*/
|
|
44
|
-
private static _uploadFromPath;
|
|
45
|
-
/**
|
|
46
|
-
* Upload file from Blob/File object (browser)
|
|
47
|
-
*/
|
|
48
|
-
private static _uploadFromBlob;
|
|
49
|
-
/**
|
|
50
|
-
* Upload file using single PUT request (Node.js, private helper)
|
|
51
|
-
*/
|
|
52
|
-
private static _uploadFileSingle;
|
|
53
|
-
/**
|
|
54
|
-
* Upload file using multipart upload (Node.js, private helper)
|
|
55
|
-
*/
|
|
56
|
-
private static _uploadFileMultipart;
|
|
57
|
-
/**
|
|
58
|
-
* Upload blob using single PUT request (browser, private helper)
|
|
59
|
-
*/
|
|
60
|
-
private static _uploadBlobSingle;
|
|
61
|
-
/**
|
|
62
|
-
* Upload blob using multipart upload (browser, private helper)
|
|
63
|
-
*/
|
|
64
|
-
private static _uploadBlobMultipart;
|
|
65
40
|
}
|