lazypock 0.16.0 → 0.17.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.cjs +103 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +62 -14
- package/dist/index.d.ts +62 -14
- package/dist/index.global.js +102 -31
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +102 -31
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/collection.ts +17 -18
- package/src/files.ts +150 -2
- package/src/index.ts +1 -0
- package/src/lazypock.ts +3 -0
- package/src/oauth2.ts +43 -51
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
|
});
|
|
@@ -641,9 +642,6 @@ function isSafeBrowserUrl(url) {
|
|
|
641
642
|
return false;
|
|
642
643
|
}
|
|
643
644
|
}
|
|
644
|
-
function isRecordAuth(value) {
|
|
645
|
-
return !!value && typeof value === "object" && typeof value.token === "string" && !!value.record && typeof value.record === "object";
|
|
646
|
-
}
|
|
647
645
|
function authorizeWithOAuth2Popup(opts) {
|
|
648
646
|
return new Promise((resolve, reject) => {
|
|
649
647
|
if (typeof window === "undefined") {
|
|
@@ -686,18 +684,20 @@ function authorizeWithOAuth2Popup(opts) {
|
|
|
686
684
|
const data = event.data;
|
|
687
685
|
if (!data || typeof data !== "object") return;
|
|
688
686
|
if (data.type === "lazypock:oauth2") {
|
|
689
|
-
|
|
687
|
+
const code = data.code;
|
|
688
|
+
if (typeof code === "string" && code) {
|
|
690
689
|
if (popup) {
|
|
691
690
|
try {
|
|
692
691
|
popup.close();
|
|
693
692
|
} catch {
|
|
694
693
|
}
|
|
695
694
|
}
|
|
696
|
-
|
|
695
|
+
const state = typeof data.state === "string" ? data.state : void 0;
|
|
696
|
+
finish(null, { code, state });
|
|
697
697
|
} else {
|
|
698
698
|
finish(
|
|
699
699
|
new ApiError(
|
|
700
|
-
"OAuth2 callback returned
|
|
700
|
+
"OAuth2 callback returned no authorization code.",
|
|
701
701
|
data,
|
|
702
702
|
400,
|
|
703
703
|
false,
|
|
@@ -708,16 +708,9 @@ function authorizeWithOAuth2Popup(opts) {
|
|
|
708
708
|
return;
|
|
709
709
|
}
|
|
710
710
|
if (data.type === "lazypock:oauth2:error") {
|
|
711
|
-
const
|
|
712
|
-
const message = typeof result.message === "string" && result.message ? result.message : "OAuth2 authorization failed.";
|
|
711
|
+
const message = typeof data.message === "string" && data.message ? data.message : "OAuth2 authorization failed.";
|
|
713
712
|
finish(
|
|
714
|
-
new ApiError(
|
|
715
|
-
message,
|
|
716
|
-
result,
|
|
717
|
-
typeof result.code === "number" ? result.code : 400,
|
|
718
|
-
false,
|
|
719
|
-
"oauth2_provider_error"
|
|
720
|
-
)
|
|
713
|
+
new ApiError(message, data, 400, false, "oauth2_provider_error")
|
|
721
714
|
);
|
|
722
715
|
}
|
|
723
716
|
};
|
|
@@ -1237,7 +1230,7 @@ var CollectionService = class _CollectionService {
|
|
|
1237
1230
|
requestKey,
|
|
1238
1231
|
autoCancel,
|
|
1239
1232
|
cancelKey,
|
|
1240
|
-
fetch,
|
|
1233
|
+
fetch: fetch2,
|
|
1241
1234
|
headers,
|
|
1242
1235
|
signal,
|
|
1243
1236
|
singleFlight,
|
|
@@ -1281,7 +1274,7 @@ var CollectionService = class _CollectionService {
|
|
|
1281
1274
|
requestKey,
|
|
1282
1275
|
autoCancel,
|
|
1283
1276
|
cancelKey,
|
|
1284
|
-
fetch,
|
|
1277
|
+
fetch: fetch2,
|
|
1285
1278
|
headers,
|
|
1286
1279
|
signal,
|
|
1287
1280
|
singleFlight,
|
|
@@ -1638,14 +1631,13 @@ var CollectionService = class _CollectionService {
|
|
|
1638
1631
|
/**
|
|
1639
1632
|
* Sign in with an OAuth2 provider (PocketBase `authWithOAuth2` parity).
|
|
1640
1633
|
*
|
|
1641
|
-
* One call handles the whole web flow: fetch the provider's `authURL`,
|
|
1642
|
-
*
|
|
1643
|
-
* `
|
|
1644
|
-
* `{ token, record, meta }`.
|
|
1634
|
+
* One call handles the whole web flow: fetch the provider's `authURL`, open a
|
|
1635
|
+
* popup (or call `options.urlCallback`), receive the single-use authorization
|
|
1636
|
+
* `code` the backend relays via `postMessage`, exchange it, populate the auth
|
|
1637
|
+
* store, and resolve with `{ token, record, meta }`.
|
|
1645
1638
|
*
|
|
1646
|
-
* `options.createData` is
|
|
1647
|
-
*
|
|
1648
|
-
* {@link authWithOAuth2Code} when you need extra fields on first sign-up.
|
|
1639
|
+
* `options.createData` is forwarded on first sign-up (the exchange goes
|
|
1640
|
+
* through {@link authWithOAuth2Code}), matching PocketBase.
|
|
1649
1641
|
*
|
|
1650
1642
|
* React Native / non-browser: the popup flow depends on
|
|
1651
1643
|
* `window.postMessage`. Use {@link listAuthMethods} + {@link authWithOAuth2Code}
|
|
@@ -1654,6 +1646,7 @@ var CollectionService = class _CollectionService {
|
|
|
1654
1646
|
async authWithOAuth2(options) {
|
|
1655
1647
|
const {
|
|
1656
1648
|
provider,
|
|
1649
|
+
createData,
|
|
1657
1650
|
urlCallback,
|
|
1658
1651
|
popup,
|
|
1659
1652
|
timeoutMs = DEFAULT_OAUTH2_TIMEOUT_MS
|
|
@@ -1671,18 +1664,19 @@ var CollectionService = class _CollectionService {
|
|
|
1671
1664
|
"oauth2_unknown_provider"
|
|
1672
1665
|
);
|
|
1673
1666
|
}
|
|
1674
|
-
const
|
|
1667
|
+
const { code } = await authorizeWithOAuth2Popup({
|
|
1675
1668
|
authURL: providerInfo.authURL,
|
|
1676
1669
|
expectedOrigin: oauth2RedirectOrigin(this.http.baseUrl),
|
|
1677
1670
|
urlCallback,
|
|
1678
1671
|
popup,
|
|
1679
1672
|
timeoutMs
|
|
1680
1673
|
});
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1674
|
+
return this.authWithOAuth2Code({
|
|
1675
|
+
provider,
|
|
1676
|
+
code,
|
|
1677
|
+
codeVerifier: providerInfo.codeVerifier,
|
|
1678
|
+
createData
|
|
1679
|
+
});
|
|
1686
1680
|
}
|
|
1687
1681
|
/**
|
|
1688
1682
|
* Exchange an OAuth2 authorization code directly (PocketBase
|
|
@@ -2006,9 +2000,28 @@ function getThumbUrl(baseUrl, fileId, size) {
|
|
|
2006
2000
|
function getScaleUrl(baseUrl, fileId, size) {
|
|
2007
2001
|
return baseUrl.replace(/\/+$/, "") + "/files/" + encodeURIComponent(fileId) + "/scale/" + encodeURIComponent(size);
|
|
2008
2002
|
}
|
|
2003
|
+
function getVariantUrl(baseUrl, fileId, preset) {
|
|
2004
|
+
return getScaleUrl(baseUrl, fileId, preset);
|
|
2005
|
+
}
|
|
2006
|
+
function parseUploadTarget(rawUrl, allowedHosts) {
|
|
2007
|
+
let parsed;
|
|
2008
|
+
try {
|
|
2009
|
+
parsed = new URL(rawUrl);
|
|
2010
|
+
} catch {
|
|
2011
|
+
throw new Error("Direct upload: the server returned an invalid upload URL");
|
|
2012
|
+
}
|
|
2013
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
2014
|
+
throw new Error(`Direct upload: unsupported protocol ${parsed.protocol}`);
|
|
2015
|
+
}
|
|
2016
|
+
if (allowedHosts && allowedHosts.length > 0 && !allowedHosts.includes(parsed.host)) {
|
|
2017
|
+
throw new Error(`Direct upload: host ${parsed.host} is not in the allowlist`);
|
|
2018
|
+
}
|
|
2019
|
+
return parsed;
|
|
2020
|
+
}
|
|
2009
2021
|
var FilesService = class {
|
|
2010
|
-
constructor(http) {
|
|
2022
|
+
constructor(http, allowedUploadHosts) {
|
|
2011
2023
|
this.http = http;
|
|
2024
|
+
this.allowedUploadHosts = allowedUploadHosts;
|
|
2012
2025
|
}
|
|
2013
2026
|
/**
|
|
2014
2027
|
* Upload a file or blob.
|
|
@@ -2029,6 +2042,8 @@ var FilesService = class {
|
|
|
2029
2042
|
formData.append("collection_name", meta.collectionName);
|
|
2030
2043
|
if (meta?.recordId) formData.append("record_id", meta.recordId);
|
|
2031
2044
|
if (meta?.fieldName) formData.append("field_name", meta.fieldName);
|
|
2045
|
+
if (meta?.origin) formData.append("origin", meta.origin);
|
|
2046
|
+
if (meta?.variants?.length) formData.append("variants", meta.variants.join(","));
|
|
2032
2047
|
const data = await this.http.request(
|
|
2033
2048
|
"POST",
|
|
2034
2049
|
"/files",
|
|
@@ -2037,6 +2052,62 @@ var FilesService = class {
|
|
|
2037
2052
|
);
|
|
2038
2053
|
return data;
|
|
2039
2054
|
}
|
|
2055
|
+
/**
|
|
2056
|
+
* Direct upload (Mode B): the browser PUTs straight to S3/R2.
|
|
2057
|
+
*
|
|
2058
|
+
* Presigns, uploads, then asks the server to verify the object. Falls back to
|
|
2059
|
+
* `upload()` when the server has no direct-upload support configured.
|
|
2060
|
+
*/
|
|
2061
|
+
async uploadDirect(file, opts) {
|
|
2062
|
+
const filename = opts?.filename || (file instanceof File ? file.name : "file");
|
|
2063
|
+
const mime = file.type || "application/octet-stream";
|
|
2064
|
+
const presigned = await this.presign({
|
|
2065
|
+
filename,
|
|
2066
|
+
size: file.size,
|
|
2067
|
+
mime,
|
|
2068
|
+
collectionName: opts?.collectionName,
|
|
2069
|
+
fieldName: opts?.fieldName,
|
|
2070
|
+
recordId: opts?.recordId
|
|
2071
|
+
});
|
|
2072
|
+
if (!presigned) return this.upload(file, filename, { signal: opts?.signal }, { collectionName: opts?.collectionName, fieldName: opts?.fieldName, recordId: opts?.recordId });
|
|
2073
|
+
const target = parseUploadTarget(presigned.url, this.allowedUploadHosts);
|
|
2074
|
+
const put = await fetch(target.href, {
|
|
2075
|
+
method: presigned.method || "PUT",
|
|
2076
|
+
headers: presigned.headers,
|
|
2077
|
+
body: file,
|
|
2078
|
+
signal: opts?.signal
|
|
2079
|
+
});
|
|
2080
|
+
if (!put.ok) throw new Error(`Direct upload failed: HTTP ${put.status}`);
|
|
2081
|
+
return this.complete(presigned.id, { variant: opts?.variant, signal: opts?.signal });
|
|
2082
|
+
}
|
|
2083
|
+
/** Step 1 of a direct upload: reserve an object and get a presigned PUT. */
|
|
2084
|
+
async presign(params) {
|
|
2085
|
+
const data = await this.http.request(
|
|
2086
|
+
"POST",
|
|
2087
|
+
"/files/presign",
|
|
2088
|
+
{
|
|
2089
|
+
filename: params.filename,
|
|
2090
|
+
size: params.size,
|
|
2091
|
+
mime: params.mime,
|
|
2092
|
+
collection_name: params.collectionName,
|
|
2093
|
+
field_name: params.fieldName,
|
|
2094
|
+
record_id: params.recordId
|
|
2095
|
+
}
|
|
2096
|
+
);
|
|
2097
|
+
if (!data || typeof data.url !== "string") return null;
|
|
2098
|
+
return data;
|
|
2099
|
+
}
|
|
2100
|
+
/** Step 2 of a direct upload: ask the server to verify the object. */
|
|
2101
|
+
async complete(fileId, opts) {
|
|
2102
|
+
const query = opts?.variant ? `?variants=${encodeURIComponent(opts.variant)}` : "";
|
|
2103
|
+
const data = await this.http.request(
|
|
2104
|
+
"POST",
|
|
2105
|
+
`/files/${encodeURIComponent(fileId)}/complete${query}`,
|
|
2106
|
+
{},
|
|
2107
|
+
{ signal: opts?.signal }
|
|
2108
|
+
);
|
|
2109
|
+
return data;
|
|
2110
|
+
}
|
|
2040
2111
|
/**
|
|
2041
2112
|
* List uploaded files (newest first), with optional filters.
|
|
2042
2113
|
*
|
|
@@ -2849,6 +2920,7 @@ function createClient(options) {
|
|
|
2849
2920
|
getFileUrl,
|
|
2850
2921
|
getScaleUrl,
|
|
2851
2922
|
getThumbUrl,
|
|
2923
|
+
getVariantUrl,
|
|
2852
2924
|
schemaFieldType,
|
|
2853
2925
|
wsUrlFromBaseUrl
|
|
2854
2926
|
});
|