@bloklabs/presets 1.12.0 → 1.14.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/README.md +17 -9
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +60 -43
- package/package.json +1 -1
- package/types/index.d.ts +17 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @bloklabs/presets
|
|
2
2
|
|
|
3
|
-
Ready-made [`uploader`](https://blokeditor.com/docs/uploader-api) implementations for [Blok](https://blokeditor.com)
|
|
3
|
+
Ready-made [`uploader`](https://blokeditor.com/docs/uploader-api) implementations for [Blok](https://blokeditor.com). Plug in storage you already have instead of writing an upload handler yourself. Five presets ship today: Supabase, S3-compatible storage (presigned URLs), Cloudinary, a generic fetch endpoint, and IndexedDB.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ npm install @bloklabs/presets @bloklabs/core
|
|
|
10
10
|
|
|
11
11
|
## What a preset is
|
|
12
12
|
|
|
13
|
-
Blok's `uploader` config option takes an object shaped like this
|
|
13
|
+
Blok's `uploader` config option takes an object shaped like this. Store the file, return where it landed:
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
16
|
import { Blok } from '@bloklabs/core';
|
|
@@ -26,7 +26,7 @@ new Blok({
|
|
|
26
26
|
});
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
Without an `uploadByFile`, an uploaded asset becomes a `blob:` URL
|
|
29
|
+
Without an `uploadByFile`, an uploaded asset becomes a `blob:` URL. That URL does not survive a reload, so most projects need to configure one. Each preset in this package implements that shape for one storage backend. Import a preset instead of writing `uploadByFile` yourself.
|
|
30
30
|
|
|
31
31
|
## Which preset, and what it needs
|
|
32
32
|
|
|
@@ -38,7 +38,7 @@ Without an `uploadByFile`, an uploaded asset becomes a `blob:` URL that doesn't
|
|
|
38
38
|
| `cloudinaryStorage` | **Yes** — Cloudinary fetches it itself | An **unsigned** upload preset in the Cloudinary dashboard |
|
|
39
39
|
| `indexedDBStorage` | No | Nothing — see the warning below |
|
|
40
40
|
|
|
41
|
-
Only `fetchStorage` and `cloudinaryStorage` can turn a pasted remote URL into a re-hosted copy
|
|
41
|
+
Only `fetchStorage` and `cloudinaryStorage` can turn a pasted remote URL into a re-hosted copy. Both have something that fetches the URL on the server side, either your endpoint or Cloudinary's. The other three cannot. A browser is not allowed to fetch a third-party URL and re-upload it itself. Their `uploadByUrl` is deliberately left undefined rather than faked. Blok then falls back to its documented behavior and stores the pasted URL verbatim.
|
|
42
42
|
|
|
43
43
|
### fetchStorage
|
|
44
44
|
|
|
@@ -48,7 +48,7 @@ import { fetchStorage } from '@bloklabs/presets';
|
|
|
48
48
|
new Blok({ holder: 'editor', uploader: fetchStorage({ baseUrl: 'https://api.myapp.com' }) });
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
Your own backend does the actual storing
|
|
51
|
+
Your own backend does the actual storing. This preset only calls it. The backend must answer `POST {baseUrl}/upload` (multipart) and `POST {baseUrl}/upload-by-url` (`{ url }` JSON). Both return `{ url, fileName? }`. The URL you receive on `/upload-by-url` is user-supplied. Validate it server-side and block requests to internal or private addresses before fetching. Otherwise the endpoint becomes an SSRF vector.
|
|
52
52
|
|
|
53
53
|
### supabaseStorage
|
|
54
54
|
|
|
@@ -60,7 +60,7 @@ const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
|
|
|
60
60
|
new Blok({ holder: 'editor', uploader: supabaseStorage(supabase, { bucket: 'blok' }) });
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
Create the bucket first (default name `"blok"`)
|
|
63
|
+
Create the bucket first (default name `"blok"`). Make it public or add an anon `SELECT` policy. This preset returns `getPublicUrl()` directly, so an unreadable object is a broken image. Then add an `INSERT` policy for whichever role the client authenticates as.
|
|
64
64
|
|
|
65
65
|
### presignedStorage
|
|
66
66
|
|
|
@@ -70,7 +70,7 @@ import { presignedStorage } from '@bloklabs/presets';
|
|
|
70
70
|
new Blok({ holder: 'editor', uploader: presignedStorage({ sign: (request) => api.sign(request) }) });
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
-
For S3, R2, MinIO, or any S3-compatible store. `sign` is your function
|
|
73
|
+
For S3, R2, MinIO, or any S3-compatible store. `sign` is your function. It gets `{ fileName, mimeType, size, kind }` and returns a presigned `{ uploadUrl, publicUrl, headers? }` from your backend. The browser then `PUT`s the file straight to `uploadUrl`. So the bucket needs a CORS rule allowing `PUT` (and `Content-Type`) from your app's origin. That CORS rule is the part that actually blocks people.
|
|
74
74
|
|
|
75
75
|
### cloudinaryStorage
|
|
76
76
|
|
|
@@ -83,7 +83,7 @@ new Blok({
|
|
|
83
83
|
});
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
`uploadPreset` must be **unsigned** (Cloudinary dashboard → Settings → Upload → Upload presets). A signed preset needs a server to sign the request
|
|
86
|
+
`uploadPreset` must be **unsigned** (Cloudinary dashboard → Settings → Upload → Upload presets). A signed preset needs a server to sign the request. That defeats the point of a preset with no backend.
|
|
87
87
|
|
|
88
88
|
### indexedDBStorage
|
|
89
89
|
|
|
@@ -96,7 +96,15 @@ new Blok({ holder: 'editor', uploader: indexedDBStorage() });
|
|
|
96
96
|
const displayUrl = await resolveBlokObjectUrl(asset.url);
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
**Not for production.** This preset stores uploaded bytes in the visitor's own browser via IndexedDB. They are gone on another device, in another browser, or the moment the user clears site data
|
|
99
|
+
**Not for production.** This preset stores uploaded bytes in the visitor's own browser via IndexedDB. They are gone on another device, in another browser, or the moment the user clears site data. Nothing is actually shared or backed up.
|
|
100
|
+
|
|
101
|
+
It exists because Blok's built-in fallback (a `blob:` URL) does not even survive a page reload. Before you wire up real storage, that makes local demos and prototypes look broken.
|
|
102
|
+
|
|
103
|
+
`uploadByFile` returns a `blok:asset/…` reference, not a directly usable URL. Resolve it with `resolveBlokObjectUrl` wherever you render an uploaded asset.
|
|
104
|
+
|
|
105
|
+
If you pass a custom `dbName` to `indexedDBStorage()`, pass the same `dbName` to `resolveBlokObjectUrl(url, { dbName })`. A mismatch resolves to `null` with no error, not a thrown exception.
|
|
106
|
+
|
|
107
|
+
Each call to `resolveBlokObjectUrl` mints a fresh `blob:` URL. Nothing is cached, and nothing is revoked. Call `URL.revokeObjectURL()` yourself once a displayed asset is no longer needed. Otherwise a view that re-renders it repeatedly will leak memory.
|
|
100
108
|
|
|
101
109
|
## Docs
|
|
102
110
|
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){return new Promise((t,n)=>{var r;let i=new XMLHttpRequest;i.open(e.method,e.url);for(let[t,n]of Object.entries((r=e.headers)==null?{}:r))i.setRequestHeader(t,n);e.onProgress&&(i.upload.onprogress=t=>{var n;!t.lengthComputable||t.total===0||(n=e.onProgress)==null||n.call(e,Math.round(t.loaded/t.total*100))}),i.onload=()=>t({status:i.status,text:i.responseText}),i.onerror=()=>n(Error(`Upload failed: network error`)),i.send(e.body)})}function t(e){"@babel/helpers - typeof";return t=typeof Symbol==`function`&&typeof Symbol.iterator==`symbol`?function(e){return typeof e}:function(e){return e&&typeof Symbol==`function`&&e.constructor===Symbol&&e!==Symbol.prototype?`symbol`:typeof e},t(e)}function n(e,n){if(t(e)!=`object`||!e)return e;var r=e[Symbol.toPrimitive];if(r!==void 0){var i=r.call(e,n||`default`);if(t(i)!=`object`)return i;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(n===`string`?String:Number)(e)}function r(e){var r=n(e,`string`);return t(r)==`symbol`?r:r+``}function i(e,t,n){return(t=r(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]==null?{}:arguments[t];t%2?a(Object(n),!0).forEach(function(t){i(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):a(Object(n)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}function s(t){var n;let r=t.baseUrl
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){return new Promise((t,n)=>{var r;let i=new XMLHttpRequest;i.open(e.method,e.url);for(let[t,n]of Object.entries((r=e.headers)==null?{}:r))i.setRequestHeader(t,n);e.onProgress&&(i.upload.onprogress=t=>{var n;!t.lengthComputable||t.total===0||(n=e.onProgress)==null||n.call(e,Math.round(t.loaded/t.total*100))}),i.onload=()=>t({status:i.status,text:i.responseText}),i.onerror=()=>n(Error(`Upload failed: network error`)),i.send(e.body)})}function t(e){"@babel/helpers - typeof";return t=typeof Symbol==`function`&&typeof Symbol.iterator==`symbol`?function(e){return typeof e}:function(e){return e&&typeof Symbol==`function`&&e.constructor===Symbol&&e!==Symbol.prototype?`symbol`:typeof e},t(e)}function n(e,n){if(t(e)!=`object`||!e)return e;var r=e[Symbol.toPrimitive];if(r!==void 0){var i=r.call(e,n||`default`);if(t(i)!=`object`)return i;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(n===`string`?String:Number)(e)}function r(e){var r=n(e,`string`);return t(r)==`symbol`?r:r+``}function i(e,t,n){return(t=r(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]==null?{}:arguments[t];t%2?a(Object(n),!0).forEach(function(t){i(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):a(Object(n)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}function s(e){let t=Array.from(e).reduce((e,t)=>t===`/`?e+1:0,0);return e.slice(0,e.length-t)}function c(t){var n;let r=s(t.baseUrl),i=(n=t.field)==null?`file`:n,a=async()=>{var e;return typeof t.headers==`function`?t.headers():(e=t.headers)==null?{}:e};return{async uploadByFile(t,n){let o=new FormData;o.append(i,t);let{status:s,text:c}=await e({method:`POST`,url:`${r}/upload`,body:o,headers:await a(),onProgress:n.onProgress});if(s<200||s>299)throw Error(`Fetch endpoint upload failed with status ${s}`);return l(c,t.name)},async uploadByUrl(e,t){let n=await fetch(`${r}/upload-by-url`,{method:`POST`,headers:o(o({},await a()),{},{"Content-Type":`application/json`}),body:JSON.stringify({url:e})});if(!n.ok)throw Error(`Fetch endpoint upload failed with status ${n.status}`);return d(u(await n.text()))},async delete(e,t){let n=await fetch(`${r}/delete`,{method:`POST`,headers:o(o({},await a()),{},{"Content-Type":`application/json`}),body:JSON.stringify({url:e})});if(!n.ok)throw Error(`Fetch endpoint delete failed with status ${n.status}`)}}}function l(e,t){var n;let r=d(u(e));return{url:r.url,fileName:(n=r.fileName)==null?t:n}}function u(e){try{return JSON.parse(e)}catch(e){throw Error(`Upload failed: the endpoint returned a malformed response`)}}function d(e){if(typeof e!=`object`||!e||typeof e.url!=`string`)throw Error(`Upload failed: the endpoint returned no url`);let t=e;return{url:t.url,fileName:typeof t.fileName==`string`?t.fileName:void 0}}function f(e){return c(e)}function p(e,t={}){let n=e=>{var n;return typeof t.bucket==`function`?t.bucket(e):(n=t.bucket)==null?`blok`:n};return{async uploadByFile(r,i){var a,o;let s=(a=(o=t.path)==null?void 0:o.call(t,r,i))==null?m(r.name):a,{data:c,error:l}=await e.storage.from(n(i.kind)).upload(s,r,{contentType:r.type||void 0,upsert:!1});if(l!==null||c===null){var u;throw Error(`Supabase upload failed: ${(u=l==null?void 0:l.message)==null?`unknown error`:u}`)}let{data:d}=e.storage.from(n(i.kind)).getPublicUrl(c.path);return{url:d.publicUrl,fileName:r.name}}}}function m(e){let t=/\.[a-z0-9]+$/i.exec(e),n=t?t[0].toLowerCase():``;return`${crypto.randomUUID()}${n}`}function h(t){return{async uploadByFile(n,r){let i=await t.sign({fileName:n.name,mimeType:n.type,size:n.size,kind:r.kind}),{status:a}=await e({method:`PUT`,url:i.uploadUrl,body:n,headers:o(o({},i.headers),{},{"Content-Type":n.type||`application/octet-stream`}),onProgress:r.onProgress});if(a<200||a>299)throw Error(`Presigned upload failed with status ${a}`);return{url:i.publicUrl,fileName:n.name}}}}var g={image:`image`,video:`video`,audio:`video`,file:`raw`};function _(t){let n=e=>{var n;return`https://api.cloudinary.com/v1_1/${t.cloudName}/${(n=g[e])==null?`raw`:n}/upload`},r=async(r,i,a)=>{let o=new FormData;o.append(`file`,i),o.append(`upload_preset`,t.uploadPreset),t.folder!==void 0&&o.append(`folder`,t.folder);let{status:s,text:c}=await e({method:`POST`,url:n(r),body:o,onProgress:a});return v(c,s,typeof i==`string`?void 0:i.name)};return{uploadByFile:(e,t)=>r(t.kind,e,t.onProgress),uploadByUrl:(e,t)=>r(t.kind,e,t.onProgress)}}function v(e,t,n){let r=y(e,t);if(typeof r.secure_url!=`string`){var i;let e=r.error;throw Error(`Cloudinary upload failed: ${(i=e==null?void 0:e.message)==null?`status ${t}`:i}`)}return{url:r.secure_url,fileName:n}}function y(e,t){try{return JSON.parse(e)}catch(e){throw Error(`Cloudinary upload failed with status ${t}`)}}var b=`blok-assets`,x=`assets`,S=`blok:asset/`;function C(e){return new Promise((t,n)=>{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{r.result.createObjectStore(x)},r.onsuccess=()=>t(r.result),r.onerror=()=>{var e;return n((e=r.error)==null?Error(`IndexedDB open failed`):e)}})}function w(e,t,n){return new Promise((r,i)=>{let a=n(e.transaction(x,t).objectStore(x));a.onsuccess=()=>r(a.result),a.onerror=()=>{var e;return i((e=a.error)==null?Error(`IndexedDB request failed`):e)}})}function T(e={}){var t;let n=(t=e.dbName)==null?b:t;return{async uploadByFile(e,t){let r=crypto.randomUUID(),i=await C(n);try{await w(i,`readwrite`,t=>t.put({blob:e,name:e.name,type:e.type},r))}finally{i.close()}return{url:`${S}${r}`,fileName:e.name}}}}async function E(e,t={}){var n;if(!e.startsWith(S))return e;let r=await D(await C((n=t.dbName)==null?b:n),e.slice(11));return r?URL.createObjectURL(r.blob):null}function D(e,t){return w(e,`readonly`,e=>e.get(t)).finally(()=>e.close())}exports.cloudinaryStorage=_,exports.fetchStorage=f,exports.indexedDBStorage=T,exports.presignedStorage=h,exports.resolveBlokObjectUrl=E,exports.supabaseStorage=p;
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/upload-xhr.ts
|
|
1
|
+
//#region ../../src/components/utils/upload-xhr.ts
|
|
2
2
|
function e(e) {
|
|
3
3
|
return new Promise((t, n) => {
|
|
4
4
|
var r;
|
|
@@ -15,7 +15,7 @@ function e(e) {
|
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
17
|
//#endregion
|
|
18
|
-
//#region \0@oxc-project+runtime@0.
|
|
18
|
+
//#region \0@oxc-project+runtime@0.147.0/helpers/esm/typeof.js
|
|
19
19
|
function t(e) {
|
|
20
20
|
"@babel/helpers - typeof";
|
|
21
21
|
return t = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(e) {
|
|
@@ -25,7 +25,7 @@ function t(e) {
|
|
|
25
25
|
}, t(e);
|
|
26
26
|
}
|
|
27
27
|
//#endregion
|
|
28
|
-
//#region \0@oxc-project+runtime@0.
|
|
28
|
+
//#region \0@oxc-project+runtime@0.147.0/helpers/esm/toPrimitive.js
|
|
29
29
|
function n(e, n) {
|
|
30
30
|
if (t(e) != "object" || !e) return e;
|
|
31
31
|
var r = e[Symbol.toPrimitive];
|
|
@@ -37,13 +37,13 @@ function n(e, n) {
|
|
|
37
37
|
return (n === "string" ? String : Number)(e);
|
|
38
38
|
}
|
|
39
39
|
//#endregion
|
|
40
|
-
//#region \0@oxc-project+runtime@0.
|
|
40
|
+
//#region \0@oxc-project+runtime@0.147.0/helpers/esm/toPropertyKey.js
|
|
41
41
|
function r(e) {
|
|
42
42
|
var r = n(e, "string");
|
|
43
43
|
return t(r) == "symbol" ? r : r + "";
|
|
44
44
|
}
|
|
45
45
|
//#endregion
|
|
46
|
-
//#region \0@oxc-project+runtime@0.
|
|
46
|
+
//#region \0@oxc-project+runtime@0.147.0/helpers/esm/defineProperty.js
|
|
47
47
|
function i(e, t, n) {
|
|
48
48
|
return (t = r(t)) in e ? Object.defineProperty(e, t, {
|
|
49
49
|
value: n,
|
|
@@ -53,7 +53,7 @@ function i(e, t, n) {
|
|
|
53
53
|
}) : e[t] = n, e;
|
|
54
54
|
}
|
|
55
55
|
//#endregion
|
|
56
|
-
//#region \0@oxc-project+runtime@0.
|
|
56
|
+
//#region \0@oxc-project+runtime@0.147.0/helpers/esm/objectSpread2.js
|
|
57
57
|
function a(e, t) {
|
|
58
58
|
var n = Object.keys(e);
|
|
59
59
|
if (Object.getOwnPropertySymbols) {
|
|
@@ -76,10 +76,14 @@ function o(e) {
|
|
|
76
76
|
return e;
|
|
77
77
|
}
|
|
78
78
|
//#endregion
|
|
79
|
-
//#region src/fetch-
|
|
80
|
-
function s(
|
|
79
|
+
//#region ../../src/components/utils/fetch-uploader.ts
|
|
80
|
+
function s(e) {
|
|
81
|
+
let t = Array.from(e).reduce((e, t) => t === "/" ? e + 1 : 0, 0);
|
|
82
|
+
return e.slice(0, e.length - t);
|
|
83
|
+
}
|
|
84
|
+
function c(t) {
|
|
81
85
|
var n;
|
|
82
|
-
let r = t.baseUrl
|
|
86
|
+
let r = s(t.baseUrl), i = (n = t.field) == null ? "file" : n, a = async () => {
|
|
83
87
|
var e;
|
|
84
88
|
return typeof t.headers == "function" ? t.headers() : (e = t.headers) == null ? {} : e;
|
|
85
89
|
};
|
|
@@ -87,7 +91,7 @@ function s(t) {
|
|
|
87
91
|
async uploadByFile(t, n) {
|
|
88
92
|
let o = new FormData();
|
|
89
93
|
o.append(i, t);
|
|
90
|
-
let { status: s, text:
|
|
94
|
+
let { status: s, text: c } = await e({
|
|
91
95
|
method: "POST",
|
|
92
96
|
url: `${r}/upload`,
|
|
93
97
|
body: o,
|
|
@@ -95,7 +99,7 @@ function s(t) {
|
|
|
95
99
|
onProgress: n.onProgress
|
|
96
100
|
});
|
|
97
101
|
if (s < 200 || s > 299) throw Error(`Fetch endpoint upload failed with status ${s}`);
|
|
98
|
-
return c
|
|
102
|
+
return l(c, t.name);
|
|
99
103
|
},
|
|
100
104
|
async uploadByUrl(e, t) {
|
|
101
105
|
let n = await fetch(`${r}/upload-by-url`, {
|
|
@@ -104,26 +108,34 @@ function s(t) {
|
|
|
104
108
|
body: JSON.stringify({ url: e })
|
|
105
109
|
});
|
|
106
110
|
if (!n.ok) throw Error(`Fetch endpoint upload failed with status ${n.status}`);
|
|
107
|
-
return u(
|
|
111
|
+
return d(u(await n.text()));
|
|
112
|
+
},
|
|
113
|
+
async delete(e, t) {
|
|
114
|
+
let n = await fetch(`${r}/delete`, {
|
|
115
|
+
method: "POST",
|
|
116
|
+
headers: o(o({}, await a()), {}, { "Content-Type": "application/json" }),
|
|
117
|
+
body: JSON.stringify({ url: e })
|
|
118
|
+
});
|
|
119
|
+
if (!n.ok) throw Error(`Fetch endpoint delete failed with status ${n.status}`);
|
|
108
120
|
}
|
|
109
121
|
};
|
|
110
122
|
}
|
|
111
|
-
function
|
|
123
|
+
function l(e, t) {
|
|
112
124
|
var n;
|
|
113
|
-
let r = u(
|
|
125
|
+
let r = d(u(e));
|
|
114
126
|
return {
|
|
115
127
|
url: r.url,
|
|
116
128
|
fileName: (n = r.fileName) == null ? t : n
|
|
117
129
|
};
|
|
118
130
|
}
|
|
119
|
-
function
|
|
131
|
+
function u(e) {
|
|
120
132
|
try {
|
|
121
133
|
return JSON.parse(e);
|
|
122
134
|
} catch (e) {
|
|
123
135
|
throw Error("Upload failed: the endpoint returned a malformed response");
|
|
124
136
|
}
|
|
125
137
|
}
|
|
126
|
-
function
|
|
138
|
+
function d(e) {
|
|
127
139
|
if (typeof e != "object" || !e || typeof e.url != "string") throw Error("Upload failed: the endpoint returned no url");
|
|
128
140
|
let t = e;
|
|
129
141
|
return {
|
|
@@ -132,15 +144,20 @@ function u(e) {
|
|
|
132
144
|
};
|
|
133
145
|
}
|
|
134
146
|
//#endregion
|
|
147
|
+
//#region src/fetch-endpoint.ts
|
|
148
|
+
function f(e) {
|
|
149
|
+
return c(e);
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
135
152
|
//#region src/supabase.ts
|
|
136
|
-
function
|
|
153
|
+
function p(e, t = {}) {
|
|
137
154
|
let n = (e) => {
|
|
138
155
|
var n;
|
|
139
156
|
return typeof t.bucket == "function" ? t.bucket(e) : (n = t.bucket) == null ? "blok" : n;
|
|
140
157
|
};
|
|
141
158
|
return { async uploadByFile(r, i) {
|
|
142
159
|
var a, o;
|
|
143
|
-
let s = (a = (o = t.path) == null ? void 0 : o.call(t, r, i)) == null ?
|
|
160
|
+
let s = (a = (o = t.path) == null ? void 0 : o.call(t, r, i)) == null ? m(r.name) : a, { data: c, error: l } = await e.storage.from(n(i.kind)).upload(s, r, {
|
|
144
161
|
contentType: r.type || void 0,
|
|
145
162
|
upsert: !1
|
|
146
163
|
});
|
|
@@ -155,13 +172,13 @@ function d(e, t = {}) {
|
|
|
155
172
|
};
|
|
156
173
|
} };
|
|
157
174
|
}
|
|
158
|
-
function
|
|
175
|
+
function m(e) {
|
|
159
176
|
let t = /\.[a-z0-9]+$/i.exec(e), n = t ? t[0].toLowerCase() : "";
|
|
160
177
|
return `${crypto.randomUUID()}${n}`;
|
|
161
178
|
}
|
|
162
179
|
//#endregion
|
|
163
180
|
//#region src/presigned.ts
|
|
164
|
-
function
|
|
181
|
+
function h(t) {
|
|
165
182
|
return { async uploadByFile(n, r) {
|
|
166
183
|
let i = await t.sign({
|
|
167
184
|
fileName: n.name,
|
|
@@ -184,16 +201,16 @@ function p(t) {
|
|
|
184
201
|
}
|
|
185
202
|
//#endregion
|
|
186
203
|
//#region src/cloudinary.ts
|
|
187
|
-
var
|
|
204
|
+
var g = {
|
|
188
205
|
image: "image",
|
|
189
206
|
video: "video",
|
|
190
207
|
audio: "video",
|
|
191
208
|
file: "raw"
|
|
192
209
|
};
|
|
193
|
-
function
|
|
210
|
+
function _(t) {
|
|
194
211
|
let n = (e) => {
|
|
195
212
|
var n;
|
|
196
|
-
return `https://api.cloudinary.com/v1_1/${t.cloudName}/${(n =
|
|
213
|
+
return `https://api.cloudinary.com/v1_1/${t.cloudName}/${(n = g[e]) == null ? "raw" : n}/upload`;
|
|
197
214
|
}, r = async (r, i, a) => {
|
|
198
215
|
let o = new FormData();
|
|
199
216
|
o.append("file", i), o.append("upload_preset", t.uploadPreset), t.folder !== void 0 && o.append("folder", t.folder);
|
|
@@ -203,15 +220,15 @@ function h(t) {
|
|
|
203
220
|
body: o,
|
|
204
221
|
onProgress: a
|
|
205
222
|
});
|
|
206
|
-
return
|
|
223
|
+
return v(c, s, typeof i == "string" ? void 0 : i.name);
|
|
207
224
|
};
|
|
208
225
|
return {
|
|
209
226
|
uploadByFile: (e, t) => r(t.kind, e, t.onProgress),
|
|
210
227
|
uploadByUrl: (e, t) => r(t.kind, e, t.onProgress)
|
|
211
228
|
};
|
|
212
229
|
}
|
|
213
|
-
function
|
|
214
|
-
let r =
|
|
230
|
+
function v(e, t, n) {
|
|
231
|
+
let r = y(e, t);
|
|
215
232
|
if (typeof r.secure_url != "string") {
|
|
216
233
|
var i;
|
|
217
234
|
let e = r.error;
|
|
@@ -222,7 +239,7 @@ function g(e, t, n) {
|
|
|
222
239
|
fileName: n
|
|
223
240
|
};
|
|
224
241
|
}
|
|
225
|
-
function
|
|
242
|
+
function y(e, t) {
|
|
226
243
|
try {
|
|
227
244
|
return JSON.parse(e);
|
|
228
245
|
} catch (e) {
|
|
@@ -231,34 +248,34 @@ function _(e, t) {
|
|
|
231
248
|
}
|
|
232
249
|
//#endregion
|
|
233
250
|
//#region src/indexeddb.ts
|
|
234
|
-
var
|
|
235
|
-
function
|
|
251
|
+
var b = "blok-assets", x = "assets", S = "blok:asset/";
|
|
252
|
+
function C(e) {
|
|
236
253
|
return new Promise((t, n) => {
|
|
237
254
|
let r = indexedDB.open(e, 1);
|
|
238
255
|
r.onupgradeneeded = () => {
|
|
239
|
-
r.result.createObjectStore(
|
|
256
|
+
r.result.createObjectStore(x);
|
|
240
257
|
}, r.onsuccess = () => t(r.result), r.onerror = () => {
|
|
241
258
|
var e;
|
|
242
259
|
return n((e = r.error) == null ? /* @__PURE__ */ Error("IndexedDB open failed") : e);
|
|
243
260
|
};
|
|
244
261
|
});
|
|
245
262
|
}
|
|
246
|
-
function
|
|
263
|
+
function w(e, t, n) {
|
|
247
264
|
return new Promise((r, i) => {
|
|
248
|
-
let a = n(e.transaction(
|
|
265
|
+
let a = n(e.transaction(x, t).objectStore(x));
|
|
249
266
|
a.onsuccess = () => r(a.result), a.onerror = () => {
|
|
250
267
|
var e;
|
|
251
268
|
return i((e = a.error) == null ? /* @__PURE__ */ Error("IndexedDB request failed") : e);
|
|
252
269
|
};
|
|
253
270
|
});
|
|
254
271
|
}
|
|
255
|
-
function
|
|
272
|
+
function T(e = {}) {
|
|
256
273
|
var t;
|
|
257
|
-
let n = (t = e.dbName) == null ?
|
|
274
|
+
let n = (t = e.dbName) == null ? b : t;
|
|
258
275
|
return { async uploadByFile(e, t) {
|
|
259
|
-
let r = crypto.randomUUID(), i = await
|
|
276
|
+
let r = crypto.randomUUID(), i = await C(n);
|
|
260
277
|
try {
|
|
261
|
-
await
|
|
278
|
+
await w(i, "readwrite", (t) => t.put({
|
|
262
279
|
blob: e,
|
|
263
280
|
name: e.name,
|
|
264
281
|
type: e.type
|
|
@@ -267,19 +284,19 @@ function C(e = {}) {
|
|
|
267
284
|
i.close();
|
|
268
285
|
}
|
|
269
286
|
return {
|
|
270
|
-
url: `${
|
|
287
|
+
url: `${S}${r}`,
|
|
271
288
|
fileName: e.name
|
|
272
289
|
};
|
|
273
290
|
} };
|
|
274
291
|
}
|
|
275
|
-
async function
|
|
292
|
+
async function E(e, t = {}) {
|
|
276
293
|
var n;
|
|
277
|
-
if (!e.startsWith(
|
|
278
|
-
let r = await
|
|
294
|
+
if (!e.startsWith(S)) return e;
|
|
295
|
+
let r = await D(await C((n = t.dbName) == null ? b : n), e.slice(11));
|
|
279
296
|
return r ? URL.createObjectURL(r.blob) : null;
|
|
280
297
|
}
|
|
281
|
-
function
|
|
282
|
-
return
|
|
298
|
+
function D(e, t) {
|
|
299
|
+
return w(e, "readonly", (e) => e.get(t)).finally(() => e.close());
|
|
283
300
|
}
|
|
284
301
|
//#endregion
|
|
285
|
-
export {
|
|
302
|
+
export { _ as cloudinaryStorage, f as fetchStorage, T as indexedDBStorage, h as presignedStorage, E as resolveBlokObjectUrl, p as supabaseStorage };
|
package/package.json
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -45,14 +45,29 @@ interface UploadContext {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
48
|
+
* Describes a single deletion request.
|
|
49
|
+
*
|
|
50
|
+
* The same `kind`/`tool` pair {@link UploadContext} carries, minus progress —
|
|
51
|
+
* a deletion has nothing to report but its outcome.
|
|
52
|
+
*/
|
|
53
|
+
interface DeleteContext {
|
|
54
|
+
/** Kind of asset being deleted. */
|
|
55
|
+
kind: AssetKind;
|
|
56
|
+
/** Name of the block tool that owned the asset, when known. */
|
|
57
|
+
tool?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Editor-level uploader, keyed by asset kind rather than by tool. Every
|
|
62
|
+
* method is optional and resolved independently.
|
|
50
63
|
*/
|
|
51
64
|
interface BlokUploader {
|
|
52
65
|
/** Store a file chosen by the user and return its public URL. */
|
|
53
66
|
uploadByFile?(file: File, ctx: UploadContext): Promise<UploadedAsset>;
|
|
54
67
|
/** Re-host an asset the user supplied by URL, and return the stored URL. */
|
|
55
68
|
uploadByUrl?(url: string, ctx: UploadContext): Promise<UploadedAsset>;
|
|
69
|
+
/** Delete a stored asset by the URL this uploader returned for it. */
|
|
70
|
+
delete?(url: string, ctx: DeleteContext): Promise<void>;
|
|
56
71
|
}
|
|
57
72
|
|
|
58
73
|
export interface FetchStorageOptions {
|