@bloklabs/presets 1.13.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/package.json +1 -1
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/package.json
CHANGED