@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.
Files changed (2) hide show
  1. package/README.md +17 -9
  2. 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) — plug in storage you already have instead of hand-writing an upload handler. Five presets ship today: Supabase, S3-compatible storage (presigned URLs), Cloudinary, a generic fetch endpoint, and IndexedDB.
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 — store the file, return where it landed:
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 that doesn't survive a reload — most projects need to configure one. Each preset in this package is a ready-made implementation of that shape for one storage backend, so you import a preset instead of writing `uploadByFile` yourself.
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: both have something that fetches the URL on the server side (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, so Blok falls back to its documented behavior and stores the pasted URL verbatim.
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; this preset just calls it. It must answer `POST {baseUrl}/upload` (multipart) and `POST {baseUrl}/upload-by-url` (`{ url }` JSON), both returning `{ url, fileName? }`. The URL you receive on `/upload-by-url` is user-supplied — validate it server-side and block requests to internal/private addresses before fetching it, or the endpoint becomes an SSRF vector.
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"`), make it public or add an anon `SELECT` policy — this preset returns `getPublicUrl()` directly, so an unreadable object is a broken image — and add an `INSERT` policy for whichever role the client authenticates as.
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: 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.
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, which defeats the point of a no-backend preset.
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 — nothing is actually shared or backed up. It exists because Blok's built-in fallback (a `blob:` URL) doesn't even survive a page reload, which makes local demos and prototypes look broken before you've wired up real storage. `uploadByFile` returns a `blok:asset/…` reference, not a directly usable URL — resolve it with `resolveBlokObjectUrl` wherever you render an uploaded asset. 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. 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, or a view that re-renders it repeatedly will leak memory.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bloklabs/presets",
3
- "version": "1.13.0",
3
+ "version": "1.14.0",
4
4
  "description": "Storage presets for Blok — ready-made uploaders for Supabase, S3-compatible storage, Cloudinary, and IndexedDB.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "JackUait",