@btst/stack 2.8.1 → 2.9.1
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 +3 -2
- package/dist/components/markdown/index.d.cts +15 -2
- package/dist/components/markdown/index.d.mts +15 -2
- package/dist/components/markdown/index.d.ts +15 -2
- package/dist/packages/stack/src/plugins/blog/client/components/forms/image-field.cjs +30 -1
- package/dist/packages/stack/src/plugins/blog/client/components/forms/image-field.mjs +30 -1
- package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor-with-overrides.cjs +49 -9
- package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor-with-overrides.mjs +50 -10
- package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor.cjs +77 -9
- package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor.mjs +77 -9
- package/dist/packages/stack/src/plugins/cms/client/components/forms/content-form.cjs +24 -5
- package/dist/packages/stack/src/plugins/cms/client/components/forms/content-form.mjs +24 -5
- package/dist/packages/stack/src/plugins/cms/client/components/forms/file-upload.cjs +47 -13
- package/dist/packages/stack/src/plugins/cms/client/components/forms/file-upload.mjs +47 -13
- package/dist/packages/stack/src/plugins/kanban/client/components/forms/board-form.cjs +1 -1
- package/dist/packages/stack/src/plugins/kanban/client/components/forms/board-form.mjs +1 -1
- package/dist/packages/stack/src/plugins/kanban/client/components/forms/task-form.cjs +6 -2
- package/dist/packages/stack/src/plugins/kanban/client/components/forms/task-form.mjs +6 -2
- package/dist/packages/stack/src/plugins/media/api/adapters/local.cjs +55 -0
- package/dist/packages/stack/src/plugins/media/api/adapters/local.mjs +37 -0
- package/dist/packages/stack/src/plugins/media/api/getters.cjs +83 -0
- package/dist/packages/stack/src/plugins/media/api/getters.mjs +78 -0
- package/dist/packages/stack/src/plugins/media/api/mutations.cjs +88 -0
- package/dist/packages/stack/src/plugins/media/api/mutations.mjs +82 -0
- package/dist/packages/stack/src/plugins/media/api/plugin.cjs +525 -0
- package/dist/packages/stack/src/plugins/media/api/plugin.mjs +523 -0
- package/dist/packages/stack/src/plugins/media/api/query-key-defs.cjs +19 -0
- package/dist/packages/stack/src/plugins/media/api/query-key-defs.mjs +16 -0
- package/dist/packages/stack/src/plugins/media/api/serializers.cjs +17 -0
- package/dist/packages/stack/src/plugins/media/api/serializers.mjs +14 -0
- package/dist/packages/stack/src/plugins/media/api/storage-adapter.cjs +15 -0
- package/dist/packages/stack/src/plugins/media/api/storage-adapter.mjs +11 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-card.cjs +129 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-card.mjs +127 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-preview-button.cjs +58 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-preview-button.mjs +56 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/browse-tab.cjs +94 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/browse-tab.mjs +92 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/folder-tree.cjs +171 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/folder-tree.mjs +168 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/index.cjs +308 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/index.mjs +305 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/upload-tab.cjs +104 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/upload-tab.mjs +102 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/url-tab.cjs +70 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/url-tab.mjs +68 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/utils.cjs +21 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/utils.mjs +17 -0
- package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.cjs +35 -0
- package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.internal.cjs +125 -0
- package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.internal.mjs +123 -0
- package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.mjs +33 -0
- package/dist/packages/stack/src/plugins/media/client/hooks/use-media.cjs +222 -0
- package/dist/packages/stack/src/plugins/media/client/hooks/use-media.mjs +214 -0
- package/dist/packages/stack/src/plugins/media/client/plugin.cjs +94 -0
- package/dist/packages/stack/src/plugins/media/client/plugin.mjs +92 -0
- package/dist/packages/stack/src/plugins/media/client/upload.cjs +121 -0
- package/dist/packages/stack/src/plugins/media/client/upload.mjs +119 -0
- package/dist/packages/stack/src/plugins/media/client/utils/image-compression.cjs +67 -0
- package/dist/packages/stack/src/plugins/media/client/utils/image-compression.mjs +65 -0
- package/dist/packages/stack/src/plugins/media/db.cjs +62 -0
- package/dist/packages/stack/src/plugins/media/db.mjs +60 -0
- package/dist/packages/stack/src/plugins/media/schemas.cjs +41 -0
- package/dist/packages/stack/src/plugins/media/schemas.mjs +35 -0
- package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-block.cjs +18 -1
- package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-block.mjs +19 -2
- package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-dialog.cjs +2 -2
- package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-dialog.mjs +2 -2
- package/dist/packages/ui/src/components/minimal-tiptap/components/section/five.cjs +3 -2
- package/dist/packages/ui/src/components/minimal-tiptap/components/section/five.mjs +3 -2
- package/dist/packages/ui/src/components/minimal-tiptap/minimal-tiptap.cjs +12 -5
- package/dist/packages/ui/src/components/minimal-tiptap/minimal-tiptap.mjs +12 -5
- package/dist/plugins/blog/client/index.d.cts +58 -1
- package/dist/plugins/blog/client/index.d.mts +58 -1
- package/dist/plugins/blog/client/index.d.ts +58 -1
- package/dist/plugins/cms/client/index.d.cts +73 -3
- package/dist/plugins/cms/client/index.d.mts +73 -3
- package/dist/plugins/cms/client/index.d.ts +73 -3
- package/dist/plugins/kanban/api/index.d.cts +1 -1
- package/dist/plugins/kanban/api/index.d.mts +1 -1
- package/dist/plugins/kanban/api/index.d.ts +1 -1
- package/dist/plugins/kanban/client/hooks/index.d.cts +1 -1
- package/dist/plugins/kanban/client/hooks/index.d.mts +1 -1
- package/dist/plugins/kanban/client/hooks/index.d.ts +1 -1
- package/dist/plugins/kanban/client/index.d.cts +1 -1
- package/dist/plugins/kanban/client/index.d.mts +1 -1
- package/dist/plugins/kanban/client/index.d.ts +1 -1
- package/dist/plugins/kanban/query-keys.d.cts +1 -1
- package/dist/plugins/kanban/query-keys.d.mts +1 -1
- package/dist/plugins/kanban/query-keys.d.ts +1 -1
- package/dist/plugins/media/api/adapters/s3.cjs +106 -0
- package/dist/plugins/media/api/adapters/s3.d.cts +60 -0
- package/dist/plugins/media/api/adapters/s3.d.mts +60 -0
- package/dist/plugins/media/api/adapters/s3.d.ts +60 -0
- package/dist/plugins/media/api/adapters/s3.mjs +104 -0
- package/dist/plugins/media/api/adapters/vercel-blob.cjs +53 -0
- package/dist/plugins/media/api/adapters/vercel-blob.d.cts +41 -0
- package/dist/plugins/media/api/adapters/vercel-blob.d.mts +41 -0
- package/dist/plugins/media/api/adapters/vercel-blob.d.ts +41 -0
- package/dist/plugins/media/api/adapters/vercel-blob.mjs +51 -0
- package/dist/plugins/media/api/index.cjs +26 -0
- package/dist/plugins/media/api/index.d.cts +116 -0
- package/dist/plugins/media/api/index.d.mts +116 -0
- package/dist/plugins/media/api/index.d.ts +116 -0
- package/dist/plugins/media/api/index.mjs +6 -0
- package/dist/plugins/media/client/components/index.cjs +10 -0
- package/dist/plugins/media/client/components/index.d.cts +55 -0
- package/dist/plugins/media/client/components/index.d.mts +55 -0
- package/dist/plugins/media/client/components/index.d.ts +55 -0
- package/dist/plugins/media/client/components/index.mjs +2 -0
- package/dist/plugins/media/client/hooks/index.cjs +13 -0
- package/dist/plugins/media/client/hooks/index.d.cts +53 -0
- package/dist/plugins/media/client/hooks/index.d.mts +53 -0
- package/dist/plugins/media/client/hooks/index.d.ts +53 -0
- package/dist/plugins/media/client/hooks/index.mjs +1 -0
- package/dist/plugins/media/client/index.cjs +9 -0
- package/dist/plugins/media/client/index.d.cts +242 -0
- package/dist/plugins/media/client/index.d.mts +242 -0
- package/dist/plugins/media/client/index.d.ts +242 -0
- package/dist/plugins/media/client/index.mjs +2 -0
- package/dist/plugins/media/client.css +1 -0
- package/dist/plugins/media/query-keys.cjs +72 -0
- package/dist/plugins/media/query-keys.d.cts +49 -0
- package/dist/plugins/media/query-keys.d.mts +49 -0
- package/dist/plugins/media/query-keys.d.ts +49 -0
- package/dist/plugins/media/query-keys.mjs +70 -0
- package/dist/plugins/media/style.css +1 -0
- package/dist/shared/{stack.DRpeDS6X.d.ts → stack.BMx2QYOK.d.ts} +25 -0
- package/dist/shared/stack.BttDsJJn.d.cts +109 -0
- package/dist/shared/stack.BttDsJJn.d.mts +109 -0
- package/dist/shared/stack.BttDsJJn.d.ts +109 -0
- package/dist/shared/stack.C7vfOBmO.d.mts +63 -0
- package/dist/shared/stack.CAni8dnD.d.cts +63 -0
- package/dist/shared/stack.CI8iRKKi.d.cts +286 -0
- package/dist/shared/stack.CLcnSF_b.d.cts +25 -0
- package/dist/shared/stack.CLcnSF_b.d.mts +25 -0
- package/dist/shared/stack.CLcnSF_b.d.ts +25 -0
- package/dist/shared/stack.CYSwntXC.d.ts +63 -0
- package/dist/shared/{stack.Jb0kQDJC.d.mts → stack.Cd6McBu1.d.mts} +25 -0
- package/dist/shared/stack.DJDjdG64.d.ts +286 -0
- package/dist/shared/{stack.BxFl46lB.d.cts → stack.DxQl8Wa1.d.cts} +25 -0
- package/dist/shared/stack.FgBVDSPi.d.mts +286 -0
- package/package.json +113 -4
- package/src/plugins/blog/client/components/forms/image-field.tsx +35 -4
- package/src/plugins/blog/client/components/forms/markdown-editor-with-overrides.tsx +67 -12
- package/src/plugins/blog/client/components/forms/markdown-editor.tsx +106 -10
- package/src/plugins/blog/client/overrides.ts +58 -1
- package/src/plugins/cms/client/components/forms/content-form.tsx +26 -7
- package/src/plugins/cms/client/components/forms/file-upload.tsx +73 -15
- package/src/plugins/cms/client/overrides.ts +57 -2
- package/src/plugins/kanban/client/components/forms/board-form.tsx +1 -1
- package/src/plugins/kanban/client/components/forms/task-form.tsx +7 -1
- package/src/plugins/kanban/client/overrides.ts +25 -0
- package/src/plugins/media/__tests__/__stubs__/vercel-blob-server.ts +9 -0
- package/src/plugins/media/__tests__/getters.test.ts +274 -0
- package/src/plugins/media/__tests__/mutations.test.ts +299 -0
- package/src/plugins/media/__tests__/plugin.test.ts +752 -0
- package/src/plugins/media/__tests__/query-key-defs.test.ts +54 -0
- package/src/plugins/media/__tests__/storage-adapters.test.ts +351 -0
- package/src/plugins/media/api/adapters/local.ts +79 -0
- package/src/plugins/media/api/adapters/s3.ts +198 -0
- package/src/plugins/media/api/adapters/vercel-blob.ts +131 -0
- package/src/plugins/media/api/getters.ts +174 -0
- package/src/plugins/media/api/index.ts +41 -0
- package/src/plugins/media/api/mutations.ts +179 -0
- package/src/plugins/media/api/plugin.ts +855 -0
- package/src/plugins/media/api/query-key-defs.ts +41 -0
- package/src/plugins/media/api/serializers.ts +28 -0
- package/src/plugins/media/api/storage-adapter.ts +139 -0
- package/src/plugins/media/client/components/index.tsx +6 -0
- package/src/plugins/media/client/components/media-picker/asset-card.tsx +150 -0
- package/src/plugins/media/client/components/media-picker/asset-preview-button.tsx +67 -0
- package/src/plugins/media/client/components/media-picker/browse-tab.tsx +116 -0
- package/src/plugins/media/client/components/media-picker/folder-tree.tsx +188 -0
- package/src/plugins/media/client/components/media-picker/index.tsx +347 -0
- package/src/plugins/media/client/components/media-picker/upload-tab.tsx +108 -0
- package/src/plugins/media/client/components/media-picker/url-tab.tsx +72 -0
- package/src/plugins/media/client/components/media-picker/utils.ts +17 -0
- package/src/plugins/media/client/components/pages/library-page.internal.tsx +134 -0
- package/src/plugins/media/client/components/pages/library-page.tsx +42 -0
- package/src/plugins/media/client/hooks/index.tsx +9 -0
- package/src/plugins/media/client/hooks/use-media.tsx +289 -0
- package/src/plugins/media/client/index.ts +4 -0
- package/src/plugins/media/client/overrides.ts +127 -0
- package/src/plugins/media/client/plugin.tsx +184 -0
- package/src/plugins/media/client/upload.ts +171 -0
- package/src/plugins/media/client/utils/image-compression.ts +131 -0
- package/src/plugins/media/client.css +1 -0
- package/src/plugins/media/db.ts +62 -0
- package/src/plugins/media/query-keys.ts +96 -0
- package/src/plugins/media/schemas.ts +37 -0
- package/src/plugins/media/style.css +1 -0
- package/src/plugins/media/types.ts +26 -0
- package/dist/shared/{stack.BOokfhZD.d.cts → stack.B6S3cgwN.d.cts} +16 -16
- package/dist/shared/{stack.CWxAl9K3.d.mts → stack.Bzfx-_lq.d.mts} +16 -16
- package/dist/shared/{stack.BvCR4-9H.d.ts → stack.j5SFLC1d.d.ts} +16 -16
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { S as S3StorageAdapter } from '../../../../shared/stack.BttDsJJn.mjs';
|
|
2
|
+
|
|
3
|
+
interface S3StorageAdapterOptions {
|
|
4
|
+
/**
|
|
5
|
+
* The S3 bucket name.
|
|
6
|
+
*/
|
|
7
|
+
bucket: string;
|
|
8
|
+
/**
|
|
9
|
+
* AWS region (e.g. `"us-east-1"`).
|
|
10
|
+
*/
|
|
11
|
+
region: string;
|
|
12
|
+
/**
|
|
13
|
+
* AWS access key ID.
|
|
14
|
+
*/
|
|
15
|
+
accessKeyId: string;
|
|
16
|
+
/**
|
|
17
|
+
* AWS secret access key.
|
|
18
|
+
*/
|
|
19
|
+
secretAccessKey: string;
|
|
20
|
+
/**
|
|
21
|
+
* Custom endpoint URL for S3-compatible providers (Cloudflare R2, MinIO, etc.).
|
|
22
|
+
* @example "https://<account-id>.r2.cloudflarestorage.com"
|
|
23
|
+
*/
|
|
24
|
+
endpoint?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Base URL used to construct the final public asset URL after upload.
|
|
27
|
+
* @example "https://assets.example.com" or "https://pub-<id>.r2.dev"
|
|
28
|
+
*/
|
|
29
|
+
publicBaseUrl: string;
|
|
30
|
+
/**
|
|
31
|
+
* Duration in seconds for which the presigned URL is valid.
|
|
32
|
+
* @default 300 (5 minutes)
|
|
33
|
+
*/
|
|
34
|
+
expiresIn?: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Create an S3-compatible presigned-URL storage adapter.
|
|
38
|
+
* The server generates a short-lived presigned PUT URL; the browser uploads
|
|
39
|
+
* the file directly to S3 (or R2 / MinIO). The server never receives file bytes.
|
|
40
|
+
*
|
|
41
|
+
* @remarks Requires `@aws-sdk/client-s3` and `@aws-sdk/s3-request-presigner`
|
|
42
|
+
* as optional peer dependencies.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* mediaBackendPlugin({
|
|
47
|
+
* storageAdapter: s3Adapter({
|
|
48
|
+
* bucket: "my-bucket",
|
|
49
|
+
* region: "us-east-1",
|
|
50
|
+
* accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
51
|
+
* secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
52
|
+
* publicBaseUrl: "https://assets.example.com",
|
|
53
|
+
* })
|
|
54
|
+
* })
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
declare function s3Adapter(options: S3StorageAdapterOptions): S3StorageAdapter;
|
|
58
|
+
|
|
59
|
+
export { s3Adapter };
|
|
60
|
+
export type { S3StorageAdapterOptions };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { S as S3StorageAdapter } from '../../../../shared/stack.BttDsJJn.js';
|
|
2
|
+
|
|
3
|
+
interface S3StorageAdapterOptions {
|
|
4
|
+
/**
|
|
5
|
+
* The S3 bucket name.
|
|
6
|
+
*/
|
|
7
|
+
bucket: string;
|
|
8
|
+
/**
|
|
9
|
+
* AWS region (e.g. `"us-east-1"`).
|
|
10
|
+
*/
|
|
11
|
+
region: string;
|
|
12
|
+
/**
|
|
13
|
+
* AWS access key ID.
|
|
14
|
+
*/
|
|
15
|
+
accessKeyId: string;
|
|
16
|
+
/**
|
|
17
|
+
* AWS secret access key.
|
|
18
|
+
*/
|
|
19
|
+
secretAccessKey: string;
|
|
20
|
+
/**
|
|
21
|
+
* Custom endpoint URL for S3-compatible providers (Cloudflare R2, MinIO, etc.).
|
|
22
|
+
* @example "https://<account-id>.r2.cloudflarestorage.com"
|
|
23
|
+
*/
|
|
24
|
+
endpoint?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Base URL used to construct the final public asset URL after upload.
|
|
27
|
+
* @example "https://assets.example.com" or "https://pub-<id>.r2.dev"
|
|
28
|
+
*/
|
|
29
|
+
publicBaseUrl: string;
|
|
30
|
+
/**
|
|
31
|
+
* Duration in seconds for which the presigned URL is valid.
|
|
32
|
+
* @default 300 (5 minutes)
|
|
33
|
+
*/
|
|
34
|
+
expiresIn?: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Create an S3-compatible presigned-URL storage adapter.
|
|
38
|
+
* The server generates a short-lived presigned PUT URL; the browser uploads
|
|
39
|
+
* the file directly to S3 (or R2 / MinIO). The server never receives file bytes.
|
|
40
|
+
*
|
|
41
|
+
* @remarks Requires `@aws-sdk/client-s3` and `@aws-sdk/s3-request-presigner`
|
|
42
|
+
* as optional peer dependencies.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* mediaBackendPlugin({
|
|
47
|
+
* storageAdapter: s3Adapter({
|
|
48
|
+
* bucket: "my-bucket",
|
|
49
|
+
* region: "us-east-1",
|
|
50
|
+
* accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
51
|
+
* secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
52
|
+
* publicBaseUrl: "https://assets.example.com",
|
|
53
|
+
* })
|
|
54
|
+
* })
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
declare function s3Adapter(options: S3StorageAdapterOptions): S3StorageAdapter;
|
|
58
|
+
|
|
59
|
+
export { s3Adapter };
|
|
60
|
+
export type { S3StorageAdapterOptions };
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
function s3Adapter(options) {
|
|
2
|
+
const {
|
|
3
|
+
bucket,
|
|
4
|
+
region,
|
|
5
|
+
accessKeyId,
|
|
6
|
+
secretAccessKey,
|
|
7
|
+
endpoint,
|
|
8
|
+
publicBaseUrl,
|
|
9
|
+
expiresIn = 300
|
|
10
|
+
} = options;
|
|
11
|
+
let s3ModulePromise = null;
|
|
12
|
+
let clientPromise = null;
|
|
13
|
+
function getS3Module() {
|
|
14
|
+
if (!s3ModulePromise) {
|
|
15
|
+
s3ModulePromise = import('@aws-sdk/client-s3').catch(() => {
|
|
16
|
+
s3ModulePromise = null;
|
|
17
|
+
throw new Error(
|
|
18
|
+
"[@btst/stack] S3 adapter requires '@aws-sdk/client-s3'. Run: npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner"
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return s3ModulePromise;
|
|
23
|
+
}
|
|
24
|
+
function getClient() {
|
|
25
|
+
if (!clientPromise) {
|
|
26
|
+
clientPromise = getS3Module().then(({ S3Client }) => {
|
|
27
|
+
return new S3Client({
|
|
28
|
+
region,
|
|
29
|
+
endpoint,
|
|
30
|
+
credentials: { accessKeyId, secretAccessKey },
|
|
31
|
+
forcePathStyle: !!endpoint
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return clientPromise.catch((error) => {
|
|
36
|
+
if (clientPromise) {
|
|
37
|
+
clientPromise = null;
|
|
38
|
+
}
|
|
39
|
+
if (s3ModulePromise && error instanceof Error) {
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
throw new Error(
|
|
43
|
+
"[@btst/stack] S3 adapter requires '@aws-sdk/client-s3'. Run: npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner"
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async function buildSignedUrl(client, command, opts) {
|
|
48
|
+
let getSignedUrl;
|
|
49
|
+
try {
|
|
50
|
+
({ getSignedUrl } = await import('@aws-sdk/s3-request-presigner'));
|
|
51
|
+
} catch {
|
|
52
|
+
throw new Error(
|
|
53
|
+
"[@btst/stack] S3 adapter requires '@aws-sdk/s3-request-presigner'. Run: npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner"
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return getSignedUrl(
|
|
57
|
+
client,
|
|
58
|
+
command,
|
|
59
|
+
opts
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
type: "s3",
|
|
64
|
+
urlPrefix: publicBaseUrl.replace(/\/$/, ""),
|
|
65
|
+
async generateUploadToken(uploadOptions) {
|
|
66
|
+
const [client, { PutObjectCommand }] = await Promise.all([
|
|
67
|
+
getClient(),
|
|
68
|
+
getS3Module()
|
|
69
|
+
]);
|
|
70
|
+
const key = uploadOptions.folderId ? `${uploadOptions.folderId}/${uploadOptions.filename}` : uploadOptions.filename;
|
|
71
|
+
const command = new PutObjectCommand({
|
|
72
|
+
Bucket: bucket,
|
|
73
|
+
Key: key,
|
|
74
|
+
ContentType: uploadOptions.mimeType,
|
|
75
|
+
ContentLength: uploadOptions.size
|
|
76
|
+
});
|
|
77
|
+
const uploadUrl = await buildSignedUrl(client, command, { expiresIn });
|
|
78
|
+
const encodedKey = key.split("/").map(encodeURIComponent).join("/");
|
|
79
|
+
const publicUrl = `${publicBaseUrl.replace(/\/$/, "")}/${encodedKey}`;
|
|
80
|
+
return {
|
|
81
|
+
type: "presigned-url",
|
|
82
|
+
payload: {
|
|
83
|
+
uploadUrl,
|
|
84
|
+
publicUrl,
|
|
85
|
+
key,
|
|
86
|
+
method: "PUT",
|
|
87
|
+
headers: { "Content-Type": uploadOptions.mimeType }
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
async delete(url) {
|
|
92
|
+
const [client, { DeleteObjectCommand }] = await Promise.all([
|
|
93
|
+
getClient(),
|
|
94
|
+
getS3Module()
|
|
95
|
+
]);
|
|
96
|
+
const base = publicBaseUrl.replace(/\/$/, "");
|
|
97
|
+
const encodedKey = url.startsWith(base) ? url.slice(base.length + 1) : url.split("/").pop() ?? url;
|
|
98
|
+
const key = decodeURIComponent(encodedKey);
|
|
99
|
+
await client.send(new DeleteObjectCommand({ Bucket: bucket, Key: key }));
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export { s3Adapter };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function vercelBlobAdapter(options = {}) {
|
|
4
|
+
return {
|
|
5
|
+
type: "vercel-blob",
|
|
6
|
+
urlHostnameSuffix: ".public.blob.vercel-storage.com",
|
|
7
|
+
async handleRequest(request, callbacks) {
|
|
8
|
+
let handleUpload;
|
|
9
|
+
try {
|
|
10
|
+
const vercelBlobClient = (
|
|
11
|
+
/* @vite-ignore */
|
|
12
|
+
await import('@vercel/blob/client')
|
|
13
|
+
);
|
|
14
|
+
({ handleUpload } = vercelBlobClient);
|
|
15
|
+
} catch {
|
|
16
|
+
throw new Error(
|
|
17
|
+
"[@btst/stack] Vercel Blob adapter requires '@vercel/blob' with 'handleUpload' exported from '@vercel/blob/client'. Run: npm install @vercel/blob"
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
const body = await request.json();
|
|
21
|
+
return handleUpload({
|
|
22
|
+
body,
|
|
23
|
+
request,
|
|
24
|
+
token: options.token,
|
|
25
|
+
onBeforeGenerateToken: async (pathname, clientPayload) => {
|
|
26
|
+
const tokenOptions = await callbacks.onBeforeGenerateToken?.(
|
|
27
|
+
pathname,
|
|
28
|
+
clientPayload ?? null
|
|
29
|
+
) ?? {};
|
|
30
|
+
return {
|
|
31
|
+
addRandomSuffix: true,
|
|
32
|
+
...tokenOptions
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
onUploadCompleted: async () => {
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
async delete(url) {
|
|
40
|
+
let del;
|
|
41
|
+
try {
|
|
42
|
+
({ del } = await import('@vercel/blob'));
|
|
43
|
+
} catch {
|
|
44
|
+
throw new Error(
|
|
45
|
+
"[@btst/stack] Vercel Blob adapter requires '@vercel/blob'. Run: npm install @vercel/blob"
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
await del(url, options.token ? { token: options.token } : void 0);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
exports.vercelBlobAdapter = vercelBlobAdapter;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { V as VercelBlobStorageAdapter } from '../../../../shared/stack.BttDsJJn.cjs';
|
|
2
|
+
|
|
3
|
+
interface VercelBlobStorageAdapterOptions {
|
|
4
|
+
/**
|
|
5
|
+
* The `BLOB_READ_WRITE_TOKEN` environment variable is read automatically
|
|
6
|
+
* by `@vercel/blob`. You only need to provide this option if you store
|
|
7
|
+
* the token under a different name.
|
|
8
|
+
*/
|
|
9
|
+
token?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Create a Vercel Blob storage adapter using the signed direct-upload protocol.
|
|
13
|
+
* The server never receives file bytes — it only issues short-lived client tokens
|
|
14
|
+
* via `@vercel/blob`'s `handleUpload` helper (available via `@vercel/blob/client`
|
|
15
|
+
* in compatible versions).
|
|
16
|
+
*
|
|
17
|
+
* @remarks Requires `@vercel/blob` as an optional peer dependency (version
|
|
18
|
+
* with `handleUpload` exported from `@vercel/blob/client`).
|
|
19
|
+
*
|
|
20
|
+
* Upload flow:
|
|
21
|
+
* 1. Client calls `POST /media/upload/vercel-blob` to obtain a client token.
|
|
22
|
+
* 2. Client uses `@vercel/blob/client`'s `upload()` to upload directly to Vercel.
|
|
23
|
+
* 3. After upload, client calls `POST /media/assets` to save metadata to the DB.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* mediaBackendPlugin({
|
|
28
|
+
* storageAdapter: vercelBlobAdapter(),
|
|
29
|
+
* hooks: {
|
|
30
|
+
* onBeforeUpload: async (_meta, ctx) => {
|
|
31
|
+
* const session = await getSession(ctx.headers);
|
|
32
|
+
* if (!session) throw new Error("Unauthorized");
|
|
33
|
+
* },
|
|
34
|
+
* },
|
|
35
|
+
* })
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
declare function vercelBlobAdapter(options?: VercelBlobStorageAdapterOptions): VercelBlobStorageAdapter;
|
|
39
|
+
|
|
40
|
+
export { vercelBlobAdapter };
|
|
41
|
+
export type { VercelBlobStorageAdapterOptions };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { V as VercelBlobStorageAdapter } from '../../../../shared/stack.BttDsJJn.mjs';
|
|
2
|
+
|
|
3
|
+
interface VercelBlobStorageAdapterOptions {
|
|
4
|
+
/**
|
|
5
|
+
* The `BLOB_READ_WRITE_TOKEN` environment variable is read automatically
|
|
6
|
+
* by `@vercel/blob`. You only need to provide this option if you store
|
|
7
|
+
* the token under a different name.
|
|
8
|
+
*/
|
|
9
|
+
token?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Create a Vercel Blob storage adapter using the signed direct-upload protocol.
|
|
13
|
+
* The server never receives file bytes — it only issues short-lived client tokens
|
|
14
|
+
* via `@vercel/blob`'s `handleUpload` helper (available via `@vercel/blob/client`
|
|
15
|
+
* in compatible versions).
|
|
16
|
+
*
|
|
17
|
+
* @remarks Requires `@vercel/blob` as an optional peer dependency (version
|
|
18
|
+
* with `handleUpload` exported from `@vercel/blob/client`).
|
|
19
|
+
*
|
|
20
|
+
* Upload flow:
|
|
21
|
+
* 1. Client calls `POST /media/upload/vercel-blob` to obtain a client token.
|
|
22
|
+
* 2. Client uses `@vercel/blob/client`'s `upload()` to upload directly to Vercel.
|
|
23
|
+
* 3. After upload, client calls `POST /media/assets` to save metadata to the DB.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* mediaBackendPlugin({
|
|
28
|
+
* storageAdapter: vercelBlobAdapter(),
|
|
29
|
+
* hooks: {
|
|
30
|
+
* onBeforeUpload: async (_meta, ctx) => {
|
|
31
|
+
* const session = await getSession(ctx.headers);
|
|
32
|
+
* if (!session) throw new Error("Unauthorized");
|
|
33
|
+
* },
|
|
34
|
+
* },
|
|
35
|
+
* })
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
declare function vercelBlobAdapter(options?: VercelBlobStorageAdapterOptions): VercelBlobStorageAdapter;
|
|
39
|
+
|
|
40
|
+
export { vercelBlobAdapter };
|
|
41
|
+
export type { VercelBlobStorageAdapterOptions };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { V as VercelBlobStorageAdapter } from '../../../../shared/stack.BttDsJJn.js';
|
|
2
|
+
|
|
3
|
+
interface VercelBlobStorageAdapterOptions {
|
|
4
|
+
/**
|
|
5
|
+
* The `BLOB_READ_WRITE_TOKEN` environment variable is read automatically
|
|
6
|
+
* by `@vercel/blob`. You only need to provide this option if you store
|
|
7
|
+
* the token under a different name.
|
|
8
|
+
*/
|
|
9
|
+
token?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Create a Vercel Blob storage adapter using the signed direct-upload protocol.
|
|
13
|
+
* The server never receives file bytes — it only issues short-lived client tokens
|
|
14
|
+
* via `@vercel/blob`'s `handleUpload` helper (available via `@vercel/blob/client`
|
|
15
|
+
* in compatible versions).
|
|
16
|
+
*
|
|
17
|
+
* @remarks Requires `@vercel/blob` as an optional peer dependency (version
|
|
18
|
+
* with `handleUpload` exported from `@vercel/blob/client`).
|
|
19
|
+
*
|
|
20
|
+
* Upload flow:
|
|
21
|
+
* 1. Client calls `POST /media/upload/vercel-blob` to obtain a client token.
|
|
22
|
+
* 2. Client uses `@vercel/blob/client`'s `upload()` to upload directly to Vercel.
|
|
23
|
+
* 3. After upload, client calls `POST /media/assets` to save metadata to the DB.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* mediaBackendPlugin({
|
|
28
|
+
* storageAdapter: vercelBlobAdapter(),
|
|
29
|
+
* hooks: {
|
|
30
|
+
* onBeforeUpload: async (_meta, ctx) => {
|
|
31
|
+
* const session = await getSession(ctx.headers);
|
|
32
|
+
* if (!session) throw new Error("Unauthorized");
|
|
33
|
+
* },
|
|
34
|
+
* },
|
|
35
|
+
* })
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
declare function vercelBlobAdapter(options?: VercelBlobStorageAdapterOptions): VercelBlobStorageAdapter;
|
|
39
|
+
|
|
40
|
+
export { vercelBlobAdapter };
|
|
41
|
+
export type { VercelBlobStorageAdapterOptions };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
function vercelBlobAdapter(options = {}) {
|
|
2
|
+
return {
|
|
3
|
+
type: "vercel-blob",
|
|
4
|
+
urlHostnameSuffix: ".public.blob.vercel-storage.com",
|
|
5
|
+
async handleRequest(request, callbacks) {
|
|
6
|
+
let handleUpload;
|
|
7
|
+
try {
|
|
8
|
+
const vercelBlobClient = (
|
|
9
|
+
/* @vite-ignore */
|
|
10
|
+
await import('@vercel/blob/client')
|
|
11
|
+
);
|
|
12
|
+
({ handleUpload } = vercelBlobClient);
|
|
13
|
+
} catch {
|
|
14
|
+
throw new Error(
|
|
15
|
+
"[@btst/stack] Vercel Blob adapter requires '@vercel/blob' with 'handleUpload' exported from '@vercel/blob/client'. Run: npm install @vercel/blob"
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
const body = await request.json();
|
|
19
|
+
return handleUpload({
|
|
20
|
+
body,
|
|
21
|
+
request,
|
|
22
|
+
token: options.token,
|
|
23
|
+
onBeforeGenerateToken: async (pathname, clientPayload) => {
|
|
24
|
+
const tokenOptions = await callbacks.onBeforeGenerateToken?.(
|
|
25
|
+
pathname,
|
|
26
|
+
clientPayload ?? null
|
|
27
|
+
) ?? {};
|
|
28
|
+
return {
|
|
29
|
+
addRandomSuffix: true,
|
|
30
|
+
...tokenOptions
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
onUploadCompleted: async () => {
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
async delete(url) {
|
|
38
|
+
let del;
|
|
39
|
+
try {
|
|
40
|
+
({ del } = await import('@vercel/blob'));
|
|
41
|
+
} catch {
|
|
42
|
+
throw new Error(
|
|
43
|
+
"[@btst/stack] Vercel Blob adapter requires '@vercel/blob'. Run: npm install @vercel/blob"
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
await del(url, options.token ? { token: options.token } : void 0);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { vercelBlobAdapter };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const plugin = require('../../../packages/stack/src/plugins/media/api/plugin.cjs');
|
|
4
|
+
const getters = require('../../../packages/stack/src/plugins/media/api/getters.cjs');
|
|
5
|
+
const mutations = require('../../../packages/stack/src/plugins/media/api/mutations.cjs');
|
|
6
|
+
const serializers = require('../../../packages/stack/src/plugins/media/api/serializers.cjs');
|
|
7
|
+
const queryKeyDefs = require('../../../packages/stack/src/plugins/media/api/query-key-defs.cjs');
|
|
8
|
+
const local = require('../../../packages/stack/src/plugins/media/api/adapters/local.cjs');
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
exports.mediaBackendPlugin = plugin.mediaBackendPlugin;
|
|
13
|
+
exports.getAssetById = getters.getAssetById;
|
|
14
|
+
exports.getFolderById = getters.getFolderById;
|
|
15
|
+
exports.listAssets = getters.listAssets;
|
|
16
|
+
exports.listFolders = getters.listFolders;
|
|
17
|
+
exports.createAsset = mutations.createAsset;
|
|
18
|
+
exports.createFolder = mutations.createFolder;
|
|
19
|
+
exports.deleteAsset = mutations.deleteAsset;
|
|
20
|
+
exports.deleteFolder = mutations.deleteFolder;
|
|
21
|
+
exports.updateAsset = mutations.updateAsset;
|
|
22
|
+
exports.serializeAsset = serializers.serializeAsset;
|
|
23
|
+
exports.serializeFolder = serializers.serializeFolder;
|
|
24
|
+
exports.MEDIA_QUERY_KEYS = queryKeyDefs.MEDIA_QUERY_KEYS;
|
|
25
|
+
exports.assetListDiscriminator = queryKeyDefs.assetListDiscriminator;
|
|
26
|
+
exports.localAdapter = local.localAdapter;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
export { a as MEDIA_QUERY_KEYS, c as MediaApiContext, M as MediaApiRouter, e as MediaBackendConfig, d as MediaBackendHooks, b as assetListDiscriminator, m as mediaBackendPlugin } from '../../../shared/stack.CI8iRKKi.cjs';
|
|
2
|
+
export { A as AssetListParams, c as AssetListResult, F as FolderListParams, g as getAssetById, b as getFolderById, l as listAssets, a as listFolders } from '../../../shared/stack.CAni8dnD.cjs';
|
|
3
|
+
import { DBAdapter } from '@btst/db';
|
|
4
|
+
import { A as Asset, F as Folder, S as SerializedAsset, a as SerializedFolder } from '../../../shared/stack.CLcnSF_b.cjs';
|
|
5
|
+
import { D as DirectStorageAdapter } from '../../../shared/stack.BttDsJJn.cjs';
|
|
6
|
+
export { S as S3StorageAdapter, b as S3UploadToken, a as StorageAdapter, U as UploadOptions, c as VercelBlobHandlerCallbacks, V as VercelBlobStorageAdapter } from '../../../shared/stack.BttDsJJn.cjs';
|
|
7
|
+
import '@btst/stack/plugins/api';
|
|
8
|
+
import 'better-call';
|
|
9
|
+
import 'zod';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Input for creating a new asset record.
|
|
13
|
+
*/
|
|
14
|
+
interface CreateAssetInput {
|
|
15
|
+
filename: string;
|
|
16
|
+
originalName: string;
|
|
17
|
+
mimeType: string;
|
|
18
|
+
size: number;
|
|
19
|
+
url: string;
|
|
20
|
+
folderId?: string;
|
|
21
|
+
alt?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Input for updating an existing asset record.
|
|
25
|
+
*/
|
|
26
|
+
interface UpdateAssetInput {
|
|
27
|
+
alt?: string;
|
|
28
|
+
folderId?: string | null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Input for creating a new folder.
|
|
32
|
+
*/
|
|
33
|
+
interface CreateFolderInput {
|
|
34
|
+
name: string;
|
|
35
|
+
parentId?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create an asset record in the database.
|
|
39
|
+
* Pure DB function — no authorization hooks, no HTTP context.
|
|
40
|
+
*
|
|
41
|
+
* @remarks **Security:** No authorization hooks (e.g. `onBeforeUpload`) are called.
|
|
42
|
+
* The caller is responsible for any access-control checks before invoking this function.
|
|
43
|
+
*/
|
|
44
|
+
declare function createAsset(adapter: DBAdapter, input: CreateAssetInput): Promise<Asset>;
|
|
45
|
+
/**
|
|
46
|
+
* Update an asset's `alt` text or `folderId`.
|
|
47
|
+
* Pure DB function — no authorization hooks, no HTTP context.
|
|
48
|
+
*
|
|
49
|
+
* @remarks **Security:** No authorization hooks are called.
|
|
50
|
+
*/
|
|
51
|
+
declare function updateAsset(adapter: DBAdapter, id: string, input: UpdateAssetInput): Promise<Asset | null>;
|
|
52
|
+
/**
|
|
53
|
+
* Delete an asset record from the database by its ID.
|
|
54
|
+
* Does NOT delete the underlying file — the caller must do that via the storage adapter.
|
|
55
|
+
* Pure DB function — no authorization hooks, no HTTP context.
|
|
56
|
+
*
|
|
57
|
+
* @remarks **Security:** No authorization hooks are called.
|
|
58
|
+
*/
|
|
59
|
+
declare function deleteAsset(adapter: DBAdapter, id: string): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Create a folder record in the database.
|
|
62
|
+
* Pure DB function — no authorization hooks, no HTTP context.
|
|
63
|
+
*
|
|
64
|
+
* @remarks **Security:** No authorization hooks are called.
|
|
65
|
+
*/
|
|
66
|
+
declare function createFolder(adapter: DBAdapter, input: CreateFolderInput): Promise<Folder>;
|
|
67
|
+
/**
|
|
68
|
+
* Delete a folder record from the database by its ID.
|
|
69
|
+
* Child folders are cascade-deleted automatically. Throws if the folder or
|
|
70
|
+
* any of its descendants contain assets (which have associated storage files
|
|
71
|
+
* that must be deleted via the storage adapter first).
|
|
72
|
+
* Pure DB function — no authorization hooks, no HTTP context.
|
|
73
|
+
*
|
|
74
|
+
* @remarks **Security:** No authorization hooks are called.
|
|
75
|
+
*/
|
|
76
|
+
declare function deleteFolder(adapter: DBAdapter, id: string): Promise<void>;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Serialize an Asset for SSR/SSG use (convert dates to strings).
|
|
80
|
+
* Pure function — no DB access, no hooks.
|
|
81
|
+
*/
|
|
82
|
+
declare function serializeAsset(asset: Asset): SerializedAsset;
|
|
83
|
+
/**
|
|
84
|
+
* Serialize a Folder for SSR/SSG use (convert dates to strings).
|
|
85
|
+
* Pure function — no DB access, no hooks.
|
|
86
|
+
*/
|
|
87
|
+
declare function serializeFolder(folder: Folder): SerializedFolder;
|
|
88
|
+
|
|
89
|
+
interface LocalStorageAdapterOptions {
|
|
90
|
+
/**
|
|
91
|
+
* Absolute path to the directory where uploaded files are stored.
|
|
92
|
+
* @default "./public/uploads"
|
|
93
|
+
*/
|
|
94
|
+
uploadDir?: string;
|
|
95
|
+
/**
|
|
96
|
+
* URL prefix used to build the public URL for uploaded files.
|
|
97
|
+
* @default "/uploads"
|
|
98
|
+
*/
|
|
99
|
+
publicPath?: string;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Create a local filesystem storage adapter.
|
|
103
|
+
* Files are written to `uploadDir` and served at `publicPath`.
|
|
104
|
+
* Suitable for development and self-hosted deployments.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* mediaBackendPlugin({
|
|
109
|
+
* storageAdapter: localAdapter({ uploadDir: "./public/uploads", publicPath: "/uploads" })
|
|
110
|
+
* })
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
declare function localAdapter(options?: LocalStorageAdapterOptions): DirectStorageAdapter;
|
|
114
|
+
|
|
115
|
+
export { DirectStorageAdapter, createAsset, createFolder, deleteAsset, deleteFolder, localAdapter, serializeAsset, serializeFolder, updateAsset };
|
|
116
|
+
export type { CreateAssetInput, CreateFolderInput, LocalStorageAdapterOptions, UpdateAssetInput };
|