@sylphx/sdk 0.8.0 → 0.10.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.
- package/dist/index.d.ts +118 -291
- package/dist/index.mjs +470 -194
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.ts +91 -310
- package/dist/react/index.mjs +1284 -1038
- package/dist/react/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -5921,6 +5921,14 @@ async function forgotPassword(config, email) {
|
|
|
5921
5921
|
body: { email }
|
|
5922
5922
|
});
|
|
5923
5923
|
}
|
|
5924
|
+
async function resendVerificationEmail(config, email) {
|
|
5925
|
+
const endpoint = authEndpoints.resendEmailVerification;
|
|
5926
|
+
const body = { email };
|
|
5927
|
+
await callApi(config, endpoint.path, {
|
|
5928
|
+
method: endpoint.method,
|
|
5929
|
+
body
|
|
5930
|
+
});
|
|
5931
|
+
}
|
|
5924
5932
|
async function resetPassword(config, input) {
|
|
5925
5933
|
await callApi(config, "/auth/reset-password", {
|
|
5926
5934
|
method: "POST",
|
|
@@ -7994,169 +8002,248 @@ async function getBillingUsage(config, options) {
|
|
|
7994
8002
|
});
|
|
7995
8003
|
}
|
|
7996
8004
|
|
|
7997
|
-
// src/
|
|
7998
|
-
import { storageEndpoints } from "@sylphx/contract";
|
|
8005
|
+
// src/lib/retry.ts
|
|
7999
8006
|
init_constants();
|
|
8000
|
-
|
|
8001
|
-
var UPLOAD_RETRY_CONFIG = {
|
|
8002
|
-
/** Maximum number of retry attempts (AWS S3 pattern) */
|
|
8007
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
8003
8008
|
maxRetries: 5,
|
|
8004
|
-
/** Base delay in milliseconds */
|
|
8005
8009
|
baseDelayMs: BASE_RETRY_DELAY_MS,
|
|
8006
|
-
|
|
8007
|
-
maxDelayMs: MAX_RETRY_DELAY_MS,
|
|
8008
|
-
/** Jitter type: 'full' for full jitter (AWS recommended) */
|
|
8009
|
-
jitter: "full"
|
|
8010
|
+
maxDelayMs: MAX_RETRY_DELAY_MS
|
|
8010
8011
|
};
|
|
8011
|
-
function calculateBackoffDelay(attempt) {
|
|
8012
|
-
const
|
|
8013
|
-
const
|
|
8014
|
-
|
|
8015
|
-
return Math.random() * cappedDelay;
|
|
8012
|
+
function calculateBackoffDelay(attempt, config = DEFAULT_RETRY_CONFIG) {
|
|
8013
|
+
const exp = config.baseDelayMs * 2 ** attempt;
|
|
8014
|
+
const capped = Math.min(exp, config.maxDelayMs);
|
|
8015
|
+
return Math.random() * capped;
|
|
8016
8016
|
}
|
|
8017
|
-
|
|
8017
|
+
function sleep(ms, signal) {
|
|
8018
8018
|
return new Promise((resolve, reject) => {
|
|
8019
8019
|
if (signal?.aborted) {
|
|
8020
|
-
reject(
|
|
8020
|
+
reject(toAbortError());
|
|
8021
8021
|
return;
|
|
8022
8022
|
}
|
|
8023
|
-
const
|
|
8023
|
+
const timer = setTimeout(resolve, ms);
|
|
8024
8024
|
signal?.addEventListener(
|
|
8025
8025
|
"abort",
|
|
8026
8026
|
() => {
|
|
8027
|
-
clearTimeout(
|
|
8028
|
-
reject(
|
|
8027
|
+
clearTimeout(timer);
|
|
8028
|
+
reject(toAbortError());
|
|
8029
8029
|
},
|
|
8030
8030
|
{ once: true }
|
|
8031
8031
|
);
|
|
8032
8032
|
});
|
|
8033
8033
|
}
|
|
8034
|
+
var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([408, 425, 429]);
|
|
8034
8035
|
function isRetryableError2(error) {
|
|
8035
|
-
if (error
|
|
8036
|
-
|
|
8037
|
-
}
|
|
8038
|
-
if (error instanceof TypeError) {
|
|
8039
|
-
return true;
|
|
8040
|
-
}
|
|
8036
|
+
if (isAbortError(error)) return false;
|
|
8037
|
+
if (error instanceof TypeError) return true;
|
|
8041
8038
|
if (error instanceof Error && "status" in error) {
|
|
8042
8039
|
const status = error.status;
|
|
8043
|
-
return status >= 500 || status
|
|
8040
|
+
return status >= 500 || RETRYABLE_STATUSES.has(status);
|
|
8044
8041
|
}
|
|
8045
8042
|
return false;
|
|
8046
8043
|
}
|
|
8047
|
-
|
|
8048
|
-
|
|
8049
|
-
if (
|
|
8050
|
-
|
|
8044
|
+
function isAbortError(error) {
|
|
8045
|
+
if (error instanceof DOMException && error.name === "AbortError") return true;
|
|
8046
|
+
if (error instanceof Error && error.name === "AbortError") return true;
|
|
8047
|
+
return false;
|
|
8048
|
+
}
|
|
8049
|
+
function toAbortError(message2 = "Aborted") {
|
|
8050
|
+
if (typeof DOMException !== "undefined") {
|
|
8051
|
+
return new DOMException(message2, "AbortError");
|
|
8051
8052
|
}
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
8053
|
+
const err = new Error(message2);
|
|
8054
|
+
err.name = "AbortError";
|
|
8055
|
+
return err;
|
|
8056
|
+
}
|
|
8057
|
+
async function withRetry(fn, options = {}) {
|
|
8058
|
+
const cfg = {
|
|
8059
|
+
maxRetries: options.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries,
|
|
8060
|
+
baseDelayMs: options.baseDelayMs ?? DEFAULT_RETRY_CONFIG.baseDelayMs,
|
|
8061
|
+
maxDelayMs: options.maxDelayMs ?? DEFAULT_RETRY_CONFIG.maxDelayMs
|
|
8062
|
+
};
|
|
8063
|
+
let lastError;
|
|
8064
|
+
for (let attempt = 0; attempt <= cfg.maxRetries; attempt++) {
|
|
8065
|
+
if (options.signal?.aborted) throw toAbortError("Aborted");
|
|
8055
8066
|
try {
|
|
8056
|
-
|
|
8057
|
-
|
|
8058
|
-
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
|
|
8062
|
-
|
|
8063
|
-
|
|
8064
|
-
|
|
8065
|
-
|
|
8066
|
-
|
|
8067
|
-
|
|
8068
|
-
|
|
8069
|
-
|
|
8070
|
-
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
|
|
8077
|
-
|
|
8078
|
-
|
|
8079
|
-
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8086
|
-
|
|
8087
|
-
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
8067
|
+
return await fn();
|
|
8068
|
+
} catch (err) {
|
|
8069
|
+
lastError = err;
|
|
8070
|
+
if (isAbortError(err)) throw err;
|
|
8071
|
+
if (attempt === cfg.maxRetries) break;
|
|
8072
|
+
if (!isRetryableError2(err)) throw err;
|
|
8073
|
+
const retryAfter = extractRetryAfter(err);
|
|
8074
|
+
const delay = retryAfter ?? calculateBackoffDelay(attempt, cfg);
|
|
8075
|
+
options.onRetry?.(attempt, delay, err);
|
|
8076
|
+
await sleep(delay, options.signal);
|
|
8077
|
+
}
|
|
8078
|
+
}
|
|
8079
|
+
throw lastError;
|
|
8080
|
+
}
|
|
8081
|
+
function extractRetryAfter(err) {
|
|
8082
|
+
if (err && typeof err === "object" && "retryAfter" in err) {
|
|
8083
|
+
const v = err.retryAfter;
|
|
8084
|
+
if (typeof v === "number" && v > 0) return v * 1e3;
|
|
8085
|
+
}
|
|
8086
|
+
return void 0;
|
|
8087
|
+
}
|
|
8088
|
+
function uuidv7() {
|
|
8089
|
+
const ms = BigInt(Date.now());
|
|
8090
|
+
const bytes = randomBytes(16);
|
|
8091
|
+
bytes[0] = Number(ms >> 40n & 0xffn);
|
|
8092
|
+
bytes[1] = Number(ms >> 32n & 0xffn);
|
|
8093
|
+
bytes[2] = Number(ms >> 24n & 0xffn);
|
|
8094
|
+
bytes[3] = Number(ms >> 16n & 0xffn);
|
|
8095
|
+
bytes[4] = Number(ms >> 8n & 0xffn);
|
|
8096
|
+
bytes[5] = Number(ms & 0xffn);
|
|
8097
|
+
bytes[6] = bytes[6] & 15 | 112;
|
|
8098
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
8099
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
8100
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
8101
|
+
}
|
|
8102
|
+
function randomBytes(n) {
|
|
8103
|
+
const out = new Uint8Array(n);
|
|
8104
|
+
const c = globalThis.crypto;
|
|
8105
|
+
if (c?.getRandomValues) {
|
|
8106
|
+
c.getRandomValues(out);
|
|
8107
|
+
return out;
|
|
8108
|
+
}
|
|
8109
|
+
for (let i = 0; i < n; i++) out[i] = Math.floor(Math.random() * 256);
|
|
8110
|
+
return out;
|
|
8111
|
+
}
|
|
8112
|
+
|
|
8113
|
+
// src/storage.ts
|
|
8114
|
+
var PATHS = {
|
|
8115
|
+
uploads: "/storage/uploads",
|
|
8116
|
+
upload: (id) => `/storage/uploads/${encodeURIComponent(String(id))}`,
|
|
8117
|
+
uploadComplete: (id) => `/storage/uploads/${encodeURIComponent(String(id))}:complete`,
|
|
8118
|
+
uploadPart: (id, n) => `/storage/uploads/${encodeURIComponent(String(id))}/parts/${n}`,
|
|
8119
|
+
files: "/storage/files",
|
|
8120
|
+
file: (id) => `/storage/files/${encodeURIComponent(String(id))}`,
|
|
8121
|
+
fileRestore: (id) => `/storage/files/${encodeURIComponent(String(id))}:restore`,
|
|
8122
|
+
fileSignedUrl: (id) => `/storage/files/${encodeURIComponent(String(id))}:signedUrl`,
|
|
8123
|
+
fileCopy: (id) => `/storage/files/${encodeURIComponent(String(id))}:copy`,
|
|
8124
|
+
versions: (id) => `/storage/files/${encodeURIComponent(String(id))}/versions`,
|
|
8125
|
+
versionRestore: (id, vid) => `/storage/files/${encodeURIComponent(String(id))}/versions/${encodeURIComponent(String(vid))}:restore`
|
|
8126
|
+
};
|
|
8127
|
+
var isBrowser = () => typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
|
|
8128
|
+
var hasXhr = () => typeof globalThis.XMLHttpRequest !== "undefined";
|
|
8129
|
+
var hasLocalStorage = () => {
|
|
8130
|
+
try {
|
|
8131
|
+
const ls = globalThis.localStorage;
|
|
8132
|
+
return Boolean(ls && typeof ls.getItem === "function");
|
|
8133
|
+
} catch {
|
|
8134
|
+
return false;
|
|
8135
|
+
}
|
|
8136
|
+
};
|
|
8137
|
+
async function computeSha256Hex(blob) {
|
|
8138
|
+
const subtle = globalThis.crypto?.subtle;
|
|
8139
|
+
if (subtle?.digest) {
|
|
8140
|
+
const buf2 = await blob.arrayBuffer();
|
|
8141
|
+
const digest2 = await subtle.digest("SHA-256", buf2);
|
|
8142
|
+
return bytesToHex(new Uint8Array(digest2));
|
|
8143
|
+
}
|
|
8144
|
+
const nodeCrypto = await import("crypto");
|
|
8145
|
+
const hash = nodeCrypto.createHash("sha256");
|
|
8146
|
+
const buf = Buffer.from(await blob.arrayBuffer());
|
|
8147
|
+
hash.update(buf);
|
|
8148
|
+
return hash.digest("hex");
|
|
8149
|
+
}
|
|
8150
|
+
function bytesToHex(b) {
|
|
8151
|
+
let s = "";
|
|
8152
|
+
for (let i = 0; i < b.length; i++) s += b[i].toString(16).padStart(2, "0");
|
|
8153
|
+
return s;
|
|
8154
|
+
}
|
|
8155
|
+
var RESUME_KEY_PREFIX = "sylphx_upload_";
|
|
8156
|
+
function resumeKey(uploadId) {
|
|
8157
|
+
return `${RESUME_KEY_PREFIX}${uploadId}`;
|
|
8158
|
+
}
|
|
8159
|
+
function persistResume(rec) {
|
|
8160
|
+
if (hasLocalStorage()) {
|
|
8161
|
+
try {
|
|
8162
|
+
localStorage.setItem(resumeKey(rec.uploadId), JSON.stringify(rec));
|
|
8163
|
+
} catch {
|
|
8094
8164
|
}
|
|
8165
|
+
return;
|
|
8095
8166
|
}
|
|
8096
|
-
if (!
|
|
8097
|
-
|
|
8098
|
-
code: "BAD_REQUEST"
|
|
8099
|
-
});
|
|
8167
|
+
if (!isBrowser()) {
|
|
8168
|
+
void persistResumeNode(rec);
|
|
8100
8169
|
}
|
|
8101
|
-
const { uploadUrl, publicUrl } = await tokenResponse.json();
|
|
8102
|
-
return executeUploadWithRetry(file, uploadUrl, publicUrl, options);
|
|
8103
8170
|
}
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
let lastError = null;
|
|
8107
|
-
for (let attempt = 0; attempt <= UPLOAD_RETRY_CONFIG.maxRetries; attempt++) {
|
|
8171
|
+
function clearResume(uploadId) {
|
|
8172
|
+
if (hasLocalStorage()) {
|
|
8108
8173
|
try {
|
|
8109
|
-
|
|
8110
|
-
} catch
|
|
8111
|
-
if (error instanceof DOMException && error.name === "AbortError") {
|
|
8112
|
-
throw error;
|
|
8113
|
-
}
|
|
8114
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
8115
|
-
if (isRetryableError2(error) && attempt < UPLOAD_RETRY_CONFIG.maxRetries) {
|
|
8116
|
-
const delay = calculateBackoffDelay(attempt);
|
|
8117
|
-
await sleep(delay, signal);
|
|
8118
|
-
continue;
|
|
8119
|
-
}
|
|
8120
|
-
throw lastError;
|
|
8174
|
+
localStorage.removeItem(resumeKey(uploadId));
|
|
8175
|
+
} catch {
|
|
8121
8176
|
}
|
|
8177
|
+
return;
|
|
8122
8178
|
}
|
|
8123
|
-
|
|
8179
|
+
if (!isBrowser()) void clearResumeNode(uploadId);
|
|
8180
|
+
}
|
|
8181
|
+
async function persistResumeNode(rec) {
|
|
8182
|
+
try {
|
|
8183
|
+
const fs = await import("fs/promises");
|
|
8184
|
+
const path = await import("path");
|
|
8185
|
+
const os = await import("os");
|
|
8186
|
+
const dir = path.join(os.homedir(), ".sylphx");
|
|
8187
|
+
await fs.mkdir(dir, { recursive: true });
|
|
8188
|
+
const file = path.join(dir, "uploads.json");
|
|
8189
|
+
let store = {};
|
|
8190
|
+
try {
|
|
8191
|
+
const text = await fs.readFile(file, "utf8");
|
|
8192
|
+
store = JSON.parse(text);
|
|
8193
|
+
} catch {
|
|
8194
|
+
store = {};
|
|
8195
|
+
}
|
|
8196
|
+
store[rec.uploadId] = rec;
|
|
8197
|
+
await fs.writeFile(file, JSON.stringify(store), "utf8");
|
|
8198
|
+
} catch {
|
|
8199
|
+
}
|
|
8200
|
+
}
|
|
8201
|
+
async function clearResumeNode(uploadId) {
|
|
8202
|
+
try {
|
|
8203
|
+
const fs = await import("fs/promises");
|
|
8204
|
+
const path = await import("path");
|
|
8205
|
+
const os = await import("os");
|
|
8206
|
+
const file = path.join(os.homedir(), ".sylphx", "uploads.json");
|
|
8207
|
+
const text = await fs.readFile(file, "utf8");
|
|
8208
|
+
const store = JSON.parse(text);
|
|
8209
|
+
delete store[uploadId];
|
|
8210
|
+
await fs.writeFile(file, JSON.stringify(store), "utf8");
|
|
8211
|
+
} catch {
|
|
8212
|
+
}
|
|
8213
|
+
}
|
|
8214
|
+
function putBlob(url, body, headers, signal, onProgress) {
|
|
8215
|
+
if (hasXhr()) return putBlobXhr(url, body, headers, signal, onProgress);
|
|
8216
|
+
return putBlobFetch(url, body, headers, signal, onProgress);
|
|
8124
8217
|
}
|
|
8125
|
-
function
|
|
8126
|
-
const { signal, onProgress } = options ?? {};
|
|
8218
|
+
function putBlobXhr(url, body, headers, signal, onProgress) {
|
|
8127
8219
|
return new Promise((resolve, reject) => {
|
|
8128
8220
|
const xhr = new XMLHttpRequest();
|
|
8129
8221
|
const handleAbort = () => {
|
|
8130
|
-
|
|
8131
|
-
|
|
8222
|
+
try {
|
|
8223
|
+
xhr.abort();
|
|
8224
|
+
} catch {
|
|
8225
|
+
}
|
|
8226
|
+
reject(toAbortError("Upload aborted"));
|
|
8132
8227
|
};
|
|
8133
8228
|
if (signal?.aborted) {
|
|
8134
|
-
reject(
|
|
8229
|
+
reject(toAbortError("Upload aborted"));
|
|
8135
8230
|
return;
|
|
8136
8231
|
}
|
|
8137
8232
|
signal?.addEventListener("abort", handleAbort, { once: true });
|
|
8138
|
-
|
|
8139
|
-
|
|
8140
|
-
onProgress(
|
|
8141
|
-
|
|
8142
|
-
|
|
8143
|
-
progress: Math.round(event.loaded / event.total * 100)
|
|
8144
|
-
});
|
|
8145
|
-
}
|
|
8146
|
-
});
|
|
8233
|
+
if (onProgress) {
|
|
8234
|
+
xhr.upload.addEventListener("progress", (e) => {
|
|
8235
|
+
if (e.lengthComputable) onProgress(e.loaded);
|
|
8236
|
+
});
|
|
8237
|
+
}
|
|
8147
8238
|
xhr.addEventListener("load", () => {
|
|
8148
8239
|
signal?.removeEventListener("abort", handleAbort);
|
|
8149
8240
|
if (xhr.status >= 200 && xhr.status < 300) {
|
|
8150
|
-
|
|
8151
|
-
|
|
8152
|
-
pathname: options?.path ? `${options.path}/${file.name}` : file.name,
|
|
8153
|
-
contentType: file.type,
|
|
8154
|
-
size: file.size
|
|
8155
|
-
});
|
|
8241
|
+
const etag = resolveXhrEtag(xhr);
|
|
8242
|
+
resolve({ etag: stripQuotes(etag), status: xhr.status });
|
|
8156
8243
|
} else {
|
|
8157
|
-
const
|
|
8158
|
-
|
|
8159
|
-
reject(
|
|
8244
|
+
const err = new Error(`PUT failed with status ${xhr.status}`);
|
|
8245
|
+
err.status = xhr.status;
|
|
8246
|
+
reject(err);
|
|
8160
8247
|
}
|
|
8161
8248
|
});
|
|
8162
8249
|
xhr.addEventListener("error", () => {
|
|
@@ -8165,93 +8252,291 @@ function executeUpload(file, uploadUrl, publicUrl, options) {
|
|
|
8165
8252
|
});
|
|
8166
8253
|
xhr.addEventListener("abort", () => {
|
|
8167
8254
|
signal?.removeEventListener("abort", handleAbort);
|
|
8168
|
-
reject(
|
|
8255
|
+
reject(toAbortError("Upload aborted"));
|
|
8169
8256
|
});
|
|
8170
|
-
xhr.open("PUT",
|
|
8171
|
-
|
|
8172
|
-
|
|
8173
|
-
|
|
8174
|
-
}
|
|
8175
|
-
|
|
8176
|
-
|
|
8177
|
-
|
|
8178
|
-
type: "avatar",
|
|
8179
|
-
userId
|
|
8257
|
+
xhr.open("PUT", url);
|
|
8258
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
8259
|
+
try {
|
|
8260
|
+
xhr.setRequestHeader(k, v);
|
|
8261
|
+
} catch {
|
|
8262
|
+
}
|
|
8263
|
+
}
|
|
8264
|
+
xhr.send(body);
|
|
8180
8265
|
});
|
|
8181
8266
|
}
|
|
8182
|
-
|
|
8183
|
-
const
|
|
8184
|
-
|
|
8185
|
-
|
|
8186
|
-
endpoint.path.replace(":id", encodeURIComponent(fileId)),
|
|
8187
|
-
{ method: endpoint.method }
|
|
8188
|
-
);
|
|
8189
|
-
}
|
|
8190
|
-
async function getFileUrl(config, fileId) {
|
|
8191
|
-
const data = await callApi(config, `/storage/files/${fileId}`, { method: "GET" });
|
|
8192
|
-
return data.url;
|
|
8267
|
+
function resolveXhrEtag(xhr) {
|
|
8268
|
+
const canonical = xhr.getResponseHeader("ETag");
|
|
8269
|
+
if (canonical !== null) return canonical;
|
|
8270
|
+
return xhr.getResponseHeader("etag") ?? "";
|
|
8193
8271
|
}
|
|
8194
|
-
async function
|
|
8195
|
-
|
|
8196
|
-
|
|
8197
|
-
|
|
8272
|
+
async function putBlobFetch(url, body, headers, signal, onProgress) {
|
|
8273
|
+
let stream = body;
|
|
8274
|
+
if (onProgress && typeof TransformStream !== "undefined" && typeof body.stream === "function") {
|
|
8275
|
+
let loaded = 0;
|
|
8276
|
+
const counter = new TransformStream({
|
|
8277
|
+
transform(chunk, controller) {
|
|
8278
|
+
loaded += chunk.byteLength;
|
|
8279
|
+
onProgress(loaded);
|
|
8280
|
+
controller.enqueue(chunk);
|
|
8281
|
+
}
|
|
8282
|
+
});
|
|
8283
|
+
stream = body.stream().pipeThrough(counter);
|
|
8284
|
+
}
|
|
8285
|
+
const res = await fetch(url, {
|
|
8286
|
+
method: "PUT",
|
|
8287
|
+
body: stream,
|
|
8288
|
+
headers,
|
|
8289
|
+
signal,
|
|
8290
|
+
// Required when streaming a request body in Chromium / undici.
|
|
8291
|
+
// Cast: TS lib lacks `duplex`.
|
|
8292
|
+
...{ duplex: "half" }
|
|
8198
8293
|
});
|
|
8294
|
+
if (!res.ok) {
|
|
8295
|
+
const err = new Error(`PUT failed with status ${res.status}`);
|
|
8296
|
+
err.status = res.status;
|
|
8297
|
+
throw err;
|
|
8298
|
+
}
|
|
8299
|
+
const etag = resolveFetchEtag(res.headers);
|
|
8300
|
+
if (onProgress) onProgress(body.size);
|
|
8301
|
+
return { etag: stripQuotes(etag), status: res.status };
|
|
8199
8302
|
}
|
|
8200
|
-
|
|
8201
|
-
const
|
|
8202
|
-
|
|
8203
|
-
|
|
8204
|
-
{ method: "GET" }
|
|
8205
|
-
);
|
|
8206
|
-
return data.versions;
|
|
8303
|
+
function resolveFetchEtag(headers) {
|
|
8304
|
+
const lowerCase = headers.get("etag");
|
|
8305
|
+
if (lowerCase !== null) return lowerCase;
|
|
8306
|
+
return headers.get("ETag") ?? "";
|
|
8207
8307
|
}
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
`/storage/files/${encodeURIComponent(fileId)}/versions/${encodeURIComponent(versionId)}/restore`,
|
|
8212
|
-
{ method: "POST" }
|
|
8213
|
-
);
|
|
8214
|
-
return data.version;
|
|
8308
|
+
function stripQuotes(s) {
|
|
8309
|
+
if (s.length >= 2 && s.startsWith('"') && s.endsWith('"')) return s.slice(1, -1);
|
|
8310
|
+
return s;
|
|
8215
8311
|
}
|
|
8216
|
-
async function
|
|
8217
|
-
|
|
8218
|
-
|
|
8312
|
+
async function putWithRetry(url, body, headers, signal, onProgress) {
|
|
8313
|
+
let lastErr;
|
|
8314
|
+
const maxRetries = 5;
|
|
8315
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
8316
|
+
if (signal?.aborted) throw toAbortError();
|
|
8317
|
+
try {
|
|
8318
|
+
return await putBlob(url, body, headers, signal, onProgress);
|
|
8319
|
+
} catch (err) {
|
|
8320
|
+
if (isAbortError(err)) throw err;
|
|
8321
|
+
lastErr = err;
|
|
8322
|
+
if (attempt === maxRetries || !isRetryableError2(err)) throw err;
|
|
8323
|
+
await sleep(calculateBackoffDelay(attempt), signal);
|
|
8324
|
+
}
|
|
8325
|
+
}
|
|
8326
|
+
throw lastErr;
|
|
8327
|
+
}
|
|
8328
|
+
async function uploadsCreate(config, blob, options) {
|
|
8329
|
+
const signal = options.signal;
|
|
8330
|
+
if (signal?.aborted) throw toAbortError();
|
|
8331
|
+
const filename = options.filename ?? (isFile(blob) ? blob.name : void 0) ?? "upload.bin";
|
|
8332
|
+
const contentType = options.contentType ?? (blob.type && blob.type.length > 0 ? blob.type : "application/octet-stream");
|
|
8333
|
+
const size = blob.size;
|
|
8334
|
+
const checksumSha256 = options.checksumSha256 ?? await computeSha256Hex(blob);
|
|
8335
|
+
const idempotencyKey = options.idempotencyKey ?? uuidv7();
|
|
8336
|
+
const createBody = {
|
|
8337
|
+
filename,
|
|
8338
|
+
contentType,
|
|
8339
|
+
size,
|
|
8340
|
+
folder: options.folder,
|
|
8341
|
+
visibility: options.visibility,
|
|
8342
|
+
metadata: options.metadata,
|
|
8343
|
+
checksumSha256,
|
|
8344
|
+
ifNoneMatch: options.ifNoneMatch
|
|
8345
|
+
};
|
|
8346
|
+
const session = await callApi(config, PATHS.uploads, {
|
|
8347
|
+
method: "POST",
|
|
8348
|
+
body: createBody,
|
|
8349
|
+
idempotencyKey,
|
|
8350
|
+
signal
|
|
8219
8351
|
});
|
|
8352
|
+
const uploadId = session.uploadId;
|
|
8353
|
+
const onAborted = async () => {
|
|
8354
|
+
try {
|
|
8355
|
+
await callApi(config, PATHS.upload(uploadId), { method: "DELETE" });
|
|
8356
|
+
} catch {
|
|
8357
|
+
}
|
|
8358
|
+
clearResume(String(uploadId));
|
|
8359
|
+
};
|
|
8360
|
+
try {
|
|
8361
|
+
if (session.method === "PUT") {
|
|
8362
|
+
return await runSinglePart(config, blob, session, options, idempotencyKey);
|
|
8363
|
+
}
|
|
8364
|
+
return await runMultipart(config, blob, session, options, idempotencyKey);
|
|
8365
|
+
} catch (err) {
|
|
8366
|
+
if (isAbortError(err)) {
|
|
8367
|
+
await onAborted();
|
|
8368
|
+
}
|
|
8369
|
+
throw err;
|
|
8370
|
+
}
|
|
8220
8371
|
}
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
config,
|
|
8224
|
-
`/storage/files/${encodeURIComponent(fileId)}/restore`,
|
|
8225
|
-
{ method: "POST" }
|
|
8226
|
-
);
|
|
8227
|
-
return data.file;
|
|
8372
|
+
function isFile(b) {
|
|
8373
|
+
return typeof b.name === "string";
|
|
8228
8374
|
}
|
|
8229
|
-
async function
|
|
8230
|
-
const
|
|
8375
|
+
async function runSinglePart(config, blob, session, options, idempotencyKey) {
|
|
8376
|
+
const { onProgress, signal } = options;
|
|
8377
|
+
const total = blob.size;
|
|
8378
|
+
const trackProgress = onProgress ? (loaded) => {
|
|
8379
|
+
onProgress({ loaded, total, partsCompleted: 0, partsTotal: 1 });
|
|
8380
|
+
} : void 0;
|
|
8381
|
+
const put = await putWithRetry(session.url, blob, session.headers, signal, trackProgress);
|
|
8382
|
+
if (onProgress) onProgress({ loaded: total, total, partsCompleted: 1, partsTotal: 1 });
|
|
8383
|
+
const completion = await callApi(
|
|
8231
8384
|
config,
|
|
8232
|
-
|
|
8385
|
+
PATHS.uploadComplete(session.uploadId),
|
|
8233
8386
|
{
|
|
8234
8387
|
method: "POST",
|
|
8235
|
-
body: {
|
|
8388
|
+
body: { parts: [{ partNumber: 1, etag: put.etag }] },
|
|
8389
|
+
idempotencyKey: `${idempotencyKey}:complete`,
|
|
8390
|
+
signal
|
|
8236
8391
|
}
|
|
8237
8392
|
);
|
|
8238
|
-
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
|
|
8393
|
+
clearResume(String(session.uploadId));
|
|
8394
|
+
return await fetchFile(config, completion.fileId);
|
|
8395
|
+
}
|
|
8396
|
+
async function runMultipart(config, blob, session, options, idempotencyKey) {
|
|
8397
|
+
const { onProgress, signal } = options;
|
|
8398
|
+
const partSize = session.partSize;
|
|
8399
|
+
const total = blob.size;
|
|
8400
|
+
const partsTotal = session.partCount;
|
|
8401
|
+
const completedParts = [];
|
|
8402
|
+
const partLoaded = /* @__PURE__ */ new Map();
|
|
8403
|
+
const reportProgress = () => {
|
|
8404
|
+
if (!onProgress) return;
|
|
8405
|
+
let loaded = 0;
|
|
8406
|
+
for (const v of partLoaded.values()) loaded += v;
|
|
8407
|
+
onProgress({ loaded, total, partsCompleted: completedParts.length, partsTotal });
|
|
8408
|
+
};
|
|
8409
|
+
for (const part of session.parts) {
|
|
8410
|
+
if (signal?.aborted) throw toAbortError();
|
|
8411
|
+
const offset = (part.partNumber - 1) * partSize;
|
|
8412
|
+
const end = Math.min(offset + partSize, total);
|
|
8413
|
+
const slice = blob.slice(offset, end);
|
|
8414
|
+
const trackProgress = onProgress ? (loaded) => {
|
|
8415
|
+
partLoaded.set(part.partNumber, loaded);
|
|
8416
|
+
reportProgress();
|
|
8417
|
+
} : void 0;
|
|
8418
|
+
const result = await putWithRetry(part.url, slice, {}, signal, trackProgress);
|
|
8419
|
+
completedParts.push({ partNumber: part.partNumber, etag: result.etag });
|
|
8420
|
+
partLoaded.set(part.partNumber, slice.size);
|
|
8421
|
+
reportProgress();
|
|
8422
|
+
persistResume({
|
|
8423
|
+
uploadId: String(session.uploadId),
|
|
8424
|
+
completedParts: [...completedParts],
|
|
8425
|
+
updatedAt: Date.now()
|
|
8242
8426
|
});
|
|
8243
8427
|
}
|
|
8244
|
-
|
|
8428
|
+
const completion = await callApi(
|
|
8429
|
+
config,
|
|
8430
|
+
PATHS.uploadComplete(session.uploadId),
|
|
8431
|
+
{
|
|
8432
|
+
method: "POST",
|
|
8433
|
+
body: { parts: completedParts },
|
|
8434
|
+
idempotencyKey: `${idempotencyKey}:complete`,
|
|
8435
|
+
signal
|
|
8436
|
+
}
|
|
8437
|
+
);
|
|
8438
|
+
clearResume(String(session.uploadId));
|
|
8439
|
+
return await fetchFile(config, completion.fileId);
|
|
8245
8440
|
}
|
|
8246
|
-
async function
|
|
8247
|
-
return callApi(config, "
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8441
|
+
async function fetchFile(config, fileId) {
|
|
8442
|
+
return callApi(config, PATHS.file(fileId), { method: "GET" });
|
|
8443
|
+
}
|
|
8444
|
+
async function uploadsAbort(config, uploadId) {
|
|
8445
|
+
await withRetry(() => callApi(config, PATHS.upload(uploadId), { method: "DELETE" }), {
|
|
8446
|
+
maxRetries: 3
|
|
8447
|
+
});
|
|
8448
|
+
clearResume(String(uploadId));
|
|
8449
|
+
}
|
|
8450
|
+
function filesListPage(config, options, cursor) {
|
|
8451
|
+
const query = {
|
|
8452
|
+
folder: options.folder,
|
|
8453
|
+
cursor: cursor ?? options.cursor,
|
|
8454
|
+
limit: options.limit,
|
|
8455
|
+
includeDeleted: options.includeDeleted
|
|
8456
|
+
};
|
|
8457
|
+
return callApi(config, PATHS.files, {
|
|
8458
|
+
method: "GET",
|
|
8459
|
+
query: {
|
|
8460
|
+
folder: query.folder,
|
|
8461
|
+
cursor: query.cursor,
|
|
8462
|
+
limit: query.limit,
|
|
8463
|
+
includeDeleted: query.includeDeleted
|
|
8464
|
+
}
|
|
8465
|
+
});
|
|
8466
|
+
}
|
|
8467
|
+
function filesList(config, options = {}) {
|
|
8468
|
+
const fetchPage = (cursor) => filesListPage(config, options, cursor);
|
|
8469
|
+
const iter = {
|
|
8470
|
+
[Symbol.asyncIterator]: async function* () {
|
|
8471
|
+
let cursor = options.cursor;
|
|
8472
|
+
do {
|
|
8473
|
+
const page2 = await fetchPage(cursor ?? void 0);
|
|
8474
|
+
for (const f of page2.files) yield f;
|
|
8475
|
+
cursor = page2.nextCursor;
|
|
8476
|
+
} while (cursor);
|
|
8252
8477
|
}
|
|
8478
|
+
};
|
|
8479
|
+
return Object.assign(iter, { fetchPage });
|
|
8480
|
+
}
|
|
8481
|
+
async function filesGet(config, fileId) {
|
|
8482
|
+
return callApi(config, PATHS.file(fileId), { method: "GET" });
|
|
8483
|
+
}
|
|
8484
|
+
async function filesDelete(config, fileId) {
|
|
8485
|
+
return callApi(config, PATHS.file(fileId), { method: "DELETE" });
|
|
8486
|
+
}
|
|
8487
|
+
async function filesRestore(config, fileId) {
|
|
8488
|
+
return callApi(config, PATHS.fileRestore(fileId), { method: "POST" });
|
|
8489
|
+
}
|
|
8490
|
+
async function filesSignedUrl(config, fileId, options = {}) {
|
|
8491
|
+
const body = {
|
|
8492
|
+
expiresIn: options.expiresIn,
|
|
8493
|
+
disposition: options.disposition,
|
|
8494
|
+
userId: options.userId
|
|
8495
|
+
};
|
|
8496
|
+
return callApi(config, PATHS.fileSignedUrl(fileId), {
|
|
8497
|
+
method: "POST",
|
|
8498
|
+
body
|
|
8499
|
+
});
|
|
8500
|
+
}
|
|
8501
|
+
async function filesCopy(config, fileId, options) {
|
|
8502
|
+
const body = {
|
|
8503
|
+
folder: options.folder,
|
|
8504
|
+
filename: options.filename,
|
|
8505
|
+
visibility: options.visibility,
|
|
8506
|
+
metadata: options.metadata
|
|
8507
|
+
};
|
|
8508
|
+
return callApi(config, PATHS.fileCopy(fileId), {
|
|
8509
|
+
method: "POST",
|
|
8510
|
+
body
|
|
8253
8511
|
});
|
|
8254
8512
|
}
|
|
8513
|
+
async function filesVersionsList(config, fileId) {
|
|
8514
|
+
const data = await callApi(config, PATHS.versions(fileId), {
|
|
8515
|
+
method: "GET"
|
|
8516
|
+
});
|
|
8517
|
+
return data.versions;
|
|
8518
|
+
}
|
|
8519
|
+
async function filesVersionsRestore(config, fileId, versionId) {
|
|
8520
|
+
return callApi(config, PATHS.versionRestore(fileId, versionId), { method: "POST" });
|
|
8521
|
+
}
|
|
8522
|
+
var storage = {
|
|
8523
|
+
uploads: {
|
|
8524
|
+
create: uploadsCreate,
|
|
8525
|
+
abort: uploadsAbort
|
|
8526
|
+
},
|
|
8527
|
+
files: {
|
|
8528
|
+
list: filesList,
|
|
8529
|
+
get: filesGet,
|
|
8530
|
+
delete: filesDelete,
|
|
8531
|
+
restore: filesRestore,
|
|
8532
|
+
signedUrl: filesSignedUrl,
|
|
8533
|
+
copy: filesCopy,
|
|
8534
|
+
versions: {
|
|
8535
|
+
list: filesVersionsList,
|
|
8536
|
+
restore: filesVersionsRestore
|
|
8537
|
+
}
|
|
8538
|
+
}
|
|
8539
|
+
};
|
|
8255
8540
|
|
|
8256
8541
|
// src/lib/notifications/service-worker.ts
|
|
8257
8542
|
function initPushServiceWorker(config = {}) {
|
|
@@ -10698,7 +10983,6 @@ export {
|
|
|
10698
10983
|
deleteCron,
|
|
10699
10984
|
deleteDocument,
|
|
10700
10985
|
deleteEnvVar,
|
|
10701
|
-
deleteFile,
|
|
10702
10986
|
deleteOrganization,
|
|
10703
10987
|
deletePasskey,
|
|
10704
10988
|
deletePermission,
|
|
@@ -10710,7 +10994,6 @@ export {
|
|
|
10710
10994
|
disableDebug,
|
|
10711
10995
|
disableTwoFactor,
|
|
10712
10996
|
disconnectOAuthProvider,
|
|
10713
|
-
downloadFileVersion,
|
|
10714
10997
|
dpop,
|
|
10715
10998
|
embed,
|
|
10716
10999
|
enableDebug,
|
|
@@ -10743,8 +11026,6 @@ export {
|
|
|
10743
11026
|
getErrorCode,
|
|
10744
11027
|
getErrorMessage,
|
|
10745
11028
|
getFacets,
|
|
10746
|
-
getFileInfo,
|
|
10747
|
-
getFileUrl,
|
|
10748
11029
|
getFlagPayload,
|
|
10749
11030
|
getFlags,
|
|
10750
11031
|
getLeaderboard,
|
|
@@ -10771,7 +11052,6 @@ export {
|
|
|
10771
11052
|
getSecrets,
|
|
10772
11053
|
getSecurityScore,
|
|
10773
11054
|
getSession,
|
|
10774
|
-
getSignedUrl,
|
|
10775
11055
|
getStreak,
|
|
10776
11056
|
getSubscription,
|
|
10777
11057
|
getTask,
|
|
@@ -10829,7 +11109,6 @@ export {
|
|
|
10829
11109
|
leaveOrganization,
|
|
10830
11110
|
linkAnonymousConsents,
|
|
10831
11111
|
listEnvVars,
|
|
10832
|
-
listFileVersions,
|
|
10833
11112
|
listOAuthProviders,
|
|
10834
11113
|
listPasskeys,
|
|
10835
11114
|
listPermissions,
|
|
@@ -10871,13 +11150,12 @@ export {
|
|
|
10871
11150
|
replayWebhookDelivery,
|
|
10872
11151
|
requestEmailChange,
|
|
10873
11152
|
rescheduleEmail,
|
|
11153
|
+
resendVerificationEmail,
|
|
10874
11154
|
resetCircuitBreaker,
|
|
10875
11155
|
resetDebugModeCache,
|
|
10876
11156
|
resetPassword,
|
|
10877
11157
|
resetPlatformCookieCache,
|
|
10878
11158
|
resetPlatformJwksCache,
|
|
10879
|
-
restoreFile,
|
|
10880
|
-
restoreFileVersion,
|
|
10881
11159
|
resumeCron,
|
|
10882
11160
|
revokeAllTokens,
|
|
10883
11161
|
revokeOrganizationInvitation,
|
|
@@ -10899,8 +11177,8 @@ export {
|
|
|
10899
11177
|
signIn,
|
|
10900
11178
|
signOut,
|
|
10901
11179
|
signUp,
|
|
10902
|
-
softDeleteFile,
|
|
10903
11180
|
startPasskeyRegistration,
|
|
11181
|
+
storage,
|
|
10904
11182
|
streamToString,
|
|
10905
11183
|
submitScore,
|
|
10906
11184
|
suspendUser,
|
|
@@ -10921,8 +11199,6 @@ export {
|
|
|
10921
11199
|
updateUserMetadata,
|
|
10922
11200
|
updateUserProfile,
|
|
10923
11201
|
updateWebhookConfig,
|
|
10924
|
-
uploadAvatar,
|
|
10925
|
-
uploadFile,
|
|
10926
11202
|
upsertDocument,
|
|
10927
11203
|
user,
|
|
10928
11204
|
userInfo,
|