clefbase 1.3.4 → 1.3.6
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/cli-src/cli/api.js +25 -3
- package/dist/cli-src/cli/commands/deploy.js +177 -28
- package/dist/cli-src/cli/index.js +36 -8
- package/dist/cli.js +191 -41
- package/dist/hosting/index.d.ts +92 -6
- package/dist/hosting/index.d.ts.map +1 -1
- package/dist/hosting/index.js +67 -14
- package/dist/hosting/index.js.map +1 -1
- package/dist/react/StorageImage.d.ts +7 -58
- package/dist/react/StorageImage.d.ts.map +1 -1
- package/dist/react/StorageImage.js +12 -69
- package/dist/react/StorageImage.js.map +1 -1
- package/dist/storage/index.d.ts +21 -3
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +43 -13
- package/dist/storage/index.js.map +1 -1
- package/package.json +1 -1
package/dist/hosting/index.js
CHANGED
|
@@ -42,6 +42,7 @@ exports.ClefbaseHosting = exports.SiteReference = void 0;
|
|
|
42
42
|
* const site = hosting.site("my-site-id");
|
|
43
43
|
* const result = await site.deployFiles({ "index.html": htmlBuffer, "app.js": jsBuffer });
|
|
44
44
|
* console.log(`Live at ${result.url}`);
|
|
45
|
+
* console.log(`Preview at ${result.previewUrl}`);
|
|
45
46
|
*/
|
|
46
47
|
class SiteReference {
|
|
47
48
|
constructor(hosting, siteId) {
|
|
@@ -74,13 +75,29 @@ class SiteReference {
|
|
|
74
75
|
* message: "v1.2.0",
|
|
75
76
|
* onProgress: (done, total) => console.log(`${done}/${total}`),
|
|
76
77
|
* });
|
|
78
|
+
* console.log(`Live at ${result.url}`);
|
|
77
79
|
*/
|
|
78
80
|
async deployFiles(files, opts) {
|
|
79
81
|
return this.hosting._deployFiles(this.siteId, files, opts);
|
|
80
82
|
}
|
|
81
|
-
/**
|
|
83
|
+
/** Check the DNS status of this site's preview + custom domains. */
|
|
84
|
+
async getDnsStatus() {
|
|
85
|
+
return this.hosting._getDnsStatus(this.siteId);
|
|
86
|
+
}
|
|
87
|
+
/** Re-provision the preview DNS record (e.g. if it was accidentally deleted). */
|
|
88
|
+
async reprovisionDns() {
|
|
89
|
+
return this.hosting._reprovisionDns(this.siteId);
|
|
90
|
+
}
|
|
91
|
+
/** The preview URL for this site ({slug}.preview.cleforyx.com). */
|
|
92
|
+
get previewUrl() {
|
|
93
|
+
return this.hosting._previewUrl(this.siteId);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The public URL — custom domain if configured, else preview subdomain.
|
|
97
|
+
* Requires the site metadata; use `(await site.get())?.customDomain` to check.
|
|
98
|
+
*/
|
|
82
99
|
get url() {
|
|
83
|
-
return this.
|
|
100
|
+
return this.previewUrl; // SDK doesn't cache site; use DeployResult.url for canonical
|
|
84
101
|
}
|
|
85
102
|
}
|
|
86
103
|
exports.SiteReference = SiteReference;
|
|
@@ -90,7 +107,12 @@ exports.SiteReference = SiteReference;
|
|
|
90
107
|
*
|
|
91
108
|
* @example
|
|
92
109
|
* const hosting = getHosting(app);
|
|
110
|
+
*
|
|
111
|
+
* // Create a site — automatically provisions {name}.preview.cleforyx.com
|
|
93
112
|
* const site = await hosting.createSite("my-app");
|
|
113
|
+
* console.log(site.previewUrl); // https://my-app.preview.cleforyx.com
|
|
114
|
+
*
|
|
115
|
+
* // Deploy files
|
|
94
116
|
* const result = await hosting.site(site.id).deployFiles(files);
|
|
95
117
|
* console.log(result.url);
|
|
96
118
|
*/
|
|
@@ -104,24 +126,45 @@ class ClefbaseHosting {
|
|
|
104
126
|
async listSites() {
|
|
105
127
|
return this.http.get(`/databases/${this.dbId}/sites`);
|
|
106
128
|
}
|
|
107
|
-
/**
|
|
129
|
+
/**
|
|
130
|
+
* Create a new hosted site.
|
|
131
|
+
*
|
|
132
|
+
* Site names must be globally unique across the platform.
|
|
133
|
+
* A CNAME record will be automatically provisioned on Cloudflare:
|
|
134
|
+
* {slug}.preview.cleforyx.com → cleforyx.com
|
|
135
|
+
*
|
|
136
|
+
* Throws a 409 ConflictError if the name is already taken.
|
|
137
|
+
*/
|
|
108
138
|
async createSite(name, description) {
|
|
109
139
|
return this.http.post(`/databases/${this.dbId}/sites`, { name, description });
|
|
110
140
|
}
|
|
111
|
-
/** Update site metadata. */
|
|
141
|
+
/** Update site metadata. Handles DNS rename if name changes. */
|
|
112
142
|
async updateSite(siteId, patch) {
|
|
113
143
|
return this.http.patch(`/databases/${this.dbId}/sites/${siteId}`, patch);
|
|
114
144
|
}
|
|
115
|
-
/**
|
|
116
|
-
|
|
117
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Begin site deletion flow.
|
|
147
|
+
*
|
|
148
|
+
* Pass `confirm: false` (or omit) to get a confirmation prompt with DNS info.
|
|
149
|
+
* Pass `confirm: true` to actually delete. Include `deleteDns: true` to also
|
|
150
|
+
* remove the Cloudflare DNS record.
|
|
151
|
+
*/
|
|
152
|
+
async deleteSite(siteId, opts) {
|
|
153
|
+
const params = new URLSearchParams();
|
|
154
|
+
if (opts?.confirm)
|
|
155
|
+
params.set("confirm", "true");
|
|
156
|
+
if (opts?.deleteDns)
|
|
157
|
+
params.set("deleteDns", "true");
|
|
158
|
+
const qs = params.toString() ? `?${params}` : "";
|
|
159
|
+
return this.http.delete(`/databases/${this.dbId}/sites/${siteId}${qs}`);
|
|
118
160
|
}
|
|
119
161
|
/** Return a SiteReference for a specific site ID. */
|
|
120
162
|
site(siteId) {
|
|
121
163
|
return new SiteReference(this, siteId);
|
|
122
164
|
}
|
|
123
165
|
// ─── Internals ─────────────────────────────────────────────────────────────
|
|
124
|
-
|
|
166
|
+
_previewUrl(siteId) {
|
|
167
|
+
// Falls back to server URL path if site metadata isn't cached
|
|
125
168
|
return `${this.serverUrl.replace(/\/+$/, "")}/hosted/${this.dbId}/${siteId}`;
|
|
126
169
|
}
|
|
127
170
|
async _getSite(siteId) {
|
|
@@ -148,28 +191,35 @@ class ClefbaseHosting {
|
|
|
148
191
|
async _listDeploys(siteId) {
|
|
149
192
|
return this.http.get(`/databases/${this.dbId}/sites/${siteId}/deploys`);
|
|
150
193
|
}
|
|
194
|
+
async _getDnsStatus(siteId) {
|
|
195
|
+
return this.http.get(`/databases/${this.dbId}/sites/${siteId}/dns`);
|
|
196
|
+
}
|
|
197
|
+
async _reprovisionDns(siteId) {
|
|
198
|
+
return this.http.post(`/databases/${this.dbId}/sites/${siteId}/dns/provision`, {});
|
|
199
|
+
}
|
|
151
200
|
async _deployFiles(siteId, files, opts = {}) {
|
|
152
201
|
const { entrypoint = "index.html", deployedBy, message, batchSize = 20, onProgress } = opts;
|
|
153
202
|
// 1. Create pending deploy
|
|
154
203
|
const pending = await this.http.post(`/databases/${this.dbId}/sites/${siteId}/deploys`, { deployedBy, entrypoint });
|
|
204
|
+
// 2. Get site for canonical URL
|
|
205
|
+
const siteData = await this._getSite(siteId).catch(() => null);
|
|
155
206
|
const filePaths = Object.keys(files);
|
|
156
207
|
const fileBuffers = Object.values(files);
|
|
157
208
|
const errors = [];
|
|
158
209
|
let uploaded = 0;
|
|
159
|
-
//
|
|
210
|
+
// 3. Detect whether we have native FormData (Node 18+) or need form-data pkg
|
|
160
211
|
const hasNativeFormData = typeof FormData !== "undefined";
|
|
161
212
|
let FormDataLegacy;
|
|
162
213
|
if (!hasNativeFormData) {
|
|
163
214
|
const mod = await Promise.resolve().then(() => __importStar(require("form-data")));
|
|
164
215
|
FormDataLegacy = mod.default;
|
|
165
216
|
}
|
|
166
|
-
//
|
|
217
|
+
// 4. Upload in batches
|
|
167
218
|
for (let i = 0; i < filePaths.length; i += batchSize) {
|
|
168
219
|
const batchPaths = filePaths.slice(i, i + batchSize);
|
|
169
220
|
const batchBuffers = fileBuffers.slice(i, i + batchSize);
|
|
170
221
|
try {
|
|
171
222
|
if (hasNativeFormData) {
|
|
172
|
-
// Node 18+ native FormData: Buffers must be wrapped in Blob/File
|
|
173
223
|
const form = new FormData();
|
|
174
224
|
for (let j = 0; j < batchPaths.length; j++) {
|
|
175
225
|
const filename = batchPaths[j].split("/").pop() ?? batchPaths[j];
|
|
@@ -182,7 +232,6 @@ class ClefbaseHosting {
|
|
|
182
232
|
await this.http.request(`/databases/${this.dbId}/deploys/${pending.id}/files/batch`, { method: "POST", body: form, isFormData: true });
|
|
183
233
|
}
|
|
184
234
|
else {
|
|
185
|
-
// Older Node: form-data package accepts Buffers natively
|
|
186
235
|
const form = new FormDataLegacy();
|
|
187
236
|
for (let j = 0; j < batchPaths.length; j++) {
|
|
188
237
|
form.append("files[]", batchBuffers[j], {
|
|
@@ -200,13 +249,17 @@ class ClefbaseHosting {
|
|
|
200
249
|
}
|
|
201
250
|
onProgress?.(Math.min(i + batchSize, filePaths.length), filePaths.length);
|
|
202
251
|
}
|
|
203
|
-
//
|
|
252
|
+
// 5. Finalize → go live
|
|
204
253
|
const live = await this.http.post(`/databases/${this.dbId}/deploys/${pending.id}/finalize`, { message: message ?? "Deployed via clefbase SDK" });
|
|
254
|
+
const canonicalUrl = siteData?.customDomain
|
|
255
|
+
? `https://${siteData.customDomain}`
|
|
256
|
+
: siteData?.previewUrl ?? this._previewUrl(siteId);
|
|
205
257
|
return {
|
|
206
258
|
deploy: live,
|
|
207
259
|
filesUploaded: uploaded,
|
|
208
260
|
errors,
|
|
209
|
-
url:
|
|
261
|
+
url: canonicalUrl,
|
|
262
|
+
previewUrl: siteData?.previewUrl ?? this._previewUrl(siteId),
|
|
210
263
|
};
|
|
211
264
|
}
|
|
212
265
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hosting/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hosting/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,75 +2,24 @@ import React, { type ImgHTMLAttributes } from "react";
|
|
|
2
2
|
import type { ClefbaseStorage } from "../storage";
|
|
3
3
|
export type StorageImageStatus = "loading" | "loaded" | "error";
|
|
4
4
|
export interface StorageImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "src"> {
|
|
5
|
-
/**
|
|
6
|
-
* The ClefbaseStorage instance returned by `getStorage(app)`.
|
|
7
|
-
*/
|
|
8
5
|
storage: ClefbaseStorage;
|
|
9
|
-
/**
|
|
10
|
-
* Path inside the default bucket, e.g. `"avatars/user-123.jpg"`.
|
|
11
|
-
* Mutually exclusive with `bucket` + `path`.
|
|
12
|
-
*/
|
|
6
|
+
/** Path inside the default bucket, e.g. "avatars/user-123.jpg". */
|
|
13
7
|
src?: string;
|
|
14
|
-
/**
|
|
15
|
-
* Named bucket to read from. Pair with `path`.
|
|
16
|
-
*/
|
|
8
|
+
/** Named bucket to read from. Pair with `path`. */
|
|
17
9
|
bucket?: string;
|
|
18
|
-
/**
|
|
19
|
-
* File path inside `bucket`.
|
|
20
|
-
*/
|
|
10
|
+
/** File path inside `bucket`. */
|
|
21
11
|
path?: string;
|
|
22
|
-
/**
|
|
23
|
-
* Shown while the download URL is being resolved.
|
|
24
|
-
* Accepts a URL string or any React node.
|
|
25
|
-
*/
|
|
12
|
+
/** Shown while the download URL is being resolved. */
|
|
26
13
|
placeholder?: React.ReactNode | string;
|
|
27
|
-
/**
|
|
28
|
-
* Shown when the URL cannot be resolved or the image fails to load.
|
|
29
|
-
* Accepts a URL string or any React node.
|
|
30
|
-
*/
|
|
14
|
+
/** Shown when the URL cannot be resolved or the image fails to load. */
|
|
31
15
|
fallback?: React.ReactNode | string;
|
|
32
|
-
/**
|
|
33
|
-
* Called with the resolved download URL once ready.
|
|
34
|
-
*/
|
|
35
16
|
onResolve?: (url: string) => void;
|
|
36
|
-
/**
|
|
37
|
-
* Called if URL resolution or image loading fails.
|
|
38
|
-
*/
|
|
39
17
|
onError?: (err: unknown) => void;
|
|
40
|
-
/**
|
|
41
|
-
* Called whenever the loading status changes.
|
|
42
|
-
*/
|
|
43
18
|
onStatusChange?: (status: StorageImageStatus) => void;
|
|
44
19
|
}
|
|
45
20
|
/**
|
|
46
|
-
*
|
|
47
|
-
* authenticated download URLs
|
|
48
|
-
*
|
|
49
|
-
* @example — default bucket
|
|
50
|
-
* ```tsx
|
|
51
|
-
* const storage = getStorage(app);
|
|
52
|
-
*
|
|
53
|
-
* <StorageImage
|
|
54
|
-
* storage={storage}
|
|
55
|
-
* src="avatars/user-123.jpg"
|
|
56
|
-
* alt="User avatar"
|
|
57
|
-
* width={64}
|
|
58
|
-
* height={64}
|
|
59
|
-
* style={{ borderRadius: "50%" }}
|
|
60
|
-
* />
|
|
61
|
-
* ```
|
|
62
|
-
*
|
|
63
|
-
* @example — named bucket
|
|
64
|
-
* ```tsx
|
|
65
|
-
* <StorageImage
|
|
66
|
-
* storage={storage}
|
|
67
|
-
* bucket="media"
|
|
68
|
-
* path="products/shoe.webp"
|
|
69
|
-
* alt="Product photo"
|
|
70
|
-
* placeholder={<Spinner />}
|
|
71
|
-
* fallback={<img src="/no-image.svg" alt="Not found" />}
|
|
72
|
-
* />
|
|
73
|
-
* ```
|
|
21
|
+
* Drop-in <img> replacement that resolves Clefbase storage paths to
|
|
22
|
+
* authenticated download URLs via /storage/resolve.
|
|
74
23
|
*/
|
|
75
24
|
export declare const StorageImage: React.ForwardRefExoticComponent<StorageImageProps & React.RefAttributes<HTMLImageElement>>;
|
|
76
25
|
export default StorageImage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StorageImage.d.ts","sourceRoot":"","sources":["../../src/react/StorageImage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAKZ,KAAK,iBAAiB,EACvB,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAIlD,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEhE,MAAM,WAAW,iBACf,SAAQ,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,EAAE,KAAK,CAAC;IACxD
|
|
1
|
+
{"version":3,"file":"StorageImage.d.ts","sourceRoot":"","sources":["../../src/react/StorageImage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAKZ,KAAK,iBAAiB,EACvB,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAIlD,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEhE,MAAM,WAAW,iBACf,SAAQ,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,EAAE,KAAK,CAAC;IACxD,OAAO,EAAE,eAAe,CAAC;IACzB,mEAAmE;IACnE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC;IACvC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC;IACpC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IACjC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;CACvD;AAID;;;GAGG;AACH,eAAO,MAAM,YAAY,4FAoIxB,CAAC;AAGF,eAAe,YAAY,CAAC"}
|
|
@@ -5,70 +5,20 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
5
5
|
const react_1 = require("react");
|
|
6
6
|
// ─── StorageImage ─────────────────────────────────────────────────────────────
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
* authenticated download URLs
|
|
10
|
-
*
|
|
11
|
-
* @example — default bucket
|
|
12
|
-
* ```tsx
|
|
13
|
-
* const storage = getStorage(app);
|
|
14
|
-
*
|
|
15
|
-
* <StorageImage
|
|
16
|
-
* storage={storage}
|
|
17
|
-
* src="avatars/user-123.jpg"
|
|
18
|
-
* alt="User avatar"
|
|
19
|
-
* width={64}
|
|
20
|
-
* height={64}
|
|
21
|
-
* style={{ borderRadius: "50%" }}
|
|
22
|
-
* />
|
|
23
|
-
* ```
|
|
24
|
-
*
|
|
25
|
-
* @example — named bucket
|
|
26
|
-
* ```tsx
|
|
27
|
-
* <StorageImage
|
|
28
|
-
* storage={storage}
|
|
29
|
-
* bucket="media"
|
|
30
|
-
* path="products/shoe.webp"
|
|
31
|
-
* alt="Product photo"
|
|
32
|
-
* placeholder={<Spinner />}
|
|
33
|
-
* fallback={<img src="/no-image.svg" alt="Not found" />}
|
|
34
|
-
* />
|
|
35
|
-
* ```
|
|
8
|
+
* Drop-in <img> replacement that resolves Clefbase storage paths to
|
|
9
|
+
* authenticated download URLs via /storage/resolve.
|
|
36
10
|
*/
|
|
37
11
|
exports.StorageImage = (0, react_1.forwardRef)(function StorageImage({ storage, src, bucket, path, placeholder, fallback, onResolve, onError, onStatusChange, alt = "", ...imgProps }, ref) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// connection pool (typically 6 connections) when many cards mount at once.
|
|
41
|
-
const getDirectUrl = () => {
|
|
42
|
-
try {
|
|
43
|
-
if (bucket && path)
|
|
44
|
-
return storage._directUrl(bucket, path);
|
|
45
|
-
if (src)
|
|
46
|
-
return storage._directUrl(storage["_defaultBucket"] ?? "default", src);
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
const initialUrl = getDirectUrl();
|
|
54
|
-
const [resolvedUrl, setResolvedUrl] = (0, react_1.useState)(initialUrl);
|
|
55
|
-
const [status, setStatus] = (0, react_1.useState)(initialUrl ? "loaded" : "loading");
|
|
56
|
-
// Keep a ref to the latest callbacks so the effect closure stays stable.
|
|
12
|
+
const [resolvedUrl, setResolvedUrl] = (0, react_1.useState)(null);
|
|
13
|
+
const [status, setStatus] = (0, react_1.useState)("loading");
|
|
57
14
|
const onResolveRef = (0, react_1.useRef)(onResolve);
|
|
58
15
|
const onErrorRef = (0, react_1.useRef)(onError);
|
|
59
16
|
const onStatusRef = (0, react_1.useRef)(onStatusChange);
|
|
60
17
|
onResolveRef.current = onResolve;
|
|
61
18
|
onErrorRef.current = onError;
|
|
62
19
|
onStatusRef.current = onStatusChange;
|
|
63
|
-
// Determine the key that uniquely identifies the requested file.
|
|
64
20
|
const fileKey = bucket && path ? `${bucket}::${path}` : src ?? null;
|
|
65
21
|
(0, react_1.useEffect)(() => {
|
|
66
|
-
// If we already resolved synchronously, notify callers and skip the
|
|
67
|
-
// async effect entirely — no extra network round-trip needed.
|
|
68
|
-
if (initialUrl) {
|
|
69
|
-
onResolveRef.current?.(initialUrl);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
22
|
if (!fileKey)
|
|
73
23
|
return;
|
|
74
24
|
let cancelled = false;
|
|
@@ -78,9 +28,7 @@ exports.StorageImage = (0, react_1.forwardRef)(function StorageImage({ storage,
|
|
|
78
28
|
try {
|
|
79
29
|
let url;
|
|
80
30
|
if (bucket && path) {
|
|
81
|
-
url = storage.bucket(bucket).ref(path).getDownloadURL
|
|
82
|
-
? await storage.bucket(bucket).ref(path).getDownloadURL()
|
|
83
|
-
: storage._directUrl(bucket, path);
|
|
31
|
+
url = await storage.bucket(bucket).ref(path).getDownloadURL();
|
|
84
32
|
}
|
|
85
33
|
else if (src) {
|
|
86
34
|
url = await storage.ref(src).getDownloadURL();
|
|
@@ -90,9 +38,11 @@ exports.StorageImage = (0, react_1.forwardRef)(function StorageImage({ storage,
|
|
|
90
38
|
}
|
|
91
39
|
if (cancelled)
|
|
92
40
|
return;
|
|
41
|
+
if (!url)
|
|
42
|
+
throw new Error("StorageImage: empty download URL returned.");
|
|
93
43
|
setResolvedUrl(url);
|
|
94
44
|
onResolveRef.current?.(url);
|
|
95
|
-
// status
|
|
45
|
+
// status → "loaded" fires in the <img> onLoad handler
|
|
96
46
|
}
|
|
97
47
|
catch (err) {
|
|
98
48
|
if (cancelled)
|
|
@@ -103,9 +53,7 @@ exports.StorageImage = (0, react_1.forwardRef)(function StorageImage({ storage,
|
|
|
103
53
|
}
|
|
104
54
|
};
|
|
105
55
|
resolve();
|
|
106
|
-
return () => {
|
|
107
|
-
cancelled = true;
|
|
108
|
-
};
|
|
56
|
+
return () => { cancelled = true; };
|
|
109
57
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
110
58
|
}, [fileKey]);
|
|
111
59
|
// ── Render helpers ────────────────────────────────────────────────────────
|
|
@@ -118,18 +66,17 @@ exports.StorageImage = (0, react_1.forwardRef)(function StorageImage({ storage,
|
|
|
118
66
|
onStatusRef.current?.("error");
|
|
119
67
|
onErrorRef.current?.(e);
|
|
120
68
|
};
|
|
121
|
-
//
|
|
69
|
+
// Placeholder while resolving
|
|
122
70
|
if (status === "loading" && !resolvedUrl) {
|
|
123
71
|
if (placeholder === undefined) {
|
|
124
|
-
// Invisible img keeps layout stable
|
|
125
72
|
return ((0, jsx_runtime_1.jsx)("img", { ...imgProps, ref: ref, src: "", alt: alt, style: { ...imgProps.style, visibility: "hidden" } }));
|
|
126
73
|
}
|
|
127
74
|
if (typeof placeholder === "string") {
|
|
128
|
-
return (
|
|
75
|
+
return (0, jsx_runtime_1.jsx)("img", { ...imgProps, ref: ref, src: placeholder, alt: alt });
|
|
129
76
|
}
|
|
130
77
|
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: placeholder });
|
|
131
78
|
}
|
|
132
|
-
//
|
|
79
|
+
// Fallback on error
|
|
133
80
|
if (status === "error" || !resolvedUrl) {
|
|
134
81
|
if (fallback === undefined) {
|
|
135
82
|
return ((0, jsx_runtime_1.jsx)("img", { ...imgProps, ref: ref, src: "", alt: alt, style: { ...imgProps.style, visibility: "hidden" } }));
|
|
@@ -139,10 +86,6 @@ exports.StorageImage = (0, react_1.forwardRef)(function StorageImage({ storage,
|
|
|
139
86
|
}
|
|
140
87
|
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: fallback });
|
|
141
88
|
}
|
|
142
|
-
// Resolved — render the real image.
|
|
143
|
-
// `loading="lazy"` defers off-screen fetches so the browser never
|
|
144
|
-
// saturates its per-host connection pool (typically 6) when many
|
|
145
|
-
// cards render simultaneously. Callers may override with loading="eager".
|
|
146
89
|
return ((0, jsx_runtime_1.jsx)("img", { loading: "lazy", ...imgProps, ref: ref, src: resolvedUrl, alt: alt, onLoad: handleLoad, onError: handleImgError }));
|
|
147
90
|
});
|
|
148
91
|
exports.StorageImage.displayName = "StorageImage";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StorageImage.js","sourceRoot":"","sources":["../../src/react/StorageImage.tsx"],"names":[],"mappings":";;;;AAAA,iCAMe;
|
|
1
|
+
{"version":3,"file":"StorageImage.js","sourceRoot":"","sources":["../../src/react/StorageImage.tsx"],"names":[],"mappings":"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"}
|
package/dist/storage/index.d.ts
CHANGED
|
@@ -22,6 +22,10 @@ export declare class StorageReference {
|
|
|
22
22
|
contentType?: string;
|
|
23
23
|
metadata?: Record<string, unknown>;
|
|
24
24
|
}): Promise<StorageFile>;
|
|
25
|
+
/**
|
|
26
|
+
* Returns the authenticated download URL for this file.
|
|
27
|
+
* Always resolves via /storage/resolve to get the real bucketId + fileId.
|
|
28
|
+
*/
|
|
25
29
|
getDownloadURL(): Promise<string>;
|
|
26
30
|
getMetadata(): Promise<StorageFile>;
|
|
27
31
|
delete(): Promise<void>;
|
|
@@ -40,18 +44,32 @@ export declare class ClefbaseStorage {
|
|
|
40
44
|
private readonly http;
|
|
41
45
|
private readonly projectId;
|
|
42
46
|
private readonly auth;
|
|
43
|
-
/** @internal
|
|
47
|
+
/** @internal used by StorageImage — kept for compat but now returns empty string (no /path route exists) */
|
|
44
48
|
_defaultBucket: string;
|
|
45
49
|
/** In-memory cache: bucket name → UUID */
|
|
46
50
|
private _bucketIdCache;
|
|
51
|
+
/** In-memory cache: "bucket::filePath" → download URL */
|
|
52
|
+
private _urlCache;
|
|
47
53
|
constructor(http: HttpClient, projectId: string, auth?: Auth | null);
|
|
48
54
|
private authHeaders;
|
|
49
55
|
setDefaultBucket(name: string): this;
|
|
50
56
|
bucket(name: string): BucketReference;
|
|
51
57
|
ref(filePath: string): StorageReference;
|
|
52
58
|
_resolveBucketId(bucketName: string): Promise<string>;
|
|
53
|
-
/**
|
|
54
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Resolves a storage path to an authenticated download URL via
|
|
61
|
+
* GET /storage/resolve?projectId=xxx&path=bucketName/folder/file.jpg
|
|
62
|
+
*
|
|
63
|
+
* The server returns { downloadUrl, id, bucketId, ... } — we use downloadUrl directly.
|
|
64
|
+
* Results are cached so repeated calls (e.g. many cards) only hit the server once per path.
|
|
65
|
+
*/
|
|
66
|
+
_getDownloadURL(bucket: string, filePath: string): Promise<string>;
|
|
67
|
+
/**
|
|
68
|
+
* @deprecated No /path route exists on the server.
|
|
69
|
+
* Kept so StorageImage's synchronous fast-path gracefully returns ""
|
|
70
|
+
* (which makes StorageImage fall through to the async resolve path).
|
|
71
|
+
*/
|
|
72
|
+
_directUrl(_bucket: string, _filePath: string): string;
|
|
55
73
|
_resolve(bucket: string, filePath: string): Promise<StorageFile>;
|
|
56
74
|
_upload(bucket: string, filePath: string, data: Buffer | Blob | File, opts?: {
|
|
57
75
|
contentType?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAIpC,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAID,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,QAAQ,CAAC,OAAO;aACR,MAAM,EAAE,MAAM;aACd,QAAQ,EAAE,MAAM;gBAFf,OAAO,EAAE,eAAe,EACzB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM;IAG5B,MAAM,CACV,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,EAC1B,IAAI,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAClE,OAAO,CAAC,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"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"}
|
package/dist/storage/index.js
CHANGED
|
@@ -44,8 +44,12 @@ class StorageReference {
|
|
|
44
44
|
async upload(data, opts) {
|
|
45
45
|
return this.storage._upload(this.bucket, this.filePath, data, opts);
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Returns the authenticated download URL for this file.
|
|
49
|
+
* Always resolves via /storage/resolve to get the real bucketId + fileId.
|
|
50
|
+
*/
|
|
47
51
|
async getDownloadURL() {
|
|
48
|
-
return this.storage.
|
|
52
|
+
return this.storage._getDownloadURL(this.bucket, this.filePath);
|
|
49
53
|
}
|
|
50
54
|
async getMetadata() {
|
|
51
55
|
return this.storage._resolve(this.bucket, this.filePath);
|
|
@@ -75,10 +79,12 @@ class ClefbaseStorage {
|
|
|
75
79
|
this.http = http;
|
|
76
80
|
this.projectId = projectId;
|
|
77
81
|
this.auth = auth;
|
|
78
|
-
/** @internal
|
|
82
|
+
/** @internal used by StorageImage — kept for compat but now returns empty string (no /path route exists) */
|
|
79
83
|
this._defaultBucket = "default";
|
|
80
84
|
/** In-memory cache: bucket name → UUID */
|
|
81
85
|
this._bucketIdCache = new Map();
|
|
86
|
+
/** In-memory cache: "bucket::filePath" → download URL */
|
|
87
|
+
this._urlCache = new Map();
|
|
82
88
|
}
|
|
83
89
|
authHeaders() {
|
|
84
90
|
return this.auth?.getAuthHeaders() ?? {};
|
|
@@ -94,10 +100,6 @@ class ClefbaseStorage {
|
|
|
94
100
|
return new StorageReference(this, this._defaultBucket, filePath.replace(/^\/+/, ""));
|
|
95
101
|
}
|
|
96
102
|
// ─── Bucket UUID resolution ───────────────────────────────────────────────
|
|
97
|
-
//
|
|
98
|
-
// The external storage router identifies buckets by UUID, not by name.
|
|
99
|
-
// dbId comes from the API key (set by dbApiKeyMiddleware), so we just call
|
|
100
|
-
// GET /buckets (no projectId needed — the key carries that context).
|
|
101
103
|
async _resolveBucketId(bucketName) {
|
|
102
104
|
const cached = this._bucketIdCache.get(bucketName);
|
|
103
105
|
if (cached)
|
|
@@ -111,19 +113,43 @@ class ClefbaseStorage {
|
|
|
111
113
|
throw new Error(`Storage bucket "${bucketName}" not found`);
|
|
112
114
|
return id;
|
|
113
115
|
}
|
|
114
|
-
// ───
|
|
115
|
-
/**
|
|
116
|
-
|
|
116
|
+
// ─── Download URL ─────────────────────────────────────────────────────────
|
|
117
|
+
/**
|
|
118
|
+
* Resolves a storage path to an authenticated download URL via
|
|
119
|
+
* GET /storage/resolve?projectId=xxx&path=bucketName/folder/file.jpg
|
|
120
|
+
*
|
|
121
|
+
* The server returns { downloadUrl, id, bucketId, ... } — we use downloadUrl directly.
|
|
122
|
+
* Results are cached so repeated calls (e.g. many cards) only hit the server once per path.
|
|
123
|
+
*/
|
|
124
|
+
async _getDownloadURL(bucket, filePath) {
|
|
117
125
|
const cleanFile = filePath.replace(/^\/+/, "");
|
|
118
|
-
|
|
119
|
-
|
|
126
|
+
// The resolve endpoint expects the full path including bucket name
|
|
127
|
+
const fullPath = `${bucket}/${cleanFile}`;
|
|
128
|
+
const cacheKey = fullPath;
|
|
129
|
+
const cached = this._urlCache.get(cacheKey);
|
|
130
|
+
if (cached)
|
|
131
|
+
return cached;
|
|
132
|
+
const meta = await this._resolve(bucket, cleanFile);
|
|
133
|
+
const url = meta.downloadUrl ?? "";
|
|
134
|
+
if (url)
|
|
135
|
+
this._urlCache.set(cacheKey, url);
|
|
136
|
+
return url;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* @deprecated No /path route exists on the server.
|
|
140
|
+
* Kept so StorageImage's synchronous fast-path gracefully returns ""
|
|
141
|
+
* (which makes StorageImage fall through to the async resolve path).
|
|
142
|
+
*/
|
|
143
|
+
_directUrl(_bucket, _filePath) {
|
|
144
|
+
return "";
|
|
120
145
|
}
|
|
121
146
|
async _resolve(bucket, filePath) {
|
|
122
147
|
const cleanFile = filePath.replace(/^\/+/, "");
|
|
123
|
-
|
|
148
|
+
// The resolve endpoint matches by fullPath = "bucketName/folder/file.jpg"
|
|
149
|
+
const fullPath = `${bucket}/${cleanFile}`;
|
|
150
|
+
return this.http.get(`/resolve?projectId=${encodeURIComponent(this.projectId)}&path=${encodeURIComponent(fullPath)}`);
|
|
124
151
|
}
|
|
125
152
|
async _upload(bucket, filePath, data, opts) {
|
|
126
|
-
// Resolve bucket name → UUID before building the URL
|
|
127
153
|
const bucketId = await this._resolveBucketId(bucket);
|
|
128
154
|
let FormDataImpl;
|
|
129
155
|
if (typeof FormData !== "undefined") {
|
|
@@ -148,6 +174,9 @@ class ClefbaseStorage {
|
|
|
148
174
|
form.append("folder", folder);
|
|
149
175
|
if (opts?.metadata)
|
|
150
176
|
form.append("metadata", JSON.stringify(opts.metadata));
|
|
177
|
+
// Bust the URL cache for this path so a fresh URL is fetched after upload
|
|
178
|
+
const cacheKey = `${bucket}/${cleanPath}`;
|
|
179
|
+
this._urlCache.delete(cacheKey);
|
|
151
180
|
return this.http.request(`/buckets/${bucketId}/files`, {
|
|
152
181
|
method: "POST",
|
|
153
182
|
body: form,
|
|
@@ -158,6 +187,7 @@ class ClefbaseStorage {
|
|
|
158
187
|
async _delete(bucket, filePath) {
|
|
159
188
|
const bucketId = await this._resolveBucketId(bucket);
|
|
160
189
|
const meta = await this._resolve(bucket, filePath);
|
|
190
|
+
this._urlCache.delete(`${bucket}/${filePath.replace(/^\/+/, "")}`);
|
|
161
191
|
await this.http.delete(`/buckets/${bucketId}/files/${meta.id}`, this.authHeaders());
|
|
162
192
|
}
|
|
163
193
|
async _list(bucket, prefix, opts) {
|