@sylphx/sdk 0.9.0 → 0.10.1
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 +13 -12
- package/dist/index.d.ts +143 -310
- package/dist/index.mjs +492 -246
- package/dist/index.mjs.map +1 -1
- package/dist/nextjs/index.d.ts +19 -6
- package/dist/nextjs/index.mjs +127 -22
- package/dist/nextjs/index.mjs.map +1 -1
- package/dist/react/index.d.ts +106 -317
- package/dist/react/index.mjs +1417 -1075
- package/dist/react/index.mjs.map +1 -1
- package/dist/server/index.d.ts +41 -34
- package/dist/server/index.mjs +81 -66
- package/dist/server/index.mjs.map +1 -1
- package/dist/web-analytics.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -4783,7 +4783,7 @@ var init_webapi = __esm({
|
|
|
4783
4783
|
// src/connection-url.ts
|
|
4784
4784
|
var SYLPHX_PROTOCOL = "sylphx:";
|
|
4785
4785
|
var DEFAULT_VERSION = "v1";
|
|
4786
|
-
var CREDENTIAL_REGEX = /^(pk|sk)_(dev|stg|prod|prev)_[a-f0-9]{32,64}$/;
|
|
4786
|
+
var CREDENTIAL_REGEX = /^(pk|sk)_(dev|stg|prod|prev)(?:_[a-z0-9]{12})?_[a-f0-9]{32,64}$/;
|
|
4787
4787
|
var VERSION_REGEX = /^v[0-9]+$/;
|
|
4788
4788
|
var SLUG_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
|
|
4789
4789
|
var InvalidConnectionUrlError = class _InvalidConnectionUrlError extends Error {
|
|
@@ -4800,7 +4800,7 @@ function fail(reason) {
|
|
|
4800
4800
|
function parseCredential(raw) {
|
|
4801
4801
|
const match = CREDENTIAL_REGEX.exec(raw);
|
|
4802
4802
|
if (!match) {
|
|
4803
|
-
fail(`credential must match (pk|sk)_(dev|stg|prod|prev)_
|
|
4803
|
+
fail(`credential must match (pk|sk)_(dev|stg|prod|prev)(_{ref})?_{hex}, got "${raw}"`);
|
|
4804
4804
|
}
|
|
4805
4805
|
return {
|
|
4806
4806
|
credentialType: match[1],
|
|
@@ -4879,7 +4879,7 @@ init_constants();
|
|
|
4879
4879
|
init_errors();
|
|
4880
4880
|
var LEGACY_EMBEDDED_REF_PATTERN = /^(pk|sk)_(dev|stg|prod|prev)_[a-z0-9]{12}_[a-f0-9]+$/;
|
|
4881
4881
|
var LEGACY_APP_KEY_PATTERN = /^app_(dev|stg|prod|prev)_/;
|
|
4882
|
-
var MIGRATION_MESSAGE = "API key format has changed. Use a sylphx:// connection URL instead.\n\nNew format: sylphx://pk_prod_{hex}@your-slug.api.sylphx.com\n\nGenerate new credentials from the Sylphx Console \u2192 Your App \u2192 Environments.\nSee https://docs.sylphx.com/migration for details.";
|
|
4882
|
+
var MIGRATION_MESSAGE = "API key format has changed. Use a sylphx:// connection URL instead.\n\nNew format: sylphx://pk_prod_{ref?}_{hex}@your-slug.api.sylphx.com\n\nGenerate new credentials from the Sylphx Console \u2192 Your App \u2192 Environments.\nSee https://docs.sylphx.com/migration for details.";
|
|
4883
4883
|
function rejectLegacyKeyFormat(input) {
|
|
4884
4884
|
const trimmed = input.trim().toLowerCase();
|
|
4885
4885
|
if (LEGACY_APP_KEY_PATTERN.test(trimmed)) {
|
|
@@ -5012,7 +5012,7 @@ function createConfigFromComponents(input) {
|
|
|
5012
5012
|
});
|
|
5013
5013
|
}
|
|
5014
5014
|
throw new SylphxError(
|
|
5015
|
-
`[Sylphx] Invalid credential format. Expected (pk|sk)_(dev|stg|prod|prev)_
|
|
5015
|
+
`[Sylphx] Invalid credential format. Expected (pk|sk)_(dev|stg|prod|prev)(_{ref})?_{hex}. Got: "${trimmedCred.slice(0, 30)}..."`,
|
|
5016
5016
|
{ code: "BAD_REQUEST" }
|
|
5017
5017
|
);
|
|
5018
5018
|
}
|
|
@@ -5291,11 +5291,12 @@ init_constants();
|
|
|
5291
5291
|
init_errors();
|
|
5292
5292
|
|
|
5293
5293
|
// src/key-validation.ts
|
|
5294
|
-
var SECRET_KEY_PATTERN = /^sk_(dev|stg|prod)_[a-z0-9_-]+$/;
|
|
5294
|
+
var SECRET_KEY_PATTERN = /^sk_(dev|stg|prod|prev)_[a-z0-9_-]+$/;
|
|
5295
5295
|
var ENV_PREFIX_MAP = {
|
|
5296
5296
|
dev: "development",
|
|
5297
5297
|
stg: "staging",
|
|
5298
|
-
prod: "production"
|
|
5298
|
+
prod: "production",
|
|
5299
|
+
prev: "preview"
|
|
5299
5300
|
};
|
|
5300
5301
|
function detectKeyIssues(key) {
|
|
5301
5302
|
const issues = [];
|
|
@@ -5320,7 +5321,7 @@ The SDK will automatically sanitize the key, but fixing the source is recommende
|
|
|
5320
5321
|
}
|
|
5321
5322
|
function createInvalidKeyError(keyType, key, envVarName) {
|
|
5322
5323
|
const maskedKey = key.length > 20 ? `${key.slice(0, 20)}...` : key;
|
|
5323
|
-
const formatHint = keyType === "appId" ? "pk_(dev|stg|prod)_{ref}_{hex} or app_(dev|stg|prod)_[id]" : "sk_(dev|stg|prod)_{ref}_{hex}";
|
|
5324
|
+
const formatHint = keyType === "appId" ? "pk_(dev|stg|prod|prev)_{ref}_{hex} or app_(dev|stg|prod|prev)_[id]" : "sk_(dev|stg|prod|prev)_{ref}_{hex}";
|
|
5324
5325
|
const keyTypeName = keyType === "appId" ? "App ID" : "Secret Key";
|
|
5325
5326
|
return `[Sylphx] Invalid ${keyTypeName} format.
|
|
5326
5327
|
|
|
@@ -5333,7 +5334,7 @@ You can find your keys in the Sylphx Console \u2192 API Keys.
|
|
|
5333
5334
|
Common issues:
|
|
5334
5335
|
\u2022 Key has uppercase characters (must be lowercase)
|
|
5335
5336
|
\u2022 Key has wrong prefix (App ID: pk_ or app_, Secret Key: sk_)
|
|
5336
|
-
\u2022 Key has invalid environment (must be dev, stg, or
|
|
5337
|
+
\u2022 Key has invalid environment (must be dev, stg, prod, or prev)
|
|
5337
5338
|
\u2022 Key was copied with extra whitespace`;
|
|
5338
5339
|
}
|
|
5339
5340
|
function extractEnvironment(key) {
|
|
@@ -5380,7 +5381,7 @@ function validateKeyForType(key, keyType, pattern, envVarName) {
|
|
|
5380
5381
|
};
|
|
5381
5382
|
}
|
|
5382
5383
|
function validateSecretKey(key) {
|
|
5383
|
-
return validateKeyForType(key, "secret", SECRET_KEY_PATTERN, "
|
|
5384
|
+
return validateKeyForType(key, "secret", SECRET_KEY_PATTERN, "secret credential");
|
|
5384
5385
|
}
|
|
5385
5386
|
function validateAndSanitizeSecretKey(key) {
|
|
5386
5387
|
const result = validateSecretKey(key);
|
|
@@ -5402,7 +5403,14 @@ async function runPipeline(middlewares, initial) {
|
|
|
5402
5403
|
if (next) request = next;
|
|
5403
5404
|
}
|
|
5404
5405
|
}
|
|
5405
|
-
|
|
5406
|
+
const fetchWithMiddleware = middlewares.reduceRight(
|
|
5407
|
+
(next, mw) => mw.onFetch ? async (request2) => {
|
|
5408
|
+
const response2 = await mw.onFetch?.({ request: request2, next });
|
|
5409
|
+
return response2 ?? next(request2);
|
|
5410
|
+
} : next,
|
|
5411
|
+
(request2) => fetch(request2)
|
|
5412
|
+
);
|
|
5413
|
+
let response = await fetchWithMiddleware(request);
|
|
5406
5414
|
for (const mw of middlewares) {
|
|
5407
5415
|
if (mw.onResponse) {
|
|
5408
5416
|
const next = await mw.onResponse({ request, response });
|
|
@@ -5421,6 +5429,11 @@ function buildUrl(baseUrl, path, params) {
|
|
|
5421
5429
|
).toString();
|
|
5422
5430
|
return `${url}?${search2}`;
|
|
5423
5431
|
}
|
|
5432
|
+
function cloneRequestWithHeaders(request, updateHeaders) {
|
|
5433
|
+
const headers = new Headers(request.headers);
|
|
5434
|
+
updateHeaders(headers);
|
|
5435
|
+
return new Request(request, { headers });
|
|
5436
|
+
}
|
|
5424
5437
|
function interpolatePath(path, pathParams) {
|
|
5425
5438
|
if (!pathParams) return path;
|
|
5426
5439
|
return path.replace(/\{(\w+)\}/g, (_match, key) => {
|
|
@@ -5483,66 +5496,51 @@ function buildClient(baseUrl, baseHeaders) {
|
|
|
5483
5496
|
function createAuthMiddleware(config) {
|
|
5484
5497
|
return {
|
|
5485
5498
|
async onRequest({ request }) {
|
|
5486
|
-
request
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5499
|
+
return cloneRequestWithHeaders(request, (headers) => {
|
|
5500
|
+
headers.set("X-SDK-Version", SDK_VERSION);
|
|
5501
|
+
headers.set("X-SDK-Platform", SDK_PLATFORM);
|
|
5502
|
+
if (config.secretKey) {
|
|
5503
|
+
headers.set("x-app-secret", config.secretKey);
|
|
5504
|
+
}
|
|
5505
|
+
const token = config.getAccessToken?.();
|
|
5506
|
+
if (token) {
|
|
5507
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
5508
|
+
}
|
|
5509
|
+
});
|
|
5496
5510
|
}
|
|
5497
5511
|
};
|
|
5498
5512
|
}
|
|
5499
5513
|
function isRetryableStatus(status) {
|
|
5500
5514
|
return status >= 500 || status === 429;
|
|
5501
5515
|
}
|
|
5502
|
-
var inFlightRequests = /* @__PURE__ */ new Map();
|
|
5503
5516
|
async function getRequestKey(request) {
|
|
5504
5517
|
const body = request.body ? await request.clone().text() : "";
|
|
5505
5518
|
return `${request.method}:${request.url}:${body}`;
|
|
5506
5519
|
}
|
|
5507
5520
|
function createDeduplicationMiddleware(config = {}) {
|
|
5508
5521
|
const { enabled = true, methods = ["GET"] } = config;
|
|
5509
|
-
if (!enabled) {
|
|
5510
|
-
|
|
5511
|
-
async onRequest({ request }) {
|
|
5512
|
-
return request;
|
|
5513
|
-
}
|
|
5514
|
-
};
|
|
5515
|
-
}
|
|
5522
|
+
if (!enabled) return {};
|
|
5523
|
+
const inFlightRequests = /* @__PURE__ */ new Map();
|
|
5516
5524
|
return {
|
|
5517
|
-
async
|
|
5518
|
-
|
|
5519
|
-
|
|
5525
|
+
async onFetch({ request, next }) {
|
|
5526
|
+
const method = request.method.toUpperCase();
|
|
5527
|
+
if (!methods.includes(method)) {
|
|
5528
|
+
return next(request);
|
|
5520
5529
|
}
|
|
5521
5530
|
const key = await getRequestKey(request);
|
|
5522
5531
|
const existing = inFlightRequests.get(key);
|
|
5523
5532
|
if (existing) {
|
|
5524
|
-
const deduped = request.clone();
|
|
5525
|
-
deduped._dedupKey = key;
|
|
5526
|
-
return deduped;
|
|
5527
|
-
}
|
|
5528
|
-
;
|
|
5529
|
-
request._dedupKey = key;
|
|
5530
|
-
return request;
|
|
5531
|
-
},
|
|
5532
|
-
async onResponse({ request, response }) {
|
|
5533
|
-
const key = request._dedupKey;
|
|
5534
|
-
if (!key) return response;
|
|
5535
|
-
const existing = inFlightRequests.get(key);
|
|
5536
|
-
if (existing && inFlightRequests.get(key) !== void 0) {
|
|
5537
5533
|
const cachedResponse = await existing;
|
|
5538
5534
|
return cachedResponse.clone();
|
|
5539
5535
|
}
|
|
5540
|
-
const responsePromise =
|
|
5536
|
+
const responsePromise = next(request).then((response) => response.clone());
|
|
5541
5537
|
inFlightRequests.set(key, responsePromise);
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5538
|
+
try {
|
|
5539
|
+
const response = await responsePromise;
|
|
5540
|
+
return response.clone();
|
|
5541
|
+
} finally {
|
|
5542
|
+
inFlightRequests.delete(key);
|
|
5543
|
+
}
|
|
5546
5544
|
}
|
|
5547
5545
|
};
|
|
5548
5546
|
}
|
|
@@ -5708,7 +5706,9 @@ function createETagMiddleware(config) {
|
|
|
5708
5706
|
if (Date.now() - cached.timestamp > ttlMs) {
|
|
5709
5707
|
etagCache.delete(cacheKey);
|
|
5710
5708
|
} else {
|
|
5711
|
-
request
|
|
5709
|
+
return cloneRequestWithHeaders(request, (headers) => {
|
|
5710
|
+
headers.set("If-None-Match", cached.etag);
|
|
5711
|
+
});
|
|
5712
5712
|
}
|
|
5713
5713
|
}
|
|
5714
5714
|
return request;
|
|
@@ -6000,11 +6000,28 @@ async function inviteUser(config, input) {
|
|
|
6000
6000
|
body: input
|
|
6001
6001
|
});
|
|
6002
6002
|
}
|
|
6003
|
-
|
|
6004
|
-
|
|
6003
|
+
function normalizeOrgScopedTokenResponse(data) {
|
|
6004
|
+
const accessToken = data.accessToken ?? data.access_token;
|
|
6005
|
+
if (!accessToken) {
|
|
6006
|
+
throw new Error("Invalid org-scoped token response: missing access token");
|
|
6007
|
+
}
|
|
6008
|
+
return {
|
|
6009
|
+
token: accessToken,
|
|
6010
|
+
accessToken,
|
|
6011
|
+
expiresIn: data.expiresIn ?? data.expires_in,
|
|
6012
|
+
tokenType: data.tokenType ?? data.token_type,
|
|
6013
|
+
user: data.user
|
|
6014
|
+
};
|
|
6015
|
+
}
|
|
6016
|
+
async function getOrgScopedToken(config, orgId) {
|
|
6017
|
+
const data = await callApi(config, "/auth/switch-org", {
|
|
6005
6018
|
method: "POST",
|
|
6006
6019
|
body: { orgId }
|
|
6007
6020
|
});
|
|
6021
|
+
return normalizeOrgScopedTokenResponse(data);
|
|
6022
|
+
}
|
|
6023
|
+
async function switchOrg(config, orgId) {
|
|
6024
|
+
return getOrgScopedToken(config, orgId);
|
|
6008
6025
|
}
|
|
6009
6026
|
var device = {
|
|
6010
6027
|
/**
|
|
@@ -8003,168 +8020,208 @@ async function getBillingUsage(config, options) {
|
|
|
8003
8020
|
}
|
|
8004
8021
|
|
|
8005
8022
|
// src/storage.ts
|
|
8006
|
-
import { storageEndpoints } from "@sylphx/contract";
|
|
8007
|
-
init_constants();
|
|
8008
8023
|
init_errors();
|
|
8009
|
-
|
|
8010
|
-
|
|
8024
|
+
|
|
8025
|
+
// src/lib/retry.ts
|
|
8026
|
+
init_constants();
|
|
8027
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
8011
8028
|
maxRetries: 5,
|
|
8012
|
-
/** Base delay in milliseconds */
|
|
8013
8029
|
baseDelayMs: BASE_RETRY_DELAY_MS,
|
|
8014
|
-
|
|
8015
|
-
maxDelayMs: MAX_RETRY_DELAY_MS,
|
|
8016
|
-
/** Jitter type: 'full' for full jitter (AWS recommended) */
|
|
8017
|
-
jitter: "full"
|
|
8030
|
+
maxDelayMs: MAX_RETRY_DELAY_MS
|
|
8018
8031
|
};
|
|
8019
|
-
function calculateBackoffDelay(attempt) {
|
|
8020
|
-
const
|
|
8021
|
-
const
|
|
8022
|
-
|
|
8023
|
-
return Math.random() * cappedDelay;
|
|
8032
|
+
function calculateBackoffDelay(attempt, config = DEFAULT_RETRY_CONFIG) {
|
|
8033
|
+
const exp = config.baseDelayMs * 2 ** attempt;
|
|
8034
|
+
const capped = Math.min(exp, config.maxDelayMs);
|
|
8035
|
+
return Math.random() * capped;
|
|
8024
8036
|
}
|
|
8025
|
-
|
|
8037
|
+
function sleep(ms, signal) {
|
|
8026
8038
|
return new Promise((resolve, reject) => {
|
|
8027
8039
|
if (signal?.aborted) {
|
|
8028
|
-
reject(
|
|
8040
|
+
reject(toAbortError());
|
|
8029
8041
|
return;
|
|
8030
8042
|
}
|
|
8031
|
-
const
|
|
8043
|
+
const timer = setTimeout(resolve, ms);
|
|
8032
8044
|
signal?.addEventListener(
|
|
8033
8045
|
"abort",
|
|
8034
8046
|
() => {
|
|
8035
|
-
clearTimeout(
|
|
8036
|
-
reject(
|
|
8047
|
+
clearTimeout(timer);
|
|
8048
|
+
reject(toAbortError());
|
|
8037
8049
|
},
|
|
8038
8050
|
{ once: true }
|
|
8039
8051
|
);
|
|
8040
8052
|
});
|
|
8041
8053
|
}
|
|
8054
|
+
var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([408, 425, 429]);
|
|
8042
8055
|
function isRetryableError2(error) {
|
|
8043
|
-
if (error
|
|
8044
|
-
|
|
8045
|
-
}
|
|
8046
|
-
if (error instanceof TypeError) {
|
|
8047
|
-
return true;
|
|
8048
|
-
}
|
|
8056
|
+
if (isAbortError(error)) return false;
|
|
8057
|
+
if (error instanceof TypeError) return true;
|
|
8049
8058
|
if (error instanceof Error && "status" in error) {
|
|
8050
8059
|
const status = error.status;
|
|
8051
|
-
return status >= 500 || status
|
|
8060
|
+
return status >= 500 || RETRYABLE_STATUSES.has(status);
|
|
8052
8061
|
}
|
|
8053
8062
|
return false;
|
|
8054
8063
|
}
|
|
8055
|
-
|
|
8056
|
-
|
|
8057
|
-
if (
|
|
8058
|
-
|
|
8064
|
+
function isAbortError(error) {
|
|
8065
|
+
if (error instanceof DOMException && error.name === "AbortError") return true;
|
|
8066
|
+
if (error instanceof Error && error.name === "AbortError") return true;
|
|
8067
|
+
return false;
|
|
8068
|
+
}
|
|
8069
|
+
function toAbortError(message2 = "Aborted") {
|
|
8070
|
+
if (typeof DOMException !== "undefined") {
|
|
8071
|
+
return new DOMException(message2, "AbortError");
|
|
8059
8072
|
}
|
|
8060
|
-
|
|
8061
|
-
|
|
8062
|
-
|
|
8073
|
+
const err = new Error(message2);
|
|
8074
|
+
err.name = "AbortError";
|
|
8075
|
+
return err;
|
|
8076
|
+
}
|
|
8077
|
+
async function withRetry(fn, options = {}) {
|
|
8078
|
+
const cfg = {
|
|
8079
|
+
maxRetries: options.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries,
|
|
8080
|
+
baseDelayMs: options.baseDelayMs ?? DEFAULT_RETRY_CONFIG.baseDelayMs,
|
|
8081
|
+
maxDelayMs: options.maxDelayMs ?? DEFAULT_RETRY_CONFIG.maxDelayMs
|
|
8082
|
+
};
|
|
8083
|
+
let lastError;
|
|
8084
|
+
for (let attempt = 0; attempt <= cfg.maxRetries; attempt++) {
|
|
8085
|
+
if (options.signal?.aborted) throw toAbortError("Aborted");
|
|
8063
8086
|
try {
|
|
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
|
-
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
|
|
8102
|
-
|
|
8087
|
+
return await fn();
|
|
8088
|
+
} catch (err) {
|
|
8089
|
+
lastError = err;
|
|
8090
|
+
if (isAbortError(err)) throw err;
|
|
8091
|
+
if (attempt === cfg.maxRetries) break;
|
|
8092
|
+
if (!isRetryableError2(err)) throw err;
|
|
8093
|
+
const retryAfter = extractRetryAfter(err);
|
|
8094
|
+
const delay = retryAfter ?? calculateBackoffDelay(attempt, cfg);
|
|
8095
|
+
options.onRetry?.(attempt, delay, err);
|
|
8096
|
+
await sleep(delay, options.signal);
|
|
8097
|
+
}
|
|
8098
|
+
}
|
|
8099
|
+
throw lastError;
|
|
8100
|
+
}
|
|
8101
|
+
function extractRetryAfter(err) {
|
|
8102
|
+
if (err && typeof err === "object" && "retryAfter" in err) {
|
|
8103
|
+
const v = err.retryAfter;
|
|
8104
|
+
if (typeof v === "number" && v > 0) return v * 1e3;
|
|
8105
|
+
}
|
|
8106
|
+
return void 0;
|
|
8107
|
+
}
|
|
8108
|
+
function uuidv7() {
|
|
8109
|
+
const ms = BigInt(Date.now());
|
|
8110
|
+
const bytes = randomBytes(16);
|
|
8111
|
+
bytes[0] = Number(ms >> 40n & 0xffn);
|
|
8112
|
+
bytes[1] = Number(ms >> 32n & 0xffn);
|
|
8113
|
+
bytes[2] = Number(ms >> 24n & 0xffn);
|
|
8114
|
+
bytes[3] = Number(ms >> 16n & 0xffn);
|
|
8115
|
+
bytes[4] = Number(ms >> 8n & 0xffn);
|
|
8116
|
+
bytes[5] = Number(ms & 0xffn);
|
|
8117
|
+
bytes[6] = bytes[6] & 15 | 112;
|
|
8118
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
8119
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
8120
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
8121
|
+
}
|
|
8122
|
+
function randomBytes(n) {
|
|
8123
|
+
const out = new Uint8Array(n);
|
|
8124
|
+
const c = globalThis.crypto;
|
|
8125
|
+
if (c?.getRandomValues) {
|
|
8126
|
+
c.getRandomValues(out);
|
|
8127
|
+
return out;
|
|
8128
|
+
}
|
|
8129
|
+
for (let i = 0; i < n; i++) out[i] = Math.floor(Math.random() * 256);
|
|
8130
|
+
return out;
|
|
8131
|
+
}
|
|
8132
|
+
|
|
8133
|
+
// src/storage.ts
|
|
8134
|
+
var PATHS = {
|
|
8135
|
+
uploads: "/storage/uploads",
|
|
8136
|
+
upload: (id) => `/storage/uploads/${encodeURIComponent(String(id))}`,
|
|
8137
|
+
uploadComplete: (id) => `/storage/uploads/${encodeURIComponent(String(id))}:complete`,
|
|
8138
|
+
uploadPart: (id, n) => `/storage/uploads/${encodeURIComponent(String(id))}/parts/${n}`,
|
|
8139
|
+
files: "/storage/files",
|
|
8140
|
+
file: (id) => `/storage/files/${encodeURIComponent(String(id))}`,
|
|
8141
|
+
fileRestore: (id) => `/storage/files/${encodeURIComponent(String(id))}:restore`,
|
|
8142
|
+
fileSignedUrl: (id) => `/storage/files/${encodeURIComponent(String(id))}:signedUrl`,
|
|
8143
|
+
fileCopy: (id) => `/storage/files/${encodeURIComponent(String(id))}:copy`,
|
|
8144
|
+
versions: (id) => `/storage/files/${encodeURIComponent(String(id))}/versions`,
|
|
8145
|
+
versionRestore: (id, vid) => `/storage/files/${encodeURIComponent(String(id))}/versions/${encodeURIComponent(String(vid))}:restore`
|
|
8146
|
+
};
|
|
8147
|
+
var hasXhr = () => typeof globalThis.XMLHttpRequest !== "undefined";
|
|
8148
|
+
var hasLocalStorage = () => {
|
|
8149
|
+
try {
|
|
8150
|
+
const ls = globalThis.localStorage;
|
|
8151
|
+
return Boolean(ls && typeof ls.getItem === "function");
|
|
8152
|
+
} catch {
|
|
8153
|
+
return false;
|
|
8103
8154
|
}
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
|
|
8155
|
+
};
|
|
8156
|
+
async function computeSha256Hex(blob) {
|
|
8157
|
+
const subtle = globalThis.crypto?.subtle;
|
|
8158
|
+
if (!subtle?.digest) {
|
|
8159
|
+
throw new SylphxError("Web Crypto SHA-256 support is required for storage uploads", {
|
|
8160
|
+
code: "NOT_IMPLEMENTED"
|
|
8107
8161
|
});
|
|
8108
8162
|
}
|
|
8109
|
-
const
|
|
8110
|
-
|
|
8163
|
+
const buf = await blob.arrayBuffer();
|
|
8164
|
+
const digest2 = await subtle.digest("SHA-256", buf);
|
|
8165
|
+
return bytesToHex(new Uint8Array(digest2));
|
|
8111
8166
|
}
|
|
8112
|
-
|
|
8113
|
-
|
|
8114
|
-
let
|
|
8115
|
-
|
|
8167
|
+
function bytesToHex(b) {
|
|
8168
|
+
let s = "";
|
|
8169
|
+
for (let i = 0; i < b.length; i++) s += b[i].toString(16).padStart(2, "0");
|
|
8170
|
+
return s;
|
|
8171
|
+
}
|
|
8172
|
+
var RESUME_KEY_PREFIX = "sylphx_upload_";
|
|
8173
|
+
function resumeKey(uploadId) {
|
|
8174
|
+
return `${RESUME_KEY_PREFIX}${uploadId}`;
|
|
8175
|
+
}
|
|
8176
|
+
function persistResume(rec) {
|
|
8177
|
+
if (hasLocalStorage()) {
|
|
8116
8178
|
try {
|
|
8117
|
-
|
|
8118
|
-
} catch
|
|
8119
|
-
if (error instanceof DOMException && error.name === "AbortError") {
|
|
8120
|
-
throw error;
|
|
8121
|
-
}
|
|
8122
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
8123
|
-
if (isRetryableError2(error) && attempt < UPLOAD_RETRY_CONFIG.maxRetries) {
|
|
8124
|
-
const delay = calculateBackoffDelay(attempt);
|
|
8125
|
-
await sleep(delay, signal);
|
|
8126
|
-
continue;
|
|
8127
|
-
}
|
|
8128
|
-
throw lastError;
|
|
8179
|
+
localStorage.setItem(resumeKey(rec.uploadId), JSON.stringify(rec));
|
|
8180
|
+
} catch {
|
|
8129
8181
|
}
|
|
8130
8182
|
}
|
|
8131
|
-
throw lastError ?? new Error("Upload failed after retries");
|
|
8132
8183
|
}
|
|
8133
|
-
function
|
|
8134
|
-
|
|
8184
|
+
function clearResume(uploadId) {
|
|
8185
|
+
if (hasLocalStorage()) {
|
|
8186
|
+
try {
|
|
8187
|
+
localStorage.removeItem(resumeKey(uploadId));
|
|
8188
|
+
} catch {
|
|
8189
|
+
}
|
|
8190
|
+
}
|
|
8191
|
+
}
|
|
8192
|
+
function putBlob(url, body, headers, signal, onProgress) {
|
|
8193
|
+
if (hasXhr()) return putBlobXhr(url, body, headers, signal, onProgress);
|
|
8194
|
+
return putBlobFetch(url, body, headers, signal, onProgress);
|
|
8195
|
+
}
|
|
8196
|
+
function putBlobXhr(url, body, headers, signal, onProgress) {
|
|
8135
8197
|
return new Promise((resolve, reject) => {
|
|
8136
8198
|
const xhr = new XMLHttpRequest();
|
|
8137
8199
|
const handleAbort = () => {
|
|
8138
|
-
|
|
8139
|
-
|
|
8200
|
+
try {
|
|
8201
|
+
xhr.abort();
|
|
8202
|
+
} catch {
|
|
8203
|
+
}
|
|
8204
|
+
reject(toAbortError("Upload aborted"));
|
|
8140
8205
|
};
|
|
8141
8206
|
if (signal?.aborted) {
|
|
8142
|
-
reject(
|
|
8207
|
+
reject(toAbortError("Upload aborted"));
|
|
8143
8208
|
return;
|
|
8144
8209
|
}
|
|
8145
8210
|
signal?.addEventListener("abort", handleAbort, { once: true });
|
|
8146
|
-
|
|
8147
|
-
|
|
8148
|
-
onProgress(
|
|
8149
|
-
|
|
8150
|
-
|
|
8151
|
-
progress: Math.round(event.loaded / event.total * 100)
|
|
8152
|
-
});
|
|
8153
|
-
}
|
|
8154
|
-
});
|
|
8211
|
+
if (onProgress) {
|
|
8212
|
+
xhr.upload.addEventListener("progress", (e) => {
|
|
8213
|
+
if (e.lengthComputable) onProgress(e.loaded);
|
|
8214
|
+
});
|
|
8215
|
+
}
|
|
8155
8216
|
xhr.addEventListener("load", () => {
|
|
8156
8217
|
signal?.removeEventListener("abort", handleAbort);
|
|
8157
8218
|
if (xhr.status >= 200 && xhr.status < 300) {
|
|
8158
|
-
|
|
8159
|
-
|
|
8160
|
-
pathname: options?.path ? `${options.path}/${file.name}` : file.name,
|
|
8161
|
-
contentType: file.type,
|
|
8162
|
-
size: file.size
|
|
8163
|
-
});
|
|
8219
|
+
const etag = resolveXhrEtag(xhr);
|
|
8220
|
+
resolve({ etag: stripQuotes(etag), status: xhr.status });
|
|
8164
8221
|
} else {
|
|
8165
|
-
const
|
|
8166
|
-
|
|
8167
|
-
reject(
|
|
8222
|
+
const err = new Error(`PUT failed with status ${xhr.status}`);
|
|
8223
|
+
err.status = xhr.status;
|
|
8224
|
+
reject(err);
|
|
8168
8225
|
}
|
|
8169
8226
|
});
|
|
8170
8227
|
xhr.addEventListener("error", () => {
|
|
@@ -8173,93 +8230,291 @@ function executeUpload(file, uploadUrl, publicUrl, options) {
|
|
|
8173
8230
|
});
|
|
8174
8231
|
xhr.addEventListener("abort", () => {
|
|
8175
8232
|
signal?.removeEventListener("abort", handleAbort);
|
|
8176
|
-
reject(
|
|
8233
|
+
reject(toAbortError("Upload aborted"));
|
|
8177
8234
|
});
|
|
8178
|
-
xhr.open("PUT",
|
|
8179
|
-
|
|
8180
|
-
|
|
8181
|
-
|
|
8182
|
-
}
|
|
8183
|
-
|
|
8184
|
-
|
|
8185
|
-
|
|
8186
|
-
type: "avatar",
|
|
8187
|
-
userId
|
|
8235
|
+
xhr.open("PUT", url);
|
|
8236
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
8237
|
+
try {
|
|
8238
|
+
xhr.setRequestHeader(k, v);
|
|
8239
|
+
} catch {
|
|
8240
|
+
}
|
|
8241
|
+
}
|
|
8242
|
+
xhr.send(body);
|
|
8188
8243
|
});
|
|
8189
8244
|
}
|
|
8190
|
-
|
|
8191
|
-
const
|
|
8192
|
-
|
|
8193
|
-
|
|
8194
|
-
endpoint.path.replace(":id", encodeURIComponent(fileId)),
|
|
8195
|
-
{ method: endpoint.method }
|
|
8196
|
-
);
|
|
8197
|
-
}
|
|
8198
|
-
async function getFileUrl(config, fileId) {
|
|
8199
|
-
const data = await callApi(config, `/storage/files/${fileId}`, { method: "GET" });
|
|
8200
|
-
return data.url;
|
|
8245
|
+
function resolveXhrEtag(xhr) {
|
|
8246
|
+
const canonical = xhr.getResponseHeader("ETag");
|
|
8247
|
+
if (canonical !== null) return canonical;
|
|
8248
|
+
return xhr.getResponseHeader("etag") ?? "";
|
|
8201
8249
|
}
|
|
8202
|
-
async function
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8250
|
+
async function putBlobFetch(url, body, headers, signal, onProgress) {
|
|
8251
|
+
let stream = body;
|
|
8252
|
+
if (onProgress && typeof TransformStream !== "undefined" && typeof body.stream === "function") {
|
|
8253
|
+
let loaded = 0;
|
|
8254
|
+
const counter = new TransformStream({
|
|
8255
|
+
transform(chunk, controller) {
|
|
8256
|
+
loaded += chunk.byteLength;
|
|
8257
|
+
onProgress(loaded);
|
|
8258
|
+
controller.enqueue(chunk);
|
|
8259
|
+
}
|
|
8260
|
+
});
|
|
8261
|
+
stream = body.stream().pipeThrough(counter);
|
|
8262
|
+
}
|
|
8263
|
+
const res = await fetch(url, {
|
|
8264
|
+
method: "PUT",
|
|
8265
|
+
body: stream,
|
|
8266
|
+
headers,
|
|
8267
|
+
signal,
|
|
8268
|
+
// Required when streaming a request body in Chromium / undici.
|
|
8269
|
+
// Cast: TS lib lacks `duplex`.
|
|
8270
|
+
...{ duplex: "half" }
|
|
8206
8271
|
});
|
|
8272
|
+
if (!res.ok) {
|
|
8273
|
+
const err = new Error(`PUT failed with status ${res.status}`);
|
|
8274
|
+
err.status = res.status;
|
|
8275
|
+
throw err;
|
|
8276
|
+
}
|
|
8277
|
+
const etag = resolveFetchEtag(res.headers);
|
|
8278
|
+
if (onProgress) onProgress(body.size);
|
|
8279
|
+
return { etag: stripQuotes(etag), status: res.status };
|
|
8207
8280
|
}
|
|
8208
|
-
|
|
8209
|
-
const
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
{ method: "GET" }
|
|
8213
|
-
);
|
|
8214
|
-
return data.versions;
|
|
8281
|
+
function resolveFetchEtag(headers) {
|
|
8282
|
+
const lowerCase = headers.get("etag");
|
|
8283
|
+
if (lowerCase !== null) return lowerCase;
|
|
8284
|
+
return headers.get("ETag") ?? "";
|
|
8215
8285
|
}
|
|
8216
|
-
|
|
8217
|
-
|
|
8218
|
-
|
|
8219
|
-
`/storage/files/${encodeURIComponent(fileId)}/versions/${encodeURIComponent(versionId)}/restore`,
|
|
8220
|
-
{ method: "POST" }
|
|
8221
|
-
);
|
|
8222
|
-
return data.version;
|
|
8286
|
+
function stripQuotes(s) {
|
|
8287
|
+
if (s.length >= 2 && s.startsWith('"') && s.endsWith('"')) return s.slice(1, -1);
|
|
8288
|
+
return s;
|
|
8223
8289
|
}
|
|
8224
|
-
async function
|
|
8225
|
-
|
|
8226
|
-
|
|
8290
|
+
async function putWithRetry(url, body, headers, signal, onProgress) {
|
|
8291
|
+
let lastErr;
|
|
8292
|
+
const maxRetries = 5;
|
|
8293
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
8294
|
+
if (signal?.aborted) throw toAbortError();
|
|
8295
|
+
try {
|
|
8296
|
+
return await putBlob(url, body, headers, signal, onProgress);
|
|
8297
|
+
} catch (err) {
|
|
8298
|
+
if (isAbortError(err)) throw err;
|
|
8299
|
+
lastErr = err;
|
|
8300
|
+
if (attempt === maxRetries || !isRetryableError2(err)) throw err;
|
|
8301
|
+
await sleep(calculateBackoffDelay(attempt), signal);
|
|
8302
|
+
}
|
|
8303
|
+
}
|
|
8304
|
+
throw lastErr;
|
|
8305
|
+
}
|
|
8306
|
+
async function uploadsCreate(config, blob, options) {
|
|
8307
|
+
const signal = options.signal;
|
|
8308
|
+
if (signal?.aborted) throw toAbortError();
|
|
8309
|
+
const filename = options.filename ?? (isFile(blob) ? blob.name : void 0) ?? "upload.bin";
|
|
8310
|
+
const contentType = options.contentType ?? (blob.type && blob.type.length > 0 ? blob.type : "application/octet-stream");
|
|
8311
|
+
const size = blob.size;
|
|
8312
|
+
const checksumSha256 = options.checksumSha256 ?? await computeSha256Hex(blob);
|
|
8313
|
+
const idempotencyKey = options.idempotencyKey ?? uuidv7();
|
|
8314
|
+
const createBody = {
|
|
8315
|
+
filename,
|
|
8316
|
+
contentType,
|
|
8317
|
+
size,
|
|
8318
|
+
folder: options.folder,
|
|
8319
|
+
visibility: options.visibility,
|
|
8320
|
+
metadata: options.metadata,
|
|
8321
|
+
checksumSha256,
|
|
8322
|
+
ifNoneMatch: options.ifNoneMatch
|
|
8323
|
+
};
|
|
8324
|
+
const session = await callApi(config, PATHS.uploads, {
|
|
8325
|
+
method: "POST",
|
|
8326
|
+
body: createBody,
|
|
8327
|
+
idempotencyKey,
|
|
8328
|
+
signal
|
|
8227
8329
|
});
|
|
8330
|
+
const uploadId = session.uploadId;
|
|
8331
|
+
const onAborted = async () => {
|
|
8332
|
+
try {
|
|
8333
|
+
await callApi(config, PATHS.upload(uploadId), { method: "DELETE" });
|
|
8334
|
+
} catch {
|
|
8335
|
+
}
|
|
8336
|
+
clearResume(String(uploadId));
|
|
8337
|
+
};
|
|
8338
|
+
try {
|
|
8339
|
+
if (session.method === "PUT") {
|
|
8340
|
+
return await runSinglePart(config, blob, session, options, idempotencyKey);
|
|
8341
|
+
}
|
|
8342
|
+
return await runMultipart(config, blob, session, options, idempotencyKey);
|
|
8343
|
+
} catch (err) {
|
|
8344
|
+
if (isAbortError(err)) {
|
|
8345
|
+
await onAborted();
|
|
8346
|
+
}
|
|
8347
|
+
throw err;
|
|
8348
|
+
}
|
|
8228
8349
|
}
|
|
8229
|
-
|
|
8230
|
-
|
|
8231
|
-
config,
|
|
8232
|
-
`/storage/files/${encodeURIComponent(fileId)}/restore`,
|
|
8233
|
-
{ method: "POST" }
|
|
8234
|
-
);
|
|
8235
|
-
return data.file;
|
|
8350
|
+
function isFile(b) {
|
|
8351
|
+
return typeof b.name === "string";
|
|
8236
8352
|
}
|
|
8237
|
-
async function
|
|
8238
|
-
const
|
|
8353
|
+
async function runSinglePart(config, blob, session, options, idempotencyKey) {
|
|
8354
|
+
const { onProgress, signal } = options;
|
|
8355
|
+
const total = blob.size;
|
|
8356
|
+
const trackProgress = onProgress ? (loaded) => {
|
|
8357
|
+
onProgress({ loaded, total, partsCompleted: 0, partsTotal: 1 });
|
|
8358
|
+
} : void 0;
|
|
8359
|
+
const put = await putWithRetry(session.url, blob, session.headers, signal, trackProgress);
|
|
8360
|
+
if (onProgress) onProgress({ loaded: total, total, partsCompleted: 1, partsTotal: 1 });
|
|
8361
|
+
const completion = await callApi(
|
|
8239
8362
|
config,
|
|
8240
|
-
|
|
8363
|
+
PATHS.uploadComplete(session.uploadId),
|
|
8241
8364
|
{
|
|
8242
8365
|
method: "POST",
|
|
8243
|
-
body: {
|
|
8366
|
+
body: { parts: [{ partNumber: 1, etag: put.etag }] },
|
|
8367
|
+
idempotencyKey: `${idempotencyKey}:complete`,
|
|
8368
|
+
signal
|
|
8244
8369
|
}
|
|
8245
8370
|
);
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
8249
|
-
|
|
8371
|
+
clearResume(String(session.uploadId));
|
|
8372
|
+
return await fetchFile(config, completion.fileId);
|
|
8373
|
+
}
|
|
8374
|
+
async function runMultipart(config, blob, session, options, idempotencyKey) {
|
|
8375
|
+
const { onProgress, signal } = options;
|
|
8376
|
+
const partSize = session.partSize;
|
|
8377
|
+
const total = blob.size;
|
|
8378
|
+
const partsTotal = session.partCount;
|
|
8379
|
+
const completedParts = [];
|
|
8380
|
+
const partLoaded = /* @__PURE__ */ new Map();
|
|
8381
|
+
const reportProgress = () => {
|
|
8382
|
+
if (!onProgress) return;
|
|
8383
|
+
let loaded = 0;
|
|
8384
|
+
for (const v of partLoaded.values()) loaded += v;
|
|
8385
|
+
onProgress({ loaded, total, partsCompleted: completedParts.length, partsTotal });
|
|
8386
|
+
};
|
|
8387
|
+
for (const part of session.parts) {
|
|
8388
|
+
if (signal?.aborted) throw toAbortError();
|
|
8389
|
+
const offset = (part.partNumber - 1) * partSize;
|
|
8390
|
+
const end = Math.min(offset + partSize, total);
|
|
8391
|
+
const slice = blob.slice(offset, end);
|
|
8392
|
+
const trackProgress = onProgress ? (loaded) => {
|
|
8393
|
+
partLoaded.set(part.partNumber, loaded);
|
|
8394
|
+
reportProgress();
|
|
8395
|
+
} : void 0;
|
|
8396
|
+
const result = await putWithRetry(part.url, slice, {}, signal, trackProgress);
|
|
8397
|
+
completedParts.push({ partNumber: part.partNumber, etag: result.etag });
|
|
8398
|
+
partLoaded.set(part.partNumber, slice.size);
|
|
8399
|
+
reportProgress();
|
|
8400
|
+
persistResume({
|
|
8401
|
+
uploadId: String(session.uploadId),
|
|
8402
|
+
completedParts: [...completedParts],
|
|
8403
|
+
updatedAt: Date.now()
|
|
8250
8404
|
});
|
|
8251
8405
|
}
|
|
8252
|
-
|
|
8406
|
+
const completion = await callApi(
|
|
8407
|
+
config,
|
|
8408
|
+
PATHS.uploadComplete(session.uploadId),
|
|
8409
|
+
{
|
|
8410
|
+
method: "POST",
|
|
8411
|
+
body: { parts: completedParts },
|
|
8412
|
+
idempotencyKey: `${idempotencyKey}:complete`,
|
|
8413
|
+
signal
|
|
8414
|
+
}
|
|
8415
|
+
);
|
|
8416
|
+
clearResume(String(session.uploadId));
|
|
8417
|
+
return await fetchFile(config, completion.fileId);
|
|
8253
8418
|
}
|
|
8254
|
-
async function
|
|
8255
|
-
return callApi(config, "
|
|
8256
|
-
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8419
|
+
async function fetchFile(config, fileId) {
|
|
8420
|
+
return callApi(config, PATHS.file(fileId), { method: "GET" });
|
|
8421
|
+
}
|
|
8422
|
+
async function uploadsAbort(config, uploadId) {
|
|
8423
|
+
await withRetry(() => callApi(config, PATHS.upload(uploadId), { method: "DELETE" }), {
|
|
8424
|
+
maxRetries: 3
|
|
8425
|
+
});
|
|
8426
|
+
clearResume(String(uploadId));
|
|
8427
|
+
}
|
|
8428
|
+
function filesListPage(config, options, cursor) {
|
|
8429
|
+
const query = {
|
|
8430
|
+
folder: options.folder,
|
|
8431
|
+
cursor: cursor ?? options.cursor,
|
|
8432
|
+
limit: options.limit,
|
|
8433
|
+
includeDeleted: options.includeDeleted
|
|
8434
|
+
};
|
|
8435
|
+
return callApi(config, PATHS.files, {
|
|
8436
|
+
method: "GET",
|
|
8437
|
+
query: {
|
|
8438
|
+
folder: query.folder,
|
|
8439
|
+
cursor: query.cursor,
|
|
8440
|
+
limit: query.limit,
|
|
8441
|
+
includeDeleted: query.includeDeleted
|
|
8260
8442
|
}
|
|
8261
8443
|
});
|
|
8262
8444
|
}
|
|
8445
|
+
function filesList(config, options = {}) {
|
|
8446
|
+
const fetchPage = (cursor) => filesListPage(config, options, cursor);
|
|
8447
|
+
const iter = {
|
|
8448
|
+
[Symbol.asyncIterator]: async function* () {
|
|
8449
|
+
let cursor = options.cursor;
|
|
8450
|
+
do {
|
|
8451
|
+
const page2 = await fetchPage(cursor ?? void 0);
|
|
8452
|
+
for (const f of page2.files) yield f;
|
|
8453
|
+
cursor = page2.nextCursor;
|
|
8454
|
+
} while (cursor);
|
|
8455
|
+
}
|
|
8456
|
+
};
|
|
8457
|
+
return Object.assign(iter, { fetchPage });
|
|
8458
|
+
}
|
|
8459
|
+
async function filesGet(config, fileId) {
|
|
8460
|
+
return callApi(config, PATHS.file(fileId), { method: "GET" });
|
|
8461
|
+
}
|
|
8462
|
+
async function filesDelete(config, fileId) {
|
|
8463
|
+
return callApi(config, PATHS.file(fileId), { method: "DELETE" });
|
|
8464
|
+
}
|
|
8465
|
+
async function filesRestore(config, fileId) {
|
|
8466
|
+
return callApi(config, PATHS.fileRestore(fileId), { method: "POST" });
|
|
8467
|
+
}
|
|
8468
|
+
async function filesSignedUrl(config, fileId, options = {}) {
|
|
8469
|
+
const body = {
|
|
8470
|
+
expiresIn: options.expiresIn,
|
|
8471
|
+
disposition: options.disposition,
|
|
8472
|
+
userId: options.userId
|
|
8473
|
+
};
|
|
8474
|
+
return callApi(config, PATHS.fileSignedUrl(fileId), {
|
|
8475
|
+
method: "POST",
|
|
8476
|
+
body
|
|
8477
|
+
});
|
|
8478
|
+
}
|
|
8479
|
+
async function filesCopy(config, fileId, options) {
|
|
8480
|
+
const body = {
|
|
8481
|
+
folder: options.folder,
|
|
8482
|
+
filename: options.filename,
|
|
8483
|
+
visibility: options.visibility,
|
|
8484
|
+
metadata: options.metadata
|
|
8485
|
+
};
|
|
8486
|
+
return callApi(config, PATHS.fileCopy(fileId), {
|
|
8487
|
+
method: "POST",
|
|
8488
|
+
body
|
|
8489
|
+
});
|
|
8490
|
+
}
|
|
8491
|
+
async function filesVersionsList(config, fileId) {
|
|
8492
|
+
const data = await callApi(config, PATHS.versions(fileId), {
|
|
8493
|
+
method: "GET"
|
|
8494
|
+
});
|
|
8495
|
+
return data.versions;
|
|
8496
|
+
}
|
|
8497
|
+
async function filesVersionsRestore(config, fileId, versionId) {
|
|
8498
|
+
return callApi(config, PATHS.versionRestore(fileId, versionId), { method: "POST" });
|
|
8499
|
+
}
|
|
8500
|
+
var storage = {
|
|
8501
|
+
uploads: {
|
|
8502
|
+
create: uploadsCreate,
|
|
8503
|
+
abort: uploadsAbort
|
|
8504
|
+
},
|
|
8505
|
+
files: {
|
|
8506
|
+
list: filesList,
|
|
8507
|
+
get: filesGet,
|
|
8508
|
+
delete: filesDelete,
|
|
8509
|
+
restore: filesRestore,
|
|
8510
|
+
signedUrl: filesSignedUrl,
|
|
8511
|
+
copy: filesCopy,
|
|
8512
|
+
versions: {
|
|
8513
|
+
list: filesVersionsList,
|
|
8514
|
+
restore: filesVersionsRestore
|
|
8515
|
+
}
|
|
8516
|
+
}
|
|
8517
|
+
};
|
|
8263
8518
|
|
|
8264
8519
|
// src/lib/notifications/service-worker.ts
|
|
8265
8520
|
function initPushServiceWorker(config = {}) {
|
|
@@ -8648,7 +8903,7 @@ function createTasksHandler(taskDefs, options = {}) {
|
|
|
8648
8903
|
{ status: 400 }
|
|
8649
8904
|
);
|
|
8650
8905
|
}
|
|
8651
|
-
const signingSecret = options.signingSecret ?? process.env.SYLPHX_SIGNING_SECRET ??
|
|
8906
|
+
const signingSecret = options.signingSecret ?? process.env.SYLPHX_SIGNING_SECRET ?? "";
|
|
8652
8907
|
if (signingSecret) {
|
|
8653
8908
|
const signature = req.headers.get("x-sylphx-signature") ?? "";
|
|
8654
8909
|
if (!signature) {
|
|
@@ -10706,7 +10961,6 @@ export {
|
|
|
10706
10961
|
deleteCron,
|
|
10707
10962
|
deleteDocument,
|
|
10708
10963
|
deleteEnvVar,
|
|
10709
|
-
deleteFile,
|
|
10710
10964
|
deleteOrganization,
|
|
10711
10965
|
deletePasskey,
|
|
10712
10966
|
deletePermission,
|
|
@@ -10718,7 +10972,6 @@ export {
|
|
|
10718
10972
|
disableDebug,
|
|
10719
10973
|
disableTwoFactor,
|
|
10720
10974
|
disconnectOAuthProvider,
|
|
10721
|
-
downloadFileVersion,
|
|
10722
10975
|
dpop,
|
|
10723
10976
|
embed,
|
|
10724
10977
|
enableDebug,
|
|
@@ -10751,14 +11004,13 @@ export {
|
|
|
10751
11004
|
getErrorCode,
|
|
10752
11005
|
getErrorMessage,
|
|
10753
11006
|
getFacets,
|
|
10754
|
-
getFileInfo,
|
|
10755
|
-
getFileUrl,
|
|
10756
11007
|
getFlagPayload,
|
|
10757
11008
|
getFlags,
|
|
10758
11009
|
getLeaderboard,
|
|
10759
11010
|
getMemberPermissions,
|
|
10760
11011
|
getMyReferralCode,
|
|
10761
11012
|
getOidcDiscoveryDocument,
|
|
11013
|
+
getOrgScopedToken,
|
|
10762
11014
|
getOrganization,
|
|
10763
11015
|
getOrganizationInvitations,
|
|
10764
11016
|
getOrganizationMembers,
|
|
@@ -10779,7 +11031,6 @@ export {
|
|
|
10779
11031
|
getSecrets,
|
|
10780
11032
|
getSecurityScore,
|
|
10781
11033
|
getSession,
|
|
10782
|
-
getSignedUrl,
|
|
10783
11034
|
getStreak,
|
|
10784
11035
|
getSubscription,
|
|
10785
11036
|
getTask,
|
|
@@ -10837,7 +11088,6 @@ export {
|
|
|
10837
11088
|
leaveOrganization,
|
|
10838
11089
|
linkAnonymousConsents,
|
|
10839
11090
|
listEnvVars,
|
|
10840
|
-
listFileVersions,
|
|
10841
11091
|
listOAuthProviders,
|
|
10842
11092
|
listPasskeys,
|
|
10843
11093
|
listPermissions,
|
|
@@ -10885,8 +11135,6 @@ export {
|
|
|
10885
11135
|
resetPassword,
|
|
10886
11136
|
resetPlatformCookieCache,
|
|
10887
11137
|
resetPlatformJwksCache,
|
|
10888
|
-
restoreFile,
|
|
10889
|
-
restoreFileVersion,
|
|
10890
11138
|
resumeCron,
|
|
10891
11139
|
revokeAllTokens,
|
|
10892
11140
|
revokeOrganizationInvitation,
|
|
@@ -10908,8 +11156,8 @@ export {
|
|
|
10908
11156
|
signIn,
|
|
10909
11157
|
signOut,
|
|
10910
11158
|
signUp,
|
|
10911
|
-
softDeleteFile,
|
|
10912
11159
|
startPasskeyRegistration,
|
|
11160
|
+
storage,
|
|
10913
11161
|
streamToString,
|
|
10914
11162
|
submitScore,
|
|
10915
11163
|
suspendUser,
|
|
@@ -10930,8 +11178,6 @@ export {
|
|
|
10930
11178
|
updateUserMetadata,
|
|
10931
11179
|
updateUserProfile,
|
|
10932
11180
|
updateWebhookConfig,
|
|
10933
|
-
uploadAvatar,
|
|
10934
|
-
uploadFile,
|
|
10935
11181
|
upsertDocument,
|
|
10936
11182
|
user,
|
|
10937
11183
|
userInfo,
|