lazypock 0.16.1 → 0.17.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/dist/index.cjs +104 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -4
- package/dist/index.d.ts +66 -4
- package/dist/index.global.js +103 -8
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +103 -8
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/auth.ts +37 -3
- package/src/files.ts +150 -2
- package/src/index.ts +1 -0
- package/src/lazypock.ts +3 -0
package/dist/index.cjs
CHANGED
|
@@ -39,6 +39,7 @@ __export(index_exports, {
|
|
|
39
39
|
getFileUrl: () => getFileUrl,
|
|
40
40
|
getScaleUrl: () => getScaleUrl,
|
|
41
41
|
getThumbUrl: () => getThumbUrl,
|
|
42
|
+
getVariantUrl: () => getVariantUrl,
|
|
42
43
|
schemaFieldType: () => schemaFieldType,
|
|
43
44
|
wsUrlFromBaseUrl: () => wsUrlFromBaseUrl
|
|
44
45
|
});
|
|
@@ -385,20 +386,24 @@ var AuthStore = class {
|
|
|
385
386
|
}
|
|
386
387
|
/**
|
|
387
388
|
* Set the auth collection name (used internally by auto-refresh).
|
|
389
|
+
*
|
|
390
|
+
* Persisted, so it survives a reload together with the token and model.
|
|
388
391
|
* @param name The collection name, or null for superuser tokens.
|
|
389
392
|
*/
|
|
390
393
|
setCollectionName(name) {
|
|
391
394
|
this._collectionName = name;
|
|
395
|
+
void this.persistCollectionName();
|
|
392
396
|
}
|
|
393
397
|
/**
|
|
394
398
|
* Load persisted auth state from storage.
|
|
395
399
|
* Should be called once at application startup.
|
|
396
400
|
*/
|
|
397
401
|
async init() {
|
|
398
|
-
const [token, model, expiresAt] = await Promise.all([
|
|
402
|
+
const [token, model, expiresAt, collectionName] = await Promise.all([
|
|
399
403
|
this.storage.get("auth_token"),
|
|
400
404
|
this.storage.get("auth_model"),
|
|
401
|
-
this.storage.get("auth_expires_at")
|
|
405
|
+
this.storage.get("auth_expires_at"),
|
|
406
|
+
this.storage.get("auth_collection")
|
|
402
407
|
]);
|
|
403
408
|
if (token) this._token = token;
|
|
404
409
|
if (expiresAt) this._tokenExpiresAt = parseInt(expiresAt, 10) || null;
|
|
@@ -408,6 +413,11 @@ var AuthStore = class {
|
|
|
408
413
|
} catch {
|
|
409
414
|
}
|
|
410
415
|
}
|
|
416
|
+
if (collectionName) {
|
|
417
|
+
this._collectionName = collectionName;
|
|
418
|
+
} else if (this._model && typeof this._model.collectionName === "string") {
|
|
419
|
+
this._collectionName = this._model.collectionName;
|
|
420
|
+
}
|
|
411
421
|
}
|
|
412
422
|
/**
|
|
413
423
|
* Update the current auth token and model, persist to storage, and notify listeners.
|
|
@@ -418,15 +428,19 @@ var AuthStore = class {
|
|
|
418
428
|
this._token = token;
|
|
419
429
|
this._model = model;
|
|
420
430
|
this._tokenExpiresAt = Date.now() + TOKEN_TTL_MS;
|
|
431
|
+
if (!this._collectionName && model && typeof model.collectionName === "string") {
|
|
432
|
+
this._collectionName = model.collectionName;
|
|
433
|
+
}
|
|
421
434
|
void Promise.all([
|
|
422
435
|
this.storage.set("auth_token", token),
|
|
423
436
|
this.storage.set("auth_expires_at", String(this._tokenExpiresAt)),
|
|
424
|
-
model ? this.storage.set("auth_model", JSON.stringify(model)) : this.storage.remove("auth_model")
|
|
437
|
+
model ? this.storage.set("auth_model", JSON.stringify(model)) : this.storage.remove("auth_model"),
|
|
438
|
+
this.persistCollectionName()
|
|
425
439
|
]);
|
|
426
440
|
this.notify();
|
|
427
441
|
}
|
|
428
442
|
/**
|
|
429
|
-
* Clear all auth state (token, model, expiry) and notify listeners.
|
|
443
|
+
* Clear all auth state (token, model, expiry, collection) and notify listeners.
|
|
430
444
|
*/
|
|
431
445
|
clear() {
|
|
432
446
|
this._token = "";
|
|
@@ -436,10 +450,14 @@ var AuthStore = class {
|
|
|
436
450
|
void Promise.all([
|
|
437
451
|
this.storage.remove("auth_token"),
|
|
438
452
|
this.storage.remove("auth_expires_at"),
|
|
439
|
-
this.storage.remove("auth_model")
|
|
453
|
+
this.storage.remove("auth_model"),
|
|
454
|
+
this.storage.remove("auth_collection")
|
|
440
455
|
]);
|
|
441
456
|
this.notify();
|
|
442
457
|
}
|
|
458
|
+
persistCollectionName() {
|
|
459
|
+
return this._collectionName ? this.storage.set("auth_collection", this._collectionName) : this.storage.remove("auth_collection");
|
|
460
|
+
}
|
|
443
461
|
/**
|
|
444
462
|
* Register a listener for auth state changes.
|
|
445
463
|
* @param fn Callback invoked with (model, token) on every change.
|
|
@@ -1229,7 +1247,7 @@ var CollectionService = class _CollectionService {
|
|
|
1229
1247
|
requestKey,
|
|
1230
1248
|
autoCancel,
|
|
1231
1249
|
cancelKey,
|
|
1232
|
-
fetch,
|
|
1250
|
+
fetch: fetch2,
|
|
1233
1251
|
headers,
|
|
1234
1252
|
signal,
|
|
1235
1253
|
singleFlight,
|
|
@@ -1273,7 +1291,7 @@ var CollectionService = class _CollectionService {
|
|
|
1273
1291
|
requestKey,
|
|
1274
1292
|
autoCancel,
|
|
1275
1293
|
cancelKey,
|
|
1276
|
-
fetch,
|
|
1294
|
+
fetch: fetch2,
|
|
1277
1295
|
headers,
|
|
1278
1296
|
signal,
|
|
1279
1297
|
singleFlight,
|
|
@@ -1999,9 +2017,28 @@ function getThumbUrl(baseUrl, fileId, size) {
|
|
|
1999
2017
|
function getScaleUrl(baseUrl, fileId, size) {
|
|
2000
2018
|
return baseUrl.replace(/\/+$/, "") + "/files/" + encodeURIComponent(fileId) + "/scale/" + encodeURIComponent(size);
|
|
2001
2019
|
}
|
|
2020
|
+
function getVariantUrl(baseUrl, fileId, preset) {
|
|
2021
|
+
return getScaleUrl(baseUrl, fileId, preset);
|
|
2022
|
+
}
|
|
2023
|
+
function parseUploadTarget(rawUrl, allowedHosts) {
|
|
2024
|
+
let parsed;
|
|
2025
|
+
try {
|
|
2026
|
+
parsed = new URL(rawUrl);
|
|
2027
|
+
} catch {
|
|
2028
|
+
throw new Error("Direct upload: the server returned an invalid upload URL");
|
|
2029
|
+
}
|
|
2030
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
2031
|
+
throw new Error(`Direct upload: unsupported protocol ${parsed.protocol}`);
|
|
2032
|
+
}
|
|
2033
|
+
if (allowedHosts && allowedHosts.length > 0 && !allowedHosts.includes(parsed.host)) {
|
|
2034
|
+
throw new Error(`Direct upload: host ${parsed.host} is not in the allowlist`);
|
|
2035
|
+
}
|
|
2036
|
+
return parsed;
|
|
2037
|
+
}
|
|
2002
2038
|
var FilesService = class {
|
|
2003
|
-
constructor(http) {
|
|
2039
|
+
constructor(http, allowedUploadHosts) {
|
|
2004
2040
|
this.http = http;
|
|
2041
|
+
this.allowedUploadHosts = allowedUploadHosts;
|
|
2005
2042
|
}
|
|
2006
2043
|
/**
|
|
2007
2044
|
* Upload a file or blob.
|
|
@@ -2022,6 +2059,8 @@ var FilesService = class {
|
|
|
2022
2059
|
formData.append("collection_name", meta.collectionName);
|
|
2023
2060
|
if (meta?.recordId) formData.append("record_id", meta.recordId);
|
|
2024
2061
|
if (meta?.fieldName) formData.append("field_name", meta.fieldName);
|
|
2062
|
+
if (meta?.origin) formData.append("origin", meta.origin);
|
|
2063
|
+
if (meta?.variants?.length) formData.append("variants", meta.variants.join(","));
|
|
2025
2064
|
const data = await this.http.request(
|
|
2026
2065
|
"POST",
|
|
2027
2066
|
"/files",
|
|
@@ -2030,6 +2069,62 @@ var FilesService = class {
|
|
|
2030
2069
|
);
|
|
2031
2070
|
return data;
|
|
2032
2071
|
}
|
|
2072
|
+
/**
|
|
2073
|
+
* Direct upload (Mode B): the browser PUTs straight to S3/R2.
|
|
2074
|
+
*
|
|
2075
|
+
* Presigns, uploads, then asks the server to verify the object. Falls back to
|
|
2076
|
+
* `upload()` when the server has no direct-upload support configured.
|
|
2077
|
+
*/
|
|
2078
|
+
async uploadDirect(file, opts) {
|
|
2079
|
+
const filename = opts?.filename || (file instanceof File ? file.name : "file");
|
|
2080
|
+
const mime = file.type || "application/octet-stream";
|
|
2081
|
+
const presigned = await this.presign({
|
|
2082
|
+
filename,
|
|
2083
|
+
size: file.size,
|
|
2084
|
+
mime,
|
|
2085
|
+
collectionName: opts?.collectionName,
|
|
2086
|
+
fieldName: opts?.fieldName,
|
|
2087
|
+
recordId: opts?.recordId
|
|
2088
|
+
});
|
|
2089
|
+
if (!presigned) return this.upload(file, filename, { signal: opts?.signal }, { collectionName: opts?.collectionName, fieldName: opts?.fieldName, recordId: opts?.recordId });
|
|
2090
|
+
const target = parseUploadTarget(presigned.url, this.allowedUploadHosts);
|
|
2091
|
+
const put = await fetch(target.href, {
|
|
2092
|
+
method: presigned.method || "PUT",
|
|
2093
|
+
headers: presigned.headers,
|
|
2094
|
+
body: file,
|
|
2095
|
+
signal: opts?.signal
|
|
2096
|
+
});
|
|
2097
|
+
if (!put.ok) throw new Error(`Direct upload failed: HTTP ${put.status}`);
|
|
2098
|
+
return this.complete(presigned.id, { variant: opts?.variant, signal: opts?.signal });
|
|
2099
|
+
}
|
|
2100
|
+
/** Step 1 of a direct upload: reserve an object and get a presigned PUT. */
|
|
2101
|
+
async presign(params) {
|
|
2102
|
+
const data = await this.http.request(
|
|
2103
|
+
"POST",
|
|
2104
|
+
"/files/presign",
|
|
2105
|
+
{
|
|
2106
|
+
filename: params.filename,
|
|
2107
|
+
size: params.size,
|
|
2108
|
+
mime: params.mime,
|
|
2109
|
+
collection_name: params.collectionName,
|
|
2110
|
+
field_name: params.fieldName,
|
|
2111
|
+
record_id: params.recordId
|
|
2112
|
+
}
|
|
2113
|
+
);
|
|
2114
|
+
if (!data || typeof data.url !== "string") return null;
|
|
2115
|
+
return data;
|
|
2116
|
+
}
|
|
2117
|
+
/** Step 2 of a direct upload: ask the server to verify the object. */
|
|
2118
|
+
async complete(fileId, opts) {
|
|
2119
|
+
const query = opts?.variant ? `?variants=${encodeURIComponent(opts.variant)}` : "";
|
|
2120
|
+
const data = await this.http.request(
|
|
2121
|
+
"POST",
|
|
2122
|
+
`/files/${encodeURIComponent(fileId)}/complete${query}`,
|
|
2123
|
+
{},
|
|
2124
|
+
{ signal: opts?.signal }
|
|
2125
|
+
);
|
|
2126
|
+
return data;
|
|
2127
|
+
}
|
|
2033
2128
|
/**
|
|
2034
2129
|
* List uploaded files (newest first), with optional filters.
|
|
2035
2130
|
*
|
|
@@ -2842,6 +2937,7 @@ function createClient(options) {
|
|
|
2842
2937
|
getFileUrl,
|
|
2843
2938
|
getScaleUrl,
|
|
2844
2939
|
getThumbUrl,
|
|
2940
|
+
getVariantUrl,
|
|
2845
2941
|
schemaFieldType,
|
|
2846
2942
|
wsUrlFromBaseUrl
|
|
2847
2943
|
});
|