@convert-buddy/cloud-client 0.1.0-beta.1 → 0.1.0-beta.3
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/LICENSE +7 -7
- package/NOTICE +3 -3
- package/README.md +5 -9
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +23 -31
- package/server.d.ts +1 -0
- package/server.js +1 -0
- package/dist/chunk-DIBGYTC7.js +0 -372
- package/dist/index.d.ts +0 -1471
- package/dist/index.js +0 -158
- package/dist/processing-DFE8ktWi.d.ts +0 -3204
- package/dist/server.d.ts +0 -10
- package/dist/server.js +0 -21
package/LICENSE
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
Diakrio Free Distribution License 1.0
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) Diakrio copyright holders. All rights reserved, except as
|
|
4
4
|
expressly granted below or by an applicable separate license.
|
|
5
5
|
|
|
6
6
|
1. Scope
|
|
7
|
-
This license applies to first-party material in official
|
|
7
|
+
This license applies to first-party material in official Diakrio package
|
|
8
8
|
distributions carrying this license (the Software), to the extent its copyright
|
|
9
9
|
holders have authority to license that material on these terms. It does not grant
|
|
10
10
|
access to the private development repository or license undistributed source code.
|
|
@@ -26,12 +26,12 @@ archiving, including private registries and public package caches.
|
|
|
26
26
|
Keep this license and all applicable copyright and third-party notices with
|
|
27
27
|
redistributed copies, or in an accompanying, accessible legal-notices file or page
|
|
28
28
|
when bundling prevents retaining individual files. Identify your modifications
|
|
29
|
-
without implying that the modified distribution is an official
|
|
29
|
+
without implying that the modified distribution is an official Diakrio release.
|
|
30
30
|
|
|
31
31
|
3. Reserved rights
|
|
32
32
|
This is a proprietary distribution license, not an open-source license. Except
|
|
33
33
|
as allowed above, you may not distribute or sublicense the Software as a standalone
|
|
34
|
-
library, engine, or replacement
|
|
34
|
+
library, engine, or replacement Diakrio package, or rebrand or sell it as
|
|
35
35
|
your own standalone software product. This does not restrict selling your own
|
|
36
36
|
applications or services that integrate the Software as permitted in section 2.
|
|
37
37
|
No rights to unpublished source code, trademarks, or an endorsement are granted.
|
|
@@ -39,14 +39,14 @@ Rights not expressly granted are reserved. Statutory rights that cannot lawfully
|
|
|
39
39
|
be excluded or limited by agreement remain unaffected.
|
|
40
40
|
|
|
41
41
|
4. Hosted services
|
|
42
|
-
This license does not include
|
|
42
|
+
This license does not include Diakrio Cloud subscriptions, hosted quotas,
|
|
43
43
|
support commitments, or service-level guarantees. Those services have separate
|
|
44
44
|
terms. Their availability or pricing does not change the free-use grant for a
|
|
45
45
|
copy of the Software obtained under this license.
|
|
46
46
|
|
|
47
47
|
5. Earlier releases and third-party material
|
|
48
48
|
This license does not revoke, replace, or restrict rights already granted under
|
|
49
|
-
MIT or any other separate license. Earlier Convert Buddy releases and portions
|
|
49
|
+
MIT or any other separate license. Earlier Diakrio or Convert Buddy releases and portions
|
|
50
50
|
made available under MIT retain those permissions; see NOTICE and accompanying
|
|
51
51
|
license files. Where that material is included in this distribution, its separate
|
|
52
52
|
license continues to apply to that material. Third-party libraries, models,
|
package/NOTICE
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
Diakrio distribution notices
|
|
2
2
|
|
|
3
|
-
Earlier Convert Buddy releases and portions licensed under MIT retain their
|
|
3
|
+
Earlier Diakrio and Convert Buddy releases and portions licensed under MIT retain their
|
|
4
4
|
original permissions. The free distribution license does not revoke those rights.
|
|
5
5
|
The following is the original repository license text, preserved for that material.
|
|
6
6
|
It is not a grant of MIT rights for new proprietary material.
|
|
7
7
|
|
|
8
8
|
MIT License
|
|
9
9
|
|
|
10
|
-
Copyright (c)
|
|
10
|
+
Copyright (c) Diakrio contributors
|
|
11
11
|
|
|
12
12
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
13
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @convert-buddy/cloud-client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **Convert Buddy is now Diakrio.** This package remains available as a compatibility alias. New projects should install [@diakrio/cloud-client](https://www.npmjs.com/package/@diakrio%2Fcloud-client).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Migration
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Replace `@convert-buddy/cloud-client` with `@diakrio/cloud-client` in your dependencies and imports. The compatibility package forwards the existing public entry points, but new features and documentation use the Diakrio name.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
V1 exports, key-only calls, and AI-mapping endpoints have been removed. `TargetSchemaV1` and the signed delivery protocol retain their own independent version numbers.
|
|
12
|
-
|
|
13
|
-
See [the public guide](https://convert-buddy.app/docs/cloud/v2). Package publication and hosted production activation remain separate release gates.
|
|
9
|
+
Documentation: https://diakrio.com/docs
|
package/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@diakrio/cloud-client';
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@diakrio/cloud-client';
|
package/package.json
CHANGED
|
@@ -1,53 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@convert-buddy/cloud-client",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.0-beta.3",
|
|
4
|
+
"description": "Compatibility package: @convert-buddy/cloud-client has moved to @diakrio/cloud-client.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"
|
|
8
|
-
"types": "./dist/index.d.ts",
|
|
7
|
+
"sideEffects": false,
|
|
9
8
|
"exports": {
|
|
10
|
-
"./server": { "types": "./dist/server.d.ts", "default": "./dist/server.js" },
|
|
11
9
|
".": {
|
|
12
|
-
"types": "./
|
|
13
|
-
"default": "./
|
|
10
|
+
"types": "./index.d.ts",
|
|
11
|
+
"default": "./index.js"
|
|
12
|
+
},
|
|
13
|
+
"./server": {
|
|
14
|
+
"types": "./server.d.ts",
|
|
15
|
+
"default": "./server.js"
|
|
14
16
|
}
|
|
15
17
|
},
|
|
16
18
|
"files": [
|
|
17
|
-
"
|
|
19
|
+
"index.js",
|
|
20
|
+
"index.d.ts",
|
|
21
|
+
"server.js",
|
|
22
|
+
"server.d.ts",
|
|
18
23
|
"README.md",
|
|
19
24
|
"LICENSE",
|
|
20
|
-
"NOTICE"
|
|
21
|
-
"!**/*.map"
|
|
25
|
+
"NOTICE"
|
|
22
26
|
],
|
|
23
|
-
"sideEffects": false,
|
|
24
|
-
"scripts": {
|
|
25
|
-
"build": "tsup",
|
|
26
|
-
"check": "tsc --noEmit",
|
|
27
|
-
"test": "npm run build && tsc -p tsconfig.test.json && node --test .test-dist/tests/widget.test.js .test-dist/tests/processing.test.js",
|
|
28
|
-
"prepack": "npm run check && npm test && node ../../scripts/distribution-policy.mjs package",
|
|
29
|
-
"prepublishOnly": "node ../../scripts/distribution-policy.mjs package"
|
|
30
|
-
},
|
|
31
27
|
"dependencies": {
|
|
32
|
-
"@
|
|
33
|
-
},
|
|
34
|
-
"devDependencies": {
|
|
35
|
-
"@types/node": "^25.0.3",
|
|
36
|
-
"tsup": "^8.5.1",
|
|
37
|
-
"typescript": "^5.5.0"
|
|
28
|
+
"@diakrio/cloud-client": "0.1.0-beta.3"
|
|
38
29
|
},
|
|
39
30
|
"engines": {
|
|
40
31
|
"node": ">=20.17"
|
|
41
32
|
},
|
|
42
|
-
"repository": {
|
|
43
|
-
"type": "git",
|
|
44
|
-
"url": "git+https://github.com/brunohanss/convert-buddy.git",
|
|
45
|
-
"directory": "packages/cloud-client"
|
|
46
|
-
},
|
|
47
33
|
"publishConfig": {
|
|
48
34
|
"access": "public",
|
|
49
35
|
"registry": "https://registry.npmjs.org/",
|
|
50
|
-
"provenance": false
|
|
36
|
+
"provenance": false,
|
|
37
|
+
"tag": "beta"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/brunohanss/diakrio.git",
|
|
42
|
+
"directory": "packages/legacy/cloud-client"
|
|
51
43
|
},
|
|
52
|
-
"homepage": "https://convert-buddy
|
|
44
|
+
"homepage": "https://diakrio.com/docs/migrating-from-convert-buddy"
|
|
53
45
|
}
|
package/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@diakrio/cloud-client/server';
|
package/server.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@diakrio/cloud-client/server';
|
package/dist/chunk-DIBGYTC7.js
DELETED
|
@@ -1,372 +0,0 @@
|
|
|
1
|
-
// src/processing.ts
|
|
2
|
-
function blobFile(blob) {
|
|
3
|
-
return {
|
|
4
|
-
size: blob.size,
|
|
5
|
-
async read(offset, length, signal) {
|
|
6
|
-
signal?.throwIfAborted();
|
|
7
|
-
const bytes = new Uint8Array(
|
|
8
|
-
await blob.slice(offset, offset + length).arrayBuffer()
|
|
9
|
-
);
|
|
10
|
-
signal?.throwIfAborted();
|
|
11
|
-
return bytes;
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
var ProcessingCloudError = class extends Error {
|
|
16
|
-
constructor(code, status, requestId) {
|
|
17
|
-
super(code);
|
|
18
|
-
this.code = code;
|
|
19
|
-
this.status = status;
|
|
20
|
-
this.requestId = requestId;
|
|
21
|
-
this.name = "ProcessingCloudError";
|
|
22
|
-
}
|
|
23
|
-
code;
|
|
24
|
-
status;
|
|
25
|
-
requestId;
|
|
26
|
-
};
|
|
27
|
-
var uuid = /^[a-f0-9]{8}-(?:[a-f0-9]{4}-){3}[a-f0-9]{12}$/i;
|
|
28
|
-
function identifier(value) {
|
|
29
|
-
if (!uuid.test(value)) throw new TypeError("Invalid resource ID");
|
|
30
|
-
return value;
|
|
31
|
-
}
|
|
32
|
-
function sleep(ms, signal) {
|
|
33
|
-
return new Promise((resolve, reject) => {
|
|
34
|
-
const abort = () => {
|
|
35
|
-
clearTimeout(timer);
|
|
36
|
-
reject(signal?.reason);
|
|
37
|
-
};
|
|
38
|
-
const timer = setTimeout(() => {
|
|
39
|
-
signal?.removeEventListener("abort", abort);
|
|
40
|
-
resolve();
|
|
41
|
-
}, ms);
|
|
42
|
-
if (signal?.aborted) abort();
|
|
43
|
-
else signal?.addEventListener("abort", abort, { once: true });
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
async function json(response) {
|
|
47
|
-
const reader = response.body?.getReader();
|
|
48
|
-
const chunks = [];
|
|
49
|
-
let length = 0;
|
|
50
|
-
try {
|
|
51
|
-
if (!reader) throw new ProcessingCloudError("INVALID_RESPONSE");
|
|
52
|
-
for (; ; ) {
|
|
53
|
-
const next = await reader.read();
|
|
54
|
-
if (next.done) break;
|
|
55
|
-
length += next.value.length;
|
|
56
|
-
if (length > 1024 * 1024)
|
|
57
|
-
throw new ProcessingCloudError("RESPONSE_LIMIT");
|
|
58
|
-
chunks.push(next.value);
|
|
59
|
-
}
|
|
60
|
-
const bytes = new Uint8Array(length);
|
|
61
|
-
let offset = 0;
|
|
62
|
-
for (const chunk of chunks) {
|
|
63
|
-
bytes.set(chunk, offset);
|
|
64
|
-
offset += chunk.length;
|
|
65
|
-
}
|
|
66
|
-
try {
|
|
67
|
-
return JSON.parse(new TextDecoder().decode(bytes));
|
|
68
|
-
} catch {
|
|
69
|
-
throw new ProcessingCloudError("INVALID_RESPONSE");
|
|
70
|
-
}
|
|
71
|
-
} finally {
|
|
72
|
-
await reader?.cancel().catch(() => void 0);
|
|
73
|
-
reader?.releaseLock();
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
function validJob(value) {
|
|
77
|
-
if (!value || !uuid.test(value.jobId) || ![
|
|
78
|
-
"queued",
|
|
79
|
-
"running",
|
|
80
|
-
"succeeded",
|
|
81
|
-
"failed",
|
|
82
|
-
"cancelling",
|
|
83
|
-
"cancelled"
|
|
84
|
-
].includes(value.status) || !Array.isArray(value.artifacts) || value.artifacts.some((a) => !uuid.test(a.artifactId)))
|
|
85
|
-
throw new ProcessingCloudError("INVALID_RESPONSE");
|
|
86
|
-
return value;
|
|
87
|
-
}
|
|
88
|
-
var ProcessingClient = class {
|
|
89
|
-
constructor(options) {
|
|
90
|
-
this.options = options;
|
|
91
|
-
this.#origin = new URL(options.apiUrl);
|
|
92
|
-
const u = this.#origin;
|
|
93
|
-
if (u.username || u.password || u.search || u.hash || u.pathname !== "/" || u.protocol !== "https:" && !(u.protocol === "http:" && ["localhost", "127.0.0.1"].includes(u.hostname)))
|
|
94
|
-
throw new TypeError(
|
|
95
|
-
"API URL must be an HTTPS origin or local HTTP origin"
|
|
96
|
-
);
|
|
97
|
-
this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
98
|
-
}
|
|
99
|
-
options;
|
|
100
|
-
#origin;
|
|
101
|
-
#fetch;
|
|
102
|
-
async #response(path, init) {
|
|
103
|
-
const headers = new Headers(await this.options.authorization());
|
|
104
|
-
new Headers(init.headers).forEach((value, key) => headers.set(key, value));
|
|
105
|
-
const response = await this.#fetch(
|
|
106
|
-
new URL(`/v2/processing${path}`, this.#origin),
|
|
107
|
-
{
|
|
108
|
-
...init,
|
|
109
|
-
headers,
|
|
110
|
-
credentials: "omit",
|
|
111
|
-
redirect: "manual",
|
|
112
|
-
cache: "no-store"
|
|
113
|
-
}
|
|
114
|
-
);
|
|
115
|
-
if (response.type === "opaqueredirect" || response.status >= 300 && response.status < 400 && response.status !== 304) {
|
|
116
|
-
await response.body?.cancel();
|
|
117
|
-
throw new ProcessingCloudError("REDIRECT_DENIED", response.status);
|
|
118
|
-
}
|
|
119
|
-
return response;
|
|
120
|
-
}
|
|
121
|
-
async #metadata(path, method, body, signal, headers) {
|
|
122
|
-
const requestHeaders = new Headers(headers);
|
|
123
|
-
requestHeaders.set("content-type", "application/json");
|
|
124
|
-
const response = await this.#response(path, {
|
|
125
|
-
method,
|
|
126
|
-
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
127
|
-
signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(3e4)]) : AbortSignal.timeout(3e4),
|
|
128
|
-
headers: requestHeaders
|
|
129
|
-
});
|
|
130
|
-
const value = await json(response);
|
|
131
|
-
if (!response.ok) {
|
|
132
|
-
const code = value && typeof value === "object" && "error" in value && value.error && typeof value.error === "object" && "code" in value.error && typeof value.error.code === "string" ? value.error.code : "HTTP_ERROR";
|
|
133
|
-
throw new ProcessingCloudError(
|
|
134
|
-
code,
|
|
135
|
-
response.status,
|
|
136
|
-
response.headers.get("x-request-id") ?? void 0
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
return value;
|
|
140
|
-
}
|
|
141
|
-
getUpload(uploadId, after = 0, signal) {
|
|
142
|
-
return this.#metadata(
|
|
143
|
-
`/uploads/${identifier(uploadId)}?after=${after}`,
|
|
144
|
-
"GET",
|
|
145
|
-
void 0,
|
|
146
|
-
signal
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
createSession(grant, signal) {
|
|
150
|
-
return this.#metadata("/sessions", "POST", grant, signal);
|
|
151
|
-
}
|
|
152
|
-
renewSession(id, grant, signal) {
|
|
153
|
-
return this.#metadata(
|
|
154
|
-
`/sessions/${identifier(id)}/renew`,
|
|
155
|
-
"POST",
|
|
156
|
-
grant,
|
|
157
|
-
signal
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
revokeSession(id, signal) {
|
|
161
|
-
return this.#metadata(
|
|
162
|
-
`/sessions/${identifier(id)}/revoke`,
|
|
163
|
-
"POST",
|
|
164
|
-
{},
|
|
165
|
-
signal
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
abortUpload(uploadId, signal) {
|
|
169
|
-
return this.#metadata(
|
|
170
|
-
`/uploads/${identifier(uploadId)}`,
|
|
171
|
-
"DELETE",
|
|
172
|
-
{},
|
|
173
|
-
signal
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
async upload(source, options) {
|
|
177
|
-
const file = source instanceof Blob ? blobFile(source) : source;
|
|
178
|
-
if (!Number.isSafeInteger(file.size) || file.size <= 0)
|
|
179
|
-
throw new TypeError(
|
|
180
|
-
"A nonempty replayable file with a known size is required"
|
|
181
|
-
);
|
|
182
|
-
const upload = await this.#metadata(
|
|
183
|
-
"/uploads",
|
|
184
|
-
"POST",
|
|
185
|
-
{
|
|
186
|
-
clientKey: identifier(options.clientKey),
|
|
187
|
-
operation: options.operation,
|
|
188
|
-
bytes: file.size,
|
|
189
|
-
contentType: options.contentType ?? "application/octet-stream"
|
|
190
|
-
},
|
|
191
|
-
options.signal
|
|
192
|
-
);
|
|
193
|
-
if (!upload || !uuid.test(upload.uploadId) || upload.bytes !== file.size || !Number.isSafeInteger(upload.partBytes) || upload.partBytes < 1 || upload.partBytes > 32 * 1024 ** 2 || upload.partCount !== Math.ceil(file.size / upload.partBytes))
|
|
194
|
-
throw new ProcessingCloudError("INVALID_RESPONSE");
|
|
195
|
-
if (upload.jobId && uuid.test(upload.jobId)) return upload;
|
|
196
|
-
if (upload.status === "deleted" || upload.status === "deleting")
|
|
197
|
-
throw new ProcessingCloudError("UPLOAD_EXPIRED", 410);
|
|
198
|
-
let page;
|
|
199
|
-
for (let part = 1; part <= upload.partCount; part++) {
|
|
200
|
-
options.signal?.throwIfAborted();
|
|
201
|
-
if (!page || page.nextPart !== null && part > page.nextPart)
|
|
202
|
-
page = await this.getUpload(upload.uploadId, part - 1, options.signal);
|
|
203
|
-
const offset = (part - 1) * upload.partBytes, expected = Math.min(upload.partBytes, file.size - offset);
|
|
204
|
-
const bytes = new Uint8Array(
|
|
205
|
-
await file.read(offset, expected, options.signal)
|
|
206
|
-
);
|
|
207
|
-
if (bytes.length !== expected)
|
|
208
|
-
throw new TypeError("File reader returned an incorrect range");
|
|
209
|
-
const hash = Array.from(
|
|
210
|
-
new Uint8Array(await crypto.subtle.digest("SHA-256", bytes)),
|
|
211
|
-
(value) => value.toString(16).padStart(2, "0")
|
|
212
|
-
).join("");
|
|
213
|
-
const existing = page.parts.find((p) => p.partNumber === part);
|
|
214
|
-
if (existing && (existing.sha256 !== hash || existing.bytes !== bytes.length))
|
|
215
|
-
throw new ProcessingCloudError("LOCAL_FILE_CHANGED", 409);
|
|
216
|
-
if (!existing) {
|
|
217
|
-
if (upload.status !== "uploading")
|
|
218
|
-
throw new ProcessingCloudError("UPLOAD_CLOSED", 409);
|
|
219
|
-
for (let attempt = 0; ; attempt++) {
|
|
220
|
-
try {
|
|
221
|
-
const response = await this.#response(
|
|
222
|
-
`/uploads/${upload.uploadId}/parts/${part}`,
|
|
223
|
-
{
|
|
224
|
-
method: "PUT",
|
|
225
|
-
body: bytes,
|
|
226
|
-
signal: options.signal,
|
|
227
|
-
headers: {
|
|
228
|
-
"content-type": "application/octet-stream",
|
|
229
|
-
"X-Part-Sha256": hash
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
);
|
|
233
|
-
const receipt = await json(response);
|
|
234
|
-
if (!response.ok)
|
|
235
|
-
throw new ProcessingCloudError(
|
|
236
|
-
receipt.error?.code ?? "UPLOAD_FAILED",
|
|
237
|
-
response.status
|
|
238
|
-
);
|
|
239
|
-
if (receipt.bytes !== bytes.length || receipt.partNumber !== part)
|
|
240
|
-
throw new ProcessingCloudError("INVALID_RESPONSE");
|
|
241
|
-
break;
|
|
242
|
-
} catch (error) {
|
|
243
|
-
if (attempt >= 4 || options.signal?.aborted || error instanceof ProcessingCloudError && ![429, 500, 502, 503, 504].includes(error.status ?? 0) && !["PART_IN_FLIGHT", "PART_NOT_ACKNOWLEDGED"].includes(
|
|
244
|
-
error.code
|
|
245
|
-
))
|
|
246
|
-
throw error;
|
|
247
|
-
await sleep(Math.min(3e4, 1e3 * 2 ** attempt), options.signal);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
options.onProgress?.(offset + bytes.length, file.size);
|
|
252
|
-
}
|
|
253
|
-
return upload;
|
|
254
|
-
}
|
|
255
|
-
async submit(request, idempotencyKey, signal) {
|
|
256
|
-
if (!/^[A-Za-z0-9._:-]{1,128}$/.test(idempotencyKey))
|
|
257
|
-
throw new TypeError("Invalid idempotency key");
|
|
258
|
-
return validJob(
|
|
259
|
-
await this.#metadata("/jobs", "POST", request, signal, {
|
|
260
|
-
"Idempotency-Key": idempotencyKey
|
|
261
|
-
})
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
async getJob(id, signal) {
|
|
265
|
-
return validJob(
|
|
266
|
-
await this.#metadata(`/jobs/${identifier(id)}`, "GET", void 0, signal)
|
|
267
|
-
);
|
|
268
|
-
}
|
|
269
|
-
async cancel(id, signal) {
|
|
270
|
-
return validJob(
|
|
271
|
-
await this.#metadata(
|
|
272
|
-
`/jobs/${identifier(id)}/cancel`,
|
|
273
|
-
"POST",
|
|
274
|
-
{},
|
|
275
|
-
signal
|
|
276
|
-
)
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
async retry(id, expectedGeneration, signal) {
|
|
280
|
-
return validJob(
|
|
281
|
-
await this.#metadata(
|
|
282
|
-
`/jobs/${identifier(id)}/retry`,
|
|
283
|
-
"POST",
|
|
284
|
-
{ expectedGeneration },
|
|
285
|
-
signal
|
|
286
|
-
)
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
async wait(id, options = {}) {
|
|
290
|
-
let delay = 1e3, etag = null;
|
|
291
|
-
for (; ; ) {
|
|
292
|
-
const response = await this.#response(`/jobs/${identifier(id)}`, {
|
|
293
|
-
method: "GET",
|
|
294
|
-
signal: options.signal,
|
|
295
|
-
headers: etag ? { "If-None-Match": etag } : {}
|
|
296
|
-
});
|
|
297
|
-
if (response.status === 304) await response.body?.cancel();
|
|
298
|
-
else {
|
|
299
|
-
if (!response.ok) {
|
|
300
|
-
await response.body?.cancel();
|
|
301
|
-
throw new ProcessingCloudError("STATUS_UNAVAILABLE", response.status);
|
|
302
|
-
}
|
|
303
|
-
const job = validJob(await json(response));
|
|
304
|
-
etag = response.headers.get("etag");
|
|
305
|
-
options.onProgress?.(job);
|
|
306
|
-
if (["succeeded", "failed", "cancelled"].includes(job.status))
|
|
307
|
-
return job;
|
|
308
|
-
}
|
|
309
|
-
await sleep(delay + Math.random() * 250, options.signal);
|
|
310
|
-
delay = Math.min(1e4, delay * 1.5);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
async download(jobId, artifactId, options = {}) {
|
|
314
|
-
if (options.offset !== void 0 && (!Number.isSafeInteger(options.offset) || options.offset < 0 || !options.etag))
|
|
315
|
-
throw new TypeError(
|
|
316
|
-
"Resuming requires a nonnegative offset and the original ETag"
|
|
317
|
-
);
|
|
318
|
-
const headers = options.offset !== void 0 ? { Range: `bytes=${options.offset}-`, "If-Range": options.etag } : void 0;
|
|
319
|
-
const response = await this.#response(
|
|
320
|
-
`/jobs/${identifier(jobId)}/artifacts/${identifier(artifactId)}`,
|
|
321
|
-
{ method: "GET", headers, signal: options.signal }
|
|
322
|
-
);
|
|
323
|
-
if (!response.ok || !response.body) {
|
|
324
|
-
await response.body?.cancel();
|
|
325
|
-
throw new ProcessingCloudError("DOWNLOAD_UNAVAILABLE", response.status);
|
|
326
|
-
}
|
|
327
|
-
if (options.offset !== void 0 && (response.status !== 206 || response.headers.get("etag") !== options.etag || !response.headers.get("content-range")?.startsWith(`bytes ${options.offset}-`))) {
|
|
328
|
-
await response.body.cancel();
|
|
329
|
-
throw new ProcessingCloudError("DOWNLOAD_CHANGED", 409);
|
|
330
|
-
}
|
|
331
|
-
return response;
|
|
332
|
-
}
|
|
333
|
-
};
|
|
334
|
-
function createBrowserProcessingClient(options) {
|
|
335
|
-
return new ProcessingClient({
|
|
336
|
-
...options,
|
|
337
|
-
async authorization() {
|
|
338
|
-
const token = await options.sessionToken();
|
|
339
|
-
if (!/^cb_ps_[a-f0-9]{64}\.[A-Za-z0-9_-]{43}$/.test(token))
|
|
340
|
-
throw new TypeError("A short-lived processing session is required");
|
|
341
|
-
return {
|
|
342
|
-
authorization: `Bearer ${token}`,
|
|
343
|
-
"X-Processing-Identity": "session"
|
|
344
|
-
};
|
|
345
|
-
}
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
function createWidgetProcessingClient(options) {
|
|
349
|
-
return new ProcessingClient({
|
|
350
|
-
...options,
|
|
351
|
-
async authorization() {
|
|
352
|
-
const token = await options.sessionToken(), route = /^cb_ws_([a-f0-9]{64})\.[A-Za-z0-9_-]{43}$/.exec(token), key = /^cb_pk_([a-f0-9]{64})\.[A-Za-z0-9_-]{43}$/.exec(
|
|
353
|
-
options.publishableKey
|
|
354
|
-
);
|
|
355
|
-
if (!route || !key || route[1] !== key[1])
|
|
356
|
-
throw new TypeError("Matching widget credentials are required");
|
|
357
|
-
return {
|
|
358
|
-
authorization: `Bearer ${token}`,
|
|
359
|
-
"X-Processing-Identity": "widget",
|
|
360
|
-
"X-Convert-Buddy-Key": options.publishableKey
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
export {
|
|
367
|
-
blobFile,
|
|
368
|
-
ProcessingCloudError,
|
|
369
|
-
ProcessingClient,
|
|
370
|
-
createBrowserProcessingClient,
|
|
371
|
-
createWidgetProcessingClient
|
|
372
|
-
};
|